diff --git a/CHANGELOG.md b/CHANGELOG.md index 9315564afc5..b76ad0edcaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Features + +* [#5072](https://github.com/osmosis-labs/osmosis/pull/5072) IBC-hooks: Add support for async acks when processing onRecvPacket + ### State Breaking * [#5532](https://github.com/osmosis-labs/osmosis/pull/5532) fix: Fix x/tokenfactory genesis import denoms reset x/bank existing denom metadata diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index ba63d28cbac..93a45b47ff3 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -246,8 +246,11 @@ func (appKeepers *AppKeepers) InitNormalKeepers( // Configure the hooks keeper hooksKeeper := ibchookskeeper.NewKeeper( appKeepers.keys[ibchookstypes.StoreKey], + appKeepers.GetSubspace(ibchookstypes.ModuleName), + appKeepers.IBCKeeper.ChannelKeeper, + nil, ) - appKeepers.IBCHooksKeeper = &hooksKeeper + appKeepers.IBCHooksKeeper = hooksKeeper appKeepers.WireICS20PreWasmKeeper(appCodec, bApp, appKeepers.IBCHooksKeeper) @@ -477,6 +480,7 @@ func (appKeepers *AppKeepers) InitNormalKeepers( appKeepers.RateLimitingICS4Wrapper.ContractKeeper = appKeepers.ContractKeeper appKeepers.Ics20WasmHooks.ContractKeeper = appKeepers.ContractKeeper appKeepers.CosmwasmPoolKeeper.SetContractKeeper(appKeepers.ContractKeeper) + appKeepers.IBCHooksKeeper.ContractKeeper = appKeepers.ContractKeeper // set token factory contract keeper appKeepers.TokenFactoryKeeper.SetContractKeeper(appKeepers.ContractKeeper) @@ -671,6 +675,7 @@ func (appKeepers *AppKeepers) initParamsKeeper(appCodec codec.BinaryCodec, legac paramsKeeper.Subspace(icqtypes.ModuleName) paramsKeeper.Subspace(packetforwardtypes.ModuleName).WithKeyTable(packetforwardtypes.ParamKeyTable()) paramsKeeper.Subspace(cosmwasmpooltypes.ModuleName) + paramsKeeper.Subspace(ibchookstypes.ModuleName) return paramsKeeper } diff --git a/app/modules.go b/app/modules.go index c163cc18d41..75ccd2de33f 100644 --- a/app/modules.go +++ b/app/modules.go @@ -181,7 +181,7 @@ func appModules( tokenfactory.NewAppModule(*app.TokenFactoryKeeper, app.AccountKeeper, app.BankKeeper), valsetprefmodule.NewAppModule(appCodec, *app.ValidatorSetPreferenceKeeper), ibcratelimitmodule.NewAppModule(*app.RateLimitingICS4Wrapper), - ibc_hooks.NewAppModule(app.AccountKeeper), + ibc_hooks.NewAppModule(app.AccountKeeper, *app.IBCHooksKeeper), icq.NewAppModule(*app.AppKeepers.ICQKeeper), packetforward.NewAppModule(app.PacketForwardKeeper), cwpoolmodule.NewAppModule(appCodec, *app.CosmwasmPoolKeeper), diff --git a/cosmwasm/contracts/crosschain-registry/src/execute.rs b/cosmwasm/contracts/crosschain-registry/src/execute.rs index 84829b9720e..4d2ff7964ce 100644 --- a/cosmwasm/contracts/crosschain-registry/src/execute.rs +++ b/cosmwasm/contracts/crosschain-registry/src/execute.rs @@ -828,7 +828,7 @@ mod tests { let unauthorized_remove_msg = ExecuteMsg::ModifyContractAlias { operations: vec![ContractAliasInput { operation: Operation::Remove, - alias: alias, + alias, address: Some(address), new_alias: None, }], diff --git a/go.mod b/go.mod index 22f594aa2c4..996b22cc319 100644 --- a/go.mod +++ b/go.mod @@ -22,9 +22,9 @@ require ( github.com/ory/dockertest/v3 v3.10.0 github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3 github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230804142026-a81cfe3ddde7 - github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230804142026-a81cfe3ddde7 + github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230807183608-16c217dedde5 github.com/osmosis-labs/osmosis/x/epochs v0.0.0-20230328024000-175ec88e4304 - github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.6 + github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.0-20230807183608-16c217dedde5 github.com/pkg/errors v0.9.1 github.com/rakyll/statik v0.1.7 github.com/spf13/cast v1.5.1 @@ -50,7 +50,6 @@ require ( github.com/Djarvur/go-err113 v0.1.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect github.com/benbjohnson/clock v1.3.0 // indirect - github.com/bytedance/sonic v1.9.1 // indirect github.com/cosmos/gogoproto v1.4.6 // indirect github.com/cosmos/iavl v0.19.5 // indirect github.com/creachadair/taskgroup v0.3.2 // indirect @@ -58,14 +57,12 @@ require ( github.com/docker/distribution v2.8.2+incompatible // indirect github.com/felixge/httpsnoop v1.0.2 // indirect github.com/go-playground/validator/v10 v10.14.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/gateway v1.1.0 // indirect github.com/google/btree v1.1.2 // indirect github.com/junk1tm/musttag v0.5.0 // indirect github.com/kkHAIKE/contextcheck v1.1.4 // indirect github.com/maratori/testableexamples v1.0.0 // indirect github.com/nunnatsa/ginkgolinter v0.9.0 // indirect - github.com/nxadm/tail v1.4.8 // indirect github.com/regen-network/cosmos-proto v0.3.1 // indirect github.com/sashamelentyev/interfacebloat v1.1.0 // indirect github.com/sashamelentyev/usestdlibvars v1.23.0 // indirect @@ -80,7 +77,6 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/goleak v1.1.12 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/arch v0.3.0 // indirect google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 // indirect ) diff --git a/go.sum b/go.sum index a57c409861e..f63f8c4e5b4 100644 --- a/go.sum +++ b/go.sum @@ -189,9 +189,6 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f github.com/butuzov/ireturn v0.1.1 h1:QvrO2QF2+/Cx1WA/vETCIYBKtRjc30vesdoPUNo1EbY= github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= @@ -212,9 +209,6 @@ github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoG github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8 h1:W9o46d2kbNL06lq7UNDPV0zYLzkrde/bjIqO02eoll0= github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8/go.mod h1:gakxgyXaaPkxvLw1XQxNGK4I37ys9iBRzNUx/B7pUCo= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= -github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -383,7 +377,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= @@ -450,7 +444,6 @@ github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -750,10 +743,7 @@ github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5 h1:2U0HzY8BJ8hVwDKIzp7y4voR9CX/nvcfymLmg2UiOio= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -907,7 +897,6 @@ github.com/nunnatsa/ginkgolinter v0.9.0 h1:Sm0zX5QfjJzkeCjEp+t6d3Ha0jwvoDjleP9XC github.com/nunnatsa/ginkgolinter v0.9.0/go.mod h1:FHaMLURXP7qImeH6bvxWJUpyH+2tuqe5j4rW1gxJRmI= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -953,12 +942,18 @@ github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3 h1:Ylmch github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3/go.mod h1:lV6KnqXYD/ayTe7310MHtM3I2q8Z6bBfMAi+bhwPYtI= github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230804142026-a81cfe3ddde7 h1:NTR4zfrPMP4pJ5T60zyZumBAnTWmTAQX/JSZLGrM9jI= github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230804142026-a81cfe3ddde7/go.mod h1:UlftwozB+QObT3o0YfkuuyL9fsVdgoWt0dm6J7MLYnU= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230804142026-a81cfe3ddde7 h1:uwP/LzPE/edqNKf/Tpn8y9L6EO3JHOVwb+zq1gjL1hE= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230804142026-a81cfe3ddde7/go.mod h1:Pl8Nzx6O6ow/+aqfMoMSz4hX+zz6RrnDYsooptECGxM= +github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230505120443-447d8f14811f h1:eXazuLCKEjXTlciPaSJqtNdSVE+CCiyhgC32p1b30qs= +github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230505120443-447d8f14811f/go.mod h1:QeqmptMwAREnUtUTL6KbPRY+zjejIZrj3rRz4RScfyM= +github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230510161551-8bf252f26bae h1:I1Cy+GpTPWbVi0lBw9+bS1w42YfQjvXNK9bW4YbHhcs= +github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230510161551-8bf252f26bae/go.mod h1:hk/o9/kmTSZmZqwXcSrPuwj/gpRMCqbE/d3vj6teL2A= +github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230807183608-16c217dedde5 h1:j4ifxomFROGROHalqWwX7VPDgxOPotMB1GiAWdb03i4= +github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230807183608-16c217dedde5/go.mod h1:Pl8Nzx6O6ow/+aqfMoMSz4hX+zz6RrnDYsooptECGxM= github.com/osmosis-labs/osmosis/x/epochs v0.0.0-20230328024000-175ec88e4304 h1:RIrWLzIiZN5Xd2JOfSOtGZaf6V3qEQYg6EaDTAkMnCo= github.com/osmosis-labs/osmosis/x/epochs v0.0.0-20230328024000-175ec88e4304/go.mod h1:yPWoJTj5RKrXKUChAicp+G/4Ni/uVEpp27mi/FF/L9c= -github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.6 h1:PjfLL5rwwm44CeLnNQssrFgmj4BdeIS5DriKYhGz7IM= -github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.6/go.mod h1:2Aqs0L6JnMfo+P+It8q7hJsP1YB+Is5DJc4nRSiBF/U= +github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.0-20230505120443-447d8f14811f h1:GHbnqUSwOiNanFy+4TtOxT1MG+SeYXRn8ilVTlXOUxg= +github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.0-20230505120443-447d8f14811f/go.mod h1:bm9OrnwxpsZjScIPvMhhzMU97Fh4JktGH+PtircEVbw= +github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.0-20230807183608-16c217dedde5 h1:clEegVniz0zTTBXKfg2oymKa63IYUxcrVXM+LtsvCpA= +github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.0-20230807183608-16c217dedde5/go.mod h1:sR0lpev9mcm9/9RY50T1og3UC3WpZAsINh/OmgrmFlg= github.com/osmosis-labs/wasmd v0.31.0-osmo-v16 h1:X747cZYdnqc/+RV48iPVeGprpVb/fUWSaKGsZUWrdbg= github.com/osmosis-labs/wasmd v0.31.0-osmo-v16/go.mod h1:Rf8zW/GgBQyFRRB4s62VQHWA6sTlMFSjoDQQpoq64iI= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= @@ -1222,8 +1217,6 @@ github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+ github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= @@ -1313,9 +1306,6 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= diff --git a/osmoutils/ibc.go b/osmoutils/ibc.go index cc2f30b5d64..310059d7c56 100644 --- a/osmoutils/ibc.go +++ b/osmoutils/ibc.go @@ -14,6 +14,22 @@ const IbcAcknowledgementErrorType = "ibc-acknowledgement-error" // NewEmitErrorAcknowledgement creates a new error acknowledgement after having emitted an event with the // details of the error. func NewEmitErrorAcknowledgement(ctx sdk.Context, err error, errorContexts ...string) channeltypes.Acknowledgement { + EmitIBCErrorEvents(ctx, err, errorContexts) + + return channeltypes.NewErrorAcknowledgement(err) +} + +// NewSuccessAckRepresentingAnError creates a new success acknowledgement that represents an error. +// This is useful for notifying the sender that an error has occurred in a way that does not allow +// the received tokens to be reverted (which means they shouldn't be released by the sender's ics20 escrow) +func NewSuccessAckRepresentingAnError(ctx sdk.Context, err error, errorContent []byte, errorContexts ...string) channeltypes.Acknowledgement { + EmitIBCErrorEvents(ctx, err, errorContexts) + + return channeltypes.NewResultAcknowledgement(errorContent) +} + +// EmitIBCErrorEvents Emit and Log errors +func EmitIBCErrorEvents(ctx sdk.Context, err error, errorContexts []string) { logger := ctx.Logger().With("module", IbcAcknowledgementErrorType) attributes := make([]sdk.Attribute, len(errorContexts)+1) @@ -29,8 +45,6 @@ func NewEmitErrorAcknowledgement(ctx sdk.Context, err error, errorContexts ...st attributes..., ), }) - - return channeltypes.NewErrorAcknowledgement(err) } // MustExtractDenomFromPacketOnRecv takes a packet with a valid ICS20 token data in the Data field and returns the diff --git a/proto/osmosis/ibc-hooks/genesis.proto b/proto/osmosis/ibc-hooks/genesis.proto new file mode 100644 index 00000000000..435af285cac --- /dev/null +++ b/proto/osmosis/ibc-hooks/genesis.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; +package osmosis.ibchooks; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "osmosis/ibc-hooks/params.proto"; + +option go_package = "github.com/osmosis-labs/osmosis/v17/x/ibc-hooks/types"; + +message GenesisState { Params params = 1 [ (gogoproto.nullable) = false ]; } diff --git a/proto/osmosis/ibc-hooks/params.proto b/proto/osmosis/ibc-hooks/params.proto new file mode 100644 index 00000000000..6ca3974c304 --- /dev/null +++ b/proto/osmosis/ibc-hooks/params.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; +package osmosis.ibchooks; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "google/protobuf/duration.proto"; + +option go_package = "github.com/osmosis-labs/osmosis/v17/x/ibc-hooks/types"; + +message Params { + repeated string allowed_async_ack_contracts = 1 + [ (gogoproto.moretags) = "yaml:\"allowed_async_ack_contracts\"" ]; +} diff --git a/proto/osmosis/ibc-hooks/tx.proto b/proto/osmosis/ibc-hooks/tx.proto new file mode 100644 index 00000000000..0ec7c398587 --- /dev/null +++ b/proto/osmosis/ibc-hooks/tx.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; +package osmosis.ibchooks; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/osmosis-labs/osmosis/v17/x/ibc-hooks/types"; + +// Msg defines the Msg service. +service Msg { + // EmitIBCAck checks the sender can emit the ack and writes the IBC + // acknowledgement + rpc EmitIBCAck(MsgEmitIBCAck) returns (MsgEmitIBCAckResponse); +} + +message MsgEmitIBCAck { + string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ]; + uint64 packet_sequence = 2 + [ (gogoproto.moretags) = "yaml:\"packet_sequence\"" ]; + string channel = 3 [ (gogoproto.moretags) = "yaml:\"channel\"" ]; +} +message MsgEmitIBCAckResponse { + string contract_result = 1 + [ (gogoproto.moretags) = "yaml:\"contract_result\"" ]; + string ibc_ack = 2 [ (gogoproto.moretags) = "yaml:\"ibc_ack\"" ]; +} diff --git a/tests/cl-genesis-positions/convert.go b/tests/cl-genesis-positions/convert.go index 96e1fc047ea..ecf0b8311e2 100644 --- a/tests/cl-genesis-positions/convert.go +++ b/tests/cl-genesis-positions/convert.go @@ -13,13 +13,13 @@ import ( "github.com/osmosis-labs/osmosis/osmomath" "github.com/osmosis-labs/osmosis/osmoutils/accum" - "github.com/osmosis-labs/osmosis/v16/app" - "github.com/osmosis-labs/osmosis/v16/app/apptesting" - cl "github.com/osmosis-labs/osmosis/v16/x/concentrated-liquidity" - "github.com/osmosis-labs/osmosis/v16/x/concentrated-liquidity/math" - "github.com/osmosis-labs/osmosis/v16/x/concentrated-liquidity/model" - cltypes "github.com/osmosis-labs/osmosis/v16/x/concentrated-liquidity/types" - clgenesis "github.com/osmosis-labs/osmosis/v16/x/concentrated-liquidity/types/genesis" + "github.com/osmosis-labs/osmosis/v17/app" + "github.com/osmosis-labs/osmosis/v17/app/apptesting" + cl "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity" + "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/math" + "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/model" + cltypes "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types" + clgenesis "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types/genesis" ) type BigBangPositions struct { diff --git a/tests/cl-genesis-positions/edit_localosmosis_genesis.go b/tests/cl-genesis-positions/edit_localosmosis_genesis.go index 42d6d732121..1705afb1546 100644 --- a/tests/cl-genesis-positions/edit_localosmosis_genesis.go +++ b/tests/cl-genesis-positions/edit_localosmosis_genesis.go @@ -16,12 +16,12 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - osmosisApp "github.com/osmosis-labs/osmosis/v16/app" - "github.com/osmosis-labs/osmosis/v16/x/concentrated-liquidity/model" + osmosisApp "github.com/osmosis-labs/osmosis/v17/app" + "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/model" - cltypes "github.com/osmosis-labs/osmosis/v16/x/concentrated-liquidity/types" - clgenesis "github.com/osmosis-labs/osmosis/v16/x/concentrated-liquidity/types/genesis" - poolmanagertypes "github.com/osmosis-labs/osmosis/v16/x/poolmanager/types" + cltypes "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types" + clgenesis "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types/genesis" + poolmanagertypes "github.com/osmosis-labs/osmosis/v17/x/poolmanager/types" ) func EditLocalOsmosisGenesis(updatedCLGenesis *clgenesis.GenesisState, updatedBankGenesis *banktypes.GenesisState) { diff --git a/tests/cl-go-client/main.go b/tests/cl-go-client/main.go index 29b32552132..5e2e309c914 100644 --- a/tests/cl-go-client/main.go +++ b/tests/cl-go-client/main.go @@ -18,13 +18,13 @@ import ( "github.com/ignite/cli/ignite/pkg/cosmosclient" "github.com/osmosis-labs/osmosis/osmoutils" - clqueryproto "github.com/osmosis-labs/osmosis/v16/x/concentrated-liquidity/client/queryproto" - "github.com/osmosis-labs/osmosis/v16/x/concentrated-liquidity/model" - cltypes "github.com/osmosis-labs/osmosis/v16/x/concentrated-liquidity/types" - incentivestypes "github.com/osmosis-labs/osmosis/v16/x/incentives/types" - lockuptypes "github.com/osmosis-labs/osmosis/v16/x/lockup/types" - poolmanagerqueryproto "github.com/osmosis-labs/osmosis/v16/x/poolmanager/client/queryproto" - poolmanagertypes "github.com/osmosis-labs/osmosis/v16/x/poolmanager/types" + clqueryproto "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/client/queryproto" + "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/model" + cltypes "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types" + incentivestypes "github.com/osmosis-labs/osmosis/v17/x/incentives/types" + lockuptypes "github.com/osmosis-labs/osmosis/v17/x/lockup/types" + poolmanagerqueryproto "github.com/osmosis-labs/osmosis/v17/x/poolmanager/client/queryproto" + poolmanagertypes "github.com/osmosis-labs/osmosis/v17/x/poolmanager/types" epochstypes "github.com/osmosis-labs/osmosis/x/epochs/types" ) diff --git a/tests/ibc-hooks/async_acks_test.go b/tests/ibc-hooks/async_acks_test.go new file mode 100644 index 00000000000..935ae6f0c1a --- /dev/null +++ b/tests/ibc-hooks/async_acks_test.go @@ -0,0 +1,122 @@ +package ibc_hooks_test + +import ( + "encoding/base64" + "encoding/json" + "fmt" + + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + sdk "github.com/cosmos/cosmos-sdk/types" + channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v4/testing" + "github.com/tidwall/gjson" + + "github.com/osmosis-labs/osmosis/osmoutils" + "github.com/osmosis-labs/osmosis/v17/app" + "github.com/osmosis-labs/osmosis/x/ibc-hooks/types" +) + +func (suite *HooksTestSuite) forceContractToEmitAckForPacket(osmosisApp *app.OsmosisApp, ctx sdk.Context, contractAddr sdk.AccAddress, packet channeltypes.Packet, success bool) ([]byte, error) { + packetJson, err := json.Marshal(packet) + suite.Require().NoError(err) + + msg := fmt.Sprintf(`{"force_emit_ibc_ack": {"packet": %s, "channel": "channel-0", "success": %v }}`, packetJson, success) + contractKeeper := wasmkeeper.NewDefaultPermissionKeeper(osmosisApp.WasmKeeper) + return contractKeeper.Execute(ctx, contractAddr, suite.chainA.SenderAccount.GetAddress(), []byte(msg), sdk.NewCoins()) + +} + +func (suite *HooksTestSuite) TestWasmHooksAsyncAcks() { + sender := suite.chainB.SenderAccount.GetAddress() + osmosisApp := suite.chainA.GetOsmosisApp() + + // Instantiate a contract that knows how to send async Acks + suite.chainA.StoreContractCode(&suite.Suite, "./bytecode/echo.wasm") + contractAddr := suite.chainA.InstantiateContract(&suite.Suite, "{}", 1) + + // Calls that don't specify async acks work as expected + memo := fmt.Sprintf(`{"wasm": {"contract": "%s", "msg": {"async": {"use_async": false}}}}`, contractAddr) + suite.fundAccount(suite.chainB, sender) + transferMsg := NewMsgTransfer(sdk.NewCoin("token0", sdk.NewInt(2000)), sender.String(), contractAddr.String(), "channel-0", memo) + sendResult, receiveResult, ack, err := suite.FullSend(transferMsg, BtoA) + suite.Require().NoError(err) + suite.Require().NotNil(sendResult) + suite.Require().NotNil(receiveResult) + suite.Require().NotNil(ack) + + // the ack has been written + allAcks := osmosisApp.IBCKeeper.ChannelKeeper.GetAllPacketAcks(suite.chainA.GetContext()) + suite.Require().Equal(1, len(allAcks)) + + // Try to emit an ack for a packet that already has been acked. This should fail + + // we extract the packet that has been acked here to test later that our contract can't emit an ack for it + alreadyAckedPacket, err := ibctesting.ParsePacketFromEvents(sendResult.GetEvents()) + suite.Require().NoError(err) + + _, err = suite.forceContractToEmitAckForPacket(osmosisApp, suite.chainA.GetContext(), contractAddr, alreadyAckedPacket, true) + suite.Require().Error(err) + suite.Require().Contains(err.Error(), "no ack actor set for channel channel-0 packet 1") + + params := types.DefaultParams() + params.AllowedAsyncAckContracts = []string{contractAddr.String()} + osmosisApp.IBCHooksKeeper.SetParams(suite.chainA.GetContext(), params) + + totalExpectedAcks := 1 + testCases := []struct { + success bool + }{ + {true}, + {false}, + } + for _, tc := range testCases { + // Calls that specify async Acks work and no Acks are sent + memo = fmt.Sprintf(`{"wasm": {"contract": "%s", "msg": {"async": {"use_async": true}}}}`, contractAddr) + suite.fundAccount(suite.chainB, sender) + transferMsg = NewMsgTransfer(sdk.NewCoin("token0", sdk.NewInt(2000)), sender.String(), contractAddr.String(), "channel-0", memo) + + sendResult, err = suite.chainB.SendMsgsNoCheck(transferMsg) + suite.Require().NoError(err) + + packet, err := ibctesting.ParsePacketFromEvents(sendResult.GetEvents()) + suite.Require().NoError(err) + + receiveResult = suite.RelayPacketNoAck(packet, BtoA) + newAck, err := ibctesting.ParseAckFromEvents(receiveResult.GetEvents()) + suite.Require().Error(err) // No ack! + suite.Require().Nil(newAck) + + // No new ack has been written + allAcks = osmosisApp.IBCKeeper.ChannelKeeper.GetAllPacketAcks(suite.chainA.GetContext()) + suite.Require().Equal(totalExpectedAcks, len(allAcks)) + + // Store a second contract and ask that one to emit an ack for the packet that the first contract sent + contractAddr2 := suite.chainA.InstantiateContract(&suite.Suite, "{}", 1) + _, err = suite.forceContractToEmitAckForPacket(osmosisApp, suite.chainA.GetContext(), contractAddr2, packet, tc.success) + // This should fail because the new contract is not authorized to emit acks for that packet + suite.Require().Error(err) + suite.Require().Contains(err.Error(), "is not allowed to send an ack for channel channel-0 packet") + + // only the contract that sent the packet can send an ack for that packet sequence + ctx := suite.chainA.GetContext() + _, err = suite.forceContractToEmitAckForPacket(osmosisApp, ctx, contractAddr, packet, tc.success) + totalExpectedAcks++ + suite.Require().NoError(err) + writtenAck, err := ibctesting.ParseAckFromEvents(ctx.EventManager().Events()) + suite.Require().NoError(err) + + allAcks = osmosisApp.IBCKeeper.ChannelKeeper.GetAllPacketAcks(suite.chainA.GetContext()) + suite.Require().Equal(totalExpectedAcks, len(allAcks)) + suite.Require().False(osmoutils.IsAckError(writtenAck)) + ackBase64 := gjson.ParseBytes(writtenAck).Get("result").String() + // decode base64 + ackBytes, err := base64.StdEncoding.DecodeString(ackBase64) + suite.Require().NoError(err) + if tc.success { + suite.Require().Equal("YWNr", gjson.ParseBytes(ackBytes).Get("ibc_ack").String()) + } else { + suite.Require().Equal("forced error", gjson.ParseBytes(ackBytes).Get("error").String()) + } + + } +} diff --git a/tests/ibc-hooks/bytecode/counter.wasm b/tests/ibc-hooks/bytecode/counter.wasm index 938171ef848..eb667380ce2 100644 Binary files a/tests/ibc-hooks/bytecode/counter.wasm and b/tests/ibc-hooks/bytecode/counter.wasm differ diff --git a/tests/ibc-hooks/bytecode/echo.wasm b/tests/ibc-hooks/bytecode/echo.wasm index 4feb4554e7c..93e7814b909 100644 Binary files a/tests/ibc-hooks/bytecode/echo.wasm and b/tests/ibc-hooks/bytecode/echo.wasm differ diff --git a/tests/ibc-hooks/testutils/Cargo.lock b/tests/ibc-hooks/testutils/Cargo.lock index 52e7e6ca1ef..96450055624 100644 --- a/tests/ibc-hooks/testutils/Cargo.lock +++ b/tests/ibc-hooks/testutils/Cargo.lock @@ -19,6 +19,12 @@ version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "base16ct" version = "0.1.1" @@ -31,6 +37,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + [[package]] name = "base64ct" version = "1.5.3" @@ -73,6 +85,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "const-oid" version = "0.9.1" @@ -81,9 +103,9 @@ checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" [[package]] name = "cosmwasm-crypto" -version = "1.1.5" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28376836c7677e1ea6d6656a754582e88b91e544ce22fae42956d5fe5549a958" +checksum = "7fecd74d3a0041114110d1260f77fcb644c5d2403549b37096c44f0e643a5177" dependencies = [ "digest 0.10.5", "ed25519-zebra", @@ -94,9 +116,9 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.1.5" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb69f4f7a8a4bce68c8fbd3646238fede1e77056e4ea31c5b6bfc37b709eec3" +checksum = "1c9f7f0e51bfc7295f7b2664fe8513c966428642aa765dad8a74acdab5e0c773" dependencies = [ "syn", ] @@ -127,11 +149,11 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.1.5" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bf9157d060abbc55152aeadcace799d03dc630575daa66604079a1206cb060" +checksum = "5034c772c1369b160731aa00bb81f93733ab2884928edd8d588733d607ac5af4" dependencies = [ - "base64", + "base64 0.13.1", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -140,6 +162,7 @@ dependencies = [ "schemars", "serde", "serde-json-wasm", + "sha2 0.10.6", "thiserror", "uint", ] @@ -162,7 +185,7 @@ dependencies = [ "cosmwasm-std", "cosmwasm-storage", "cw-multi-test", - "cw-storage-plus", + "cw-storage-plus 0.16.0", "cw2", "schemars", "serde", @@ -227,11 +250,11 @@ checksum = "7192aec80d0c01a0e5941392eea7e2b7e212ee74ca7f430bfdc899420c055ef6" dependencies = [ "anyhow", "cosmwasm-std", - "cw-storage-plus", + "cw-storage-plus 0.16.0", "cw-utils", "derivative", "itertools", - "prost", + "prost 0.9.0", "schemars", "serde", "thiserror", @@ -248,6 +271,17 @@ dependencies = [ "serde", ] +[[package]] +name = "cw-storage-plus" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a5083c258acd68386734f428a5a171b29f7d733151ae83090c6fcc9417ffa" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", +] + [[package]] name = "cw-utils" version = "0.16.0" @@ -271,7 +305,7 @@ checksum = "91398113b806f4d2a8d5f8d05684704a20ffd5968bf87e3473e1973710b884ad" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus", + "cw-storage-plus 0.16.0", "schemars", "serde", ] @@ -339,11 +373,15 @@ dependencies = [ name = "echo" version = "0.1.0" dependencies = [ + "base64 0.21.0", "cosmwasm-schema", "cosmwasm-std", "cosmwasm-storage", "cw-multi-test", - "cw-storage-plus", + "cw-storage-plus 1.0.1", + "osmosis-std", + "osmosis-std-derive", + "prost 0.11.9", "schemars", "serde", "thiserror", @@ -495,6 +533,25 @@ version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.16.0" @@ -507,6 +564,34 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "osmosis-std" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a10a056b61279b3529aeaa623b0a4b37b39b4b7061e833ff963fb4bc614d5e13" +dependencies = [ + "chrono", + "cosmwasm-std", + "osmosis-std-derive", + "prost 0.11.9", + "prost-types", + "schemars", + "serde", + "serde-cw-value", +] + +[[package]] +name = "osmosis-std-derive" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd08ecb36b79fd53fb6c94b3444566deb185ad6b47bbeeb6cfff6b54e3d37d4" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pkcs8" version = "0.9.0" @@ -533,7 +618,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.9.0", +] + +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive 0.11.9", ] [[package]] @@ -549,6 +644,28 @@ dependencies = [ "syn", ] +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +dependencies = [ + "prost 0.11.9", +] + [[package]] name = "quote" version = "1.0.21" @@ -643,11 +760,20 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-cw-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75d32da6b8ed758b7d850b6c3c08f1d7df51a4df3cb201296e63e34a78e99d4" +dependencies = [ + "serde", +] + [[package]] name = "serde-json-wasm" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" +checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" dependencies = [ "serde", ] diff --git a/tests/ibc-hooks/testutils/contracts/counter/src/contract.rs b/tests/ibc-hooks/testutils/contracts/counter/src/contract.rs index 259d3634924..e54e1506298 100644 --- a/tests/ibc-hooks/testutils/contracts/counter/src/contract.rs +++ b/tests/ibc-hooks/testutils/contracts/counter/src/contract.rs @@ -366,12 +366,12 @@ mod tests { // No acks query(deps.as_ref(), env.clone(), get_msg.clone()).unwrap_err(); - let msg = SudoMsg::ReceiveAck { + let msg = SudoMsg::IBCLifecycleComplete(IBCLifecycleComplete::IBCAck { 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 @@ -379,12 +379,12 @@ mod tests { let value: GetCountResponse = from_binary(&res).unwrap(); assert_eq!(1, value.count); - let msg = SudoMsg::ReceiveAck { + let msg = SudoMsg::IBCLifecycleComplete(IBCLifecycleComplete::IBCAck { 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 diff --git a/tests/ibc-hooks/testutils/contracts/echo/Cargo.toml b/tests/ibc-hooks/testutils/contracts/echo/Cargo.toml index 6980ce8cb24..62553b0fd40 100644 --- a/tests/ibc-hooks/testutils/contracts/echo/Cargo.toml +++ b/tests/ibc-hooks/testutils/contracts/echo/Cargo.toml @@ -30,10 +30,14 @@ optimize = """docker run --rm -v "$(pwd)":/code \ """ [dependencies] +base64 = "0.21.0" cosmwasm-schema = "1.1.3" -cosmwasm-std = "1.1.3" +cosmwasm-std = {version = "1.2", features = ["stargate"]} cosmwasm-storage = "1.1.3" -cw-storage-plus = "0.16.0" +cw-storage-plus = "1.0.1" +osmosis-std = "0.15.2" +osmosis-std-derive = "0.15.2" +prost = {version = "0.11.2", default-features = false, features = ["prost-derive"]} schemars = "0.8.10" serde = { version = "1.0.145", default-features = false, features = ["derive"] } thiserror = { version = "1.0.31" } diff --git a/tests/ibc-hooks/testutils/contracts/echo/src/ibc.rs b/tests/ibc-hooks/testutils/contracts/echo/src/ibc.rs new file mode 100644 index 00000000000..8bda147ed80 --- /dev/null +++ b/tests/ibc-hooks/testutils/contracts/echo/src/ibc.rs @@ -0,0 +1,65 @@ +use cosmwasm_schema::cw_serde; + +#[cw_serde] +pub struct Height { + /// Previously known as "epoch" + #[serde(skip_serializing_if = "Option::is_none")] + revision_number: Option, + + /// The height of a block + #[serde(skip_serializing_if = "Option::is_none")] + revision_height: Option, +} + +// An IBC packet +#[cw_serde] +pub struct Packet { + pub sequence: u64, + pub source_port: String, + pub source_channel: String, + pub destination_port: String, + pub destination_channel: String, + pub data: String, // FungibleTokenData + pub timeout_height: Height, + #[serde(skip_serializing_if = "Option::is_none")] + pub timeout_timestamp: Option, +} + +// The following are part of the wasm_hooks interface +#[cw_serde] +pub enum IBCAsyncOptions { + #[serde(rename = "request_ack")] + RequestAck { + /// The source channel (osmosis side) of the IBC packet + source_channel: String, + /// The sequence number that the packet was sent with + packet_sequence: u64, + }, +} + +#[cw_serde] +pub struct OnRecvPacketAsyncResponse { + pub is_async_ack: bool, +} + +#[cw_serde] +pub struct ContractAck { + pub contract_result: String, + pub ibc_ack: String, +} + + +#[cw_serde] +#[serde(tag = "type", content = "content")] +pub enum IBCAck { + AckResponse{ + packet: Packet, + contract_ack: ContractAck, + }, + AckError { + packet: Packet, + error_description: String, + error_response: String, + } +} + diff --git a/tests/ibc-hooks/testutils/contracts/echo/src/lib.rs b/tests/ibc-hooks/testutils/contracts/echo/src/lib.rs index ab1d16a3aef..b34c18752f9 100644 --- a/tests/ibc-hooks/testutils/contracts/echo/src/lib.rs +++ b/tests/ibc-hooks/testutils/contracts/echo/src/lib.rs @@ -1,7 +1,12 @@ +pub mod ibc; +pub mod state; +use cosmwasm_schema::cw_serde; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{DepsMut, Env, MessageInfo, Response, StdError}; -use cosmwasm_schema::{cw_serde}; +use cosmwasm_std::{to_binary, DepsMut, Env, MessageInfo, Response, StdError}; +use ibc::{ContractAck, IBCAck, IBCAsyncOptions, OnRecvPacketAsyncResponse, Packet}; +use osmosis_std_derive::CosmwasmExt; +use state::INFLIGHT_PACKETS; // Messages #[cw_serde] @@ -9,7 +14,25 @@ pub struct InstantiateMsg {} #[cw_serde] pub enum ExecuteMsg { - Echo { msg: String }, + Echo { + msg: String, + }, + Async { + use_async: bool, + }, + #[serde(rename = "force_emit_ibc_ack")] + ForceEmitIBCAck { + packet: Packet, + channel: String, + success: bool, + }, +} + +/// Message type for `sudo` entry_point +#[cw_serde] +pub enum SudoMsg { + #[serde(rename = "ibc_async")] + IBCAsync(IBCAsyncOptions), } // Instantiate @@ -29,14 +52,82 @@ fn simple_response(msg: String) -> Response { .add_attribute("echo", msg) .set_data(b"this should echo") } + +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.ibchooks.MsgEmitIBCAck")] +pub struct MsgEmitIBCAck { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + #[prost(uint64, tag = "2")] + pub packet_sequence: u64, + #[prost(string, tag = "3")] + pub channel: ::prost::alloc::string::String, +} + #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( - _deps: DepsMut, - _env: Env, + deps: DepsMut, + env: Env, _info: MessageInfo, msg: ExecuteMsg, ) -> Result { match msg { ExecuteMsg::Echo { msg } => Ok(simple_response(msg)), + ExecuteMsg::Async { use_async } => { + if use_async { + let response_data = OnRecvPacketAsyncResponse { is_async_ack: true }; + Ok(Response::new().set_data(to_binary(&response_data)?)) + } else { + Ok(Response::default()) + } + } + ExecuteMsg::ForceEmitIBCAck { packet, channel, success } => { + INFLIGHT_PACKETS.save( + deps.storage, + (&packet.destination_channel, packet.sequence), + &(packet.clone(), success), + )?; + let msg = MsgEmitIBCAck { + sender: env.contract.address.to_string(), + packet_sequence: packet.sequence, + channel, + }; + Ok(Response::new().add_message(msg)) + } + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn sudo(deps: DepsMut, _env: Env, msg: SudoMsg) -> Result { + match msg { + SudoMsg::IBCAsync(IBCAsyncOptions::RequestAck { + source_channel, + packet_sequence, + }) => { + let (packet, success) = INFLIGHT_PACKETS.load(deps.storage, (&source_channel, packet_sequence))?; + let data = match success { + true => to_binary(&IBCAck::AckResponse { + packet, + contract_ack: ContractAck { + contract_result: base64::encode("success"), + ibc_ack: base64::encode("ack"), + }, + })?, + false => to_binary(&IBCAck::AckError { + packet, + error_description: "forced error".to_string(), + error_response: r#"{"error": "forced error"}"#.to_string(), + })? + }; + Ok(Response::new().set_data(data))}, } } diff --git a/tests/ibc-hooks/testutils/contracts/echo/src/state.rs b/tests/ibc-hooks/testutils/contracts/echo/src/state.rs new file mode 100644 index 00000000000..5bdb309a683 --- /dev/null +++ b/tests/ibc-hooks/testutils/contracts/echo/src/state.rs @@ -0,0 +1,6 @@ +use cw_storage_plus::Map; + +use crate::ibc; + +// (channel, sequence) -> packet +pub const INFLIGHT_PACKETS: Map<(&str, u64), (ibc::Packet, bool)> = Map::new("inflight"); diff --git a/x/ibc-hooks/README.md b/x/ibc-hooks/README.md index ed18cfd1459..71d4f45e3c5 100644 --- a/x/ibc-hooks/README.md +++ b/x/ibc-hooks/README.md @@ -182,6 +182,124 @@ pub enum SudoMsg { } ``` +### Async Acks + +IBC supports the ability to send an ack back to the sender of the packet asynchronously. This is useful for +cases where the packet is received, but the ack is not immediately known. For example, if the packet is being +forwarded to another chain, the ack may not be known until the packet is received on the other chain. + +Note this ACK does not imply full revertability. It is possible that unrevertable actions have occurred +even if there is an Ack Error. (This is distinct from the behavior of ICS-20 transfers). If you want to ensure +revertability, your contract should be implemented in a way that actions are not finalized until a success ack +is received. + +#### Use case + +Async acks are useful in cases where the contract needs to wait for a response from another chain before +returning a result to the caller. + +For example, if you want to send tokens to another chain after the contract is executed you need to +add a new ibc packet and wait for its ack. + +In the synchronous acks case, the caller will receive an ack from the contract before the second packet +has been processed. This means that the caller will have to wait (and potentially track) if the second +packet has been processed successfully or not. + +With async acks, you contract can take this responsibility and only send an ack to the caller once the +second packet has been processed + +#### Making contract Acks async + +To support this, we allow contracts to return an `IBCAsync` response from the function being executed when the +packet is received. That response specifies that the ack should be handled asynchronously. + +Concretely the contract should return: + +```rust +#[cw_serde] +pub struct OnRecvPacketAsyncResponse { + pub is_async_ack: bool, +} +``` + +if `is_async_ack` is set to true, `OnRecvPacket` will return `nil` and the ack will not be written. Instead, the +contract wil be stored as the "ack actor" for the packet so that only that contract is allowed to send an ack +for it. + +It is up to the contract developers to decide which conditions will trigger the ack to be sent. + +#### Sending an async ack + +To send the async ack, the contract needs to send the MsgEmitIBCAck message to the chain. This message will +then make a sudo call to the contract requesting the ack and write the ack to state. + +That message can be specified in the contract as: + +```rust +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.ibchooks.MsgEmitIBCAck")] +pub struct MsgEmitIBCAck { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + #[prost(uint64, tag = "2")] + pub packet_sequence: u64, + #[prost(string, tag = "3")] + pub channel: ::prost::alloc::string::String, +} +``` + +The contract is expected to implement the following sudo message handler: + +```rust +#[cw_serde] +pub enum IBCAsyncOptions { + #[serde(rename = "request_ack")] + RequestAck { + /// The source channel (osmosis side) of the IBC packet + source_channel: String, + /// The sequence number that the packet was sent with + packet_sequence: u64, + }, +} + +#[cw_serde] +pub enum SudoMsg { + #[serde(rename = "ibc_async")] + IBCAsync(IBCAsyncOptions), +} +``` + +and that sudo call should return an `IBCAckResponse`: + +```rust +#[cw_serde] +#[serde(tag = "type", content = "content")] +pub enum IBCAck { + AckResponse{ + packet: Packet, + contract_ack: ContractAck, + }, + AckError { + packet: Packet, + error_description: String, + error_response: String, + } +} +``` + +Note: the sudo call is required to potentially allow anyone to send the MsgEmitIBCAck message. For now, however, +this is artificially limited so that the message can only be send by the same contract. This could be expanded in +the future if needed. + # Testing strategy See go tests. \ No newline at end of file diff --git a/x/ibc-hooks/keeper/keeper.go b/x/ibc-hooks/keeper/keeper.go index 9d14337bc4d..b4bc23cf269 100644 --- a/x/ibc-hooks/keeper/keeper.go +++ b/x/ibc-hooks/keeper/keeper.go @@ -1,11 +1,18 @@ package keeper import ( + "encoding/hex" + "encoding/json" "fmt" - + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" "github.com/cosmos/cosmos-sdk/types/address" - + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + "github.com/osmosis-labs/osmosis/osmoutils" + "github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/libs/log" + "strings" "github.com/osmosis-labs/osmosis/x/ibc-hooks/types" @@ -14,16 +21,29 @@ import ( type ( Keeper struct { - storeKey sdk.StoreKey + storeKey sdk.StoreKey + paramSpace paramtypes.Subspace + + channelKeeper types.ChannelKeeper + ContractKeeper *wasmkeeper.PermissionedKeeper } ) // NewKeeper returns a new instance of the x/ibchooks keeper func NewKeeper( storeKey sdk.StoreKey, -) Keeper { - return Keeper{ - storeKey: storeKey, + paramSpace paramtypes.Subspace, + channelKeeper types.ChannelKeeper, + contractKeeper *wasmkeeper.PermissionedKeeper, +) *Keeper { + if !paramSpace.HasKeyTable() { + paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) + } + return &Keeper{ + storeKey: storeKey, + paramSpace: paramSpace, + channelKeeper: channelKeeper, + ContractKeeper: contractKeeper, } } @@ -32,31 +52,221 @@ 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 { +// GetParams returns the total set of the module's parameters. +func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { + k.paramSpace.GetParamSet(ctx, ¶ms) + return params +} + +// SetParams sets the module's parameters with the provided parameters. +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { + k.paramSpace.SetParamSet(ctx, ¶ms) +} + +func (k Keeper) InitGenesis(ctx sdk.Context, genState types.GenesisState) { + k.SetParams(ctx, genState.Params) +} + +func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { + return &types.GenesisState{ + Params: k.GetParams(ctx), + } +} + +func GetPacketCallbackKey(channel string, packetSequence uint64) []byte { return []byte(fmt.Sprintf("%s::%d", channel, packetSequence)) } +func GetPacketAckKey(channel string, packetSequence uint64) []byte { + return []byte(fmt.Sprintf("%s::%d::ack", channel, packetSequence)) +} + +func GeneratePacketAckValue(packet channeltypes.Packet, contract string) ([]byte, error) { + if _, err := sdk.AccAddressFromBech32(contract); err != nil { + return nil, sdkerrors.Wrap(types.ErrInvalidContractAddr, contract) + } + + packetHash, err := hashPacket(packet) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not hash packet") + } + + return []byte(fmt.Sprintf("%s::%s", contract, packetHash)), nil +} + // 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)) + store.Set(GetPacketCallbackKey(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))) + return string(store.Get(GetPacketCallbackKey(channel, packetSequence))) +} + +// IsInAllowList checks the params to see if the contract is in the KeyAsyncAckAllowList param +func (k Keeper) IsInAllowList(ctx sdk.Context, contract string) bool { + var allowList []string + k.paramSpace.GetIfExists(ctx, types.KeyAsyncAckAllowList, &allowList) + for _, addr := range allowList { + if addr == contract { + return true + } + } + return false } // 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)) + store.Delete(GetPacketCallbackKey(channel, packetSequence)) } +// StorePacketAckActor stores which contract is allowed to send an ack for the packet +func (k Keeper) StorePacketAckActor(ctx sdk.Context, packet channeltypes.Packet, contract string) { + store := ctx.KVStore(k.storeKey) + channel := packet.GetSourceChannel() + packetSequence := packet.GetSequence() + + val, err := GeneratePacketAckValue(packet, contract) + if err != nil { + panic(err) + } + store.Set(GetPacketAckKey(channel, packetSequence), val) +} + +// GetPacketAckActor returns the bech32 addr of the contract that is allowed to send an ack for the packet and the packet hash +func (k Keeper) GetPacketAckActor(ctx sdk.Context, channel string, packetSequence uint64) (string, string) { + store := ctx.KVStore(k.storeKey) + rawData := store.Get(GetPacketAckKey(channel, packetSequence)) + if rawData == nil { + return "", "" + } + data := strings.Split(string(rawData), "::") + if len(data) != 2 { + return "", "" + } + // validate that the contract is a valid bech32 addr + if _, err := sdk.AccAddressFromBech32(data[0]); err != nil { + return "", "" + } + // validate that the hash is a valid sha256sum hash + if _, err := hex.DecodeString(data[1]); err != nil { + return "", "" + } + + return data[0], data[1] +} + +// DeletePacketAckActor deletes the ack actor from storage once it has been used +func (k Keeper) DeletePacketAckActor(ctx sdk.Context, channel string, packetSequence uint64) { + store := ctx.KVStore(k.storeKey) + store.Delete(GetPacketAckKey(channel, packetSequence)) +} + +// DeriveIntermediateSender derives the sender address to be used when calling wasm hooks func DeriveIntermediateSender(channel, originalSender, bech32Prefix string) (string, error) { senderStr := fmt.Sprintf("%s/%s", channel, originalSender) senderHash32 := address.Hash(types.SenderPrefix, []byte(senderStr)) sender := sdk.AccAddress(senderHash32[:]) return sdk.Bech32ifyAddressBytes(bech32Prefix, sender) } + +// EmitIBCAck emits an event that the IBC packet has been acknowledged +func (k Keeper) EmitIBCAck(ctx sdk.Context, sender, channel string, packetSequence uint64) ([]byte, error) { + contract, packetHash := k.GetPacketAckActor(ctx, channel, packetSequence) + if contract == "" { + return nil, fmt.Errorf("no ack actor set for channel %s packet %d", channel, packetSequence) + } + // Only the contract itself can request for the ack to be emitted. This will generally happen as a callback + // when the result of other IBC actions has finished, but it could be exposed directly by the contract if the + // proper checks are made + if sender != contract { + return nil, fmt.Errorf("sender %s is not allowed to send an ack for channel %s packet %d", sender, channel, packetSequence) + } + + // Write the acknowledgement + _, cap, err := k.channelKeeper.LookupModuleByChannel(ctx, "transfer", channel) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + } + + // Calling the contract. This could be made generic by using an interface if we want + // to support other types of AckActors, but keeping it here for now for simplicity. + contractAddr, err := sdk.AccAddressFromBech32(contract) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not parse contract address") + } + + msg := types.IBCAsync{ + RequestAck: types.RequestAck{RequestAckI: types.RequestAckI{ + PacketSequence: packetSequence, + SourceChannel: channel, + }}, + } + msgBytes, err := json.Marshal(msg) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not marshal message") + } + bz, err := k.ContractKeeper.Sudo(ctx, contractAddr, msgBytes) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not execute contract") + } + + ack, err := types.UnmarshalIBCAck(bz) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not unmarshal into IBCAckResponse or IBCAckError") + + } + var newAck channeltypes.Acknowledgement + var packet channeltypes.Packet + + switch ack.Type { + case "ack_response": + jsonAck, err := json.Marshal(ack.AckResponse.ContractAck) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not marshal acknowledgement") + } + packet = ack.AckResponse.Packet + newAck = channeltypes.NewResultAcknowledgement(jsonAck) + case "ack_error": + packet = ack.AckError.Packet + newAck = osmoutils.NewSuccessAckRepresentingAnError(ctx, types.ErrAckFromContract, []byte(ack.AckError.ErrorResponse), ack.AckError.ErrorDescription) + default: + return nil, sdkerrors.Wrap(err, "could not unmarshal into IBCAckResponse or IBCAckError") + } + + // Validate that the packet returned by the contract matches the one we stored when sending + receivedPacketHash, err := hashPacket(packet) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not hash packet") + } + if receivedPacketHash != packetHash { + return nil, sdkerrors.Wrap(types.ErrAckPacketMismatch, fmt.Sprintf("packet hash mismatch. Expected %s, got %s", packetHash, receivedPacketHash)) + } + + // Now we can write the acknowledgement + err = k.channelKeeper.WriteAcknowledgement(ctx, cap, packet, newAck) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not write acknowledgement") + } + + response, err := json.Marshal(newAck) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not marshal acknowledgement") + } + return response, nil +} + +func hashPacket(packet channeltypes.Packet) (string, error) { + // ignore the data here. We only care about the channel information + packet.Data = nil + bz, err := json.Marshal(packet) + if err != nil { + return "", sdkerrors.Wrap(err, "could not marshal packet") + } + packetHash := tmhash.Sum(bz) + return hex.EncodeToString(packetHash), nil +} diff --git a/x/ibc-hooks/keeper/msg_server.go b/x/ibc-hooks/keeper/msg_server.go new file mode 100644 index 00000000000..fbb7ed5b133 --- /dev/null +++ b/x/ibc-hooks/keeper/msg_server.go @@ -0,0 +1,40 @@ +package keeper + +import ( + "context" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/osmosis-labs/osmosis/x/ibc-hooks/types" + "strconv" +) + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +var _ types.MsgServer = msgServer{} + +func (m msgServer) EmitIBCAck(goCtx context.Context, msg *types.MsgEmitIBCAck) (*types.MsgEmitIBCAckResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.MsgEmitAckKey, + sdk.NewAttribute(types.AttributeSender, msg.Sender), + sdk.NewAttribute(types.AttributeChannel, msg.Channel), + sdk.NewAttribute(types.AttributePacketSequence, strconv.FormatUint(msg.PacketSequence, 10)), + ), + ) + + ack, err := m.Keeper.EmitIBCAck(ctx, msg.Sender, msg.Channel, msg.PacketSequence) + if err != nil { + return nil, err + } + + return &types.MsgEmitIBCAckResponse{ContractResult: string(ack), IbcAck: string(ack)}, nil +} diff --git a/x/ibc-hooks/sdkmodule.go b/x/ibc-hooks/sdkmodule.go index 160e5945808..1f4cccda6aa 100644 --- a/x/ibc-hooks/sdkmodule.go +++ b/x/ibc-hooks/sdkmodule.go @@ -3,6 +3,7 @@ package ibc_hooks import ( "encoding/json" "fmt" + "github.com/osmosis-labs/osmosis/x/ibc-hooks/keeper" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" @@ -38,21 +39,28 @@ func (AppModuleBasic) Name() string { } // RegisterLegacyAminoCodec registers the ibc-hooks module's types on the given LegacyAmino codec. -func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {} +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterCodec(cdc) +} -// RegisterInterfaces registers the module's interface types. -func (b AppModuleBasic) RegisterInterfaces(_ cdctypes.InterfaceRegistry) {} +// RegisterInterfaces registers the module's interface types +func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) { + types.RegisterInterfaces(reg) +} // DefaultGenesis returns default genesis state as raw bytes for the // module. func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { - emptyString := "{}" - return []byte(emptyString) + return cdc.MustMarshalJSON(types.DefaultGenesis()) } // ValidateGenesis performs genesis state validation for the ibc-hooks module. func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { - return nil + var genState types.GenesisState + if err := cdc.UnmarshalJSON(bz, &genState); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) + } + return genState.Validate() } // RegisterRESTRoutes registers the REST routes for the ibc-hooks module. @@ -76,13 +84,15 @@ type AppModule struct { AppModuleBasic authKeeper osmoutils.AccountKeeper + keeper keeper.Keeper } // NewAppModule creates a new AppModule object. -func NewAppModule(ak osmoutils.AccountKeeper) AppModule { +func NewAppModule(ak osmoutils.AccountKeeper, keeper keeper.Keeper) AppModule { return AppModule{ AppModuleBasic: AppModuleBasic{}, authKeeper: ak, + keeper: keeper, } } @@ -112,16 +122,24 @@ func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sd // RegisterServices registers a gRPC query service to respond to the // module-specific gRPC queries. func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) } // InitGenesis performs genesis initialization for the ibc-hooks module. It returns // no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { + var genState types.GenesisState + // Initialize global index to index in genesis state + cdc.MustUnmarshalJSON(data, &genState) + + am.keeper.InitGenesis(ctx, genState) + return []abci.ValidatorUpdate{} } func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { - return json.RawMessage([]byte("{}")) + genState := am.keeper.ExportGenesis(ctx) + return cdc.MustMarshalJSON(genState) } // BeginBlock returns the begin blocker for the ibc-hooks module. diff --git a/x/ibc-hooks/types/codec.go b/x/ibc-hooks/types/codec.go new file mode 100644 index 00000000000..e4b1dcd78bc --- /dev/null +++ b/x/ibc-hooks/types/codec.go @@ -0,0 +1,38 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" + + // this line is used by starport scaffolding # 1 + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +func RegisterCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgEmitIBCAck{}, "osmosis/ibc-hooks/emit-ibc-ack", nil) +} + +func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgEmitIBCAck{}, + ) + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewProtoCodec(cdctypes.NewInterfaceRegistry()) +) + +func init() { + RegisterCodec(amino) + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + sdk.RegisterLegacyAminoCodec(amino) + RegisterCodec(authzcodec.Amino) + + amino.Seal() +} diff --git a/x/ibc-hooks/types/errors.go b/x/ibc-hooks/types/errors.go index 717e33bddd4..31722619aaf 100644 --- a/x/ibc-hooks/types/errors.go +++ b/x/ibc-hooks/types/errors.go @@ -8,10 +8,14 @@ var ( ErrBadMetadataFormatMsg = "wasm metadata not properly formatted for: '%v'. %s" ErrBadExecutionMsg = "cannot execute contract: %v" - ErrMsgValidation = errorsmod.Register("wasm-hooks", 2, "error in wasmhook message validation") - ErrMarshaling = errorsmod.Register("wasm-hooks", 3, "cannot marshal the ICS20 packet") - ErrInvalidPacket = errorsmod.Register("wasm-hooks", 4, "invalid packet data") - ErrBadResponse = errorsmod.Register("wasm-hooks", 5, "cannot create response") - ErrWasmError = errorsmod.Register("wasm-hooks", 6, "wasm error") - ErrBadSender = errorsmod.Register("wasm-hooks", 7, "bad sender") + ErrMsgValidation = errorsmod.Register("wasm-hooks", 2, "error in wasmhook message validation") + ErrMarshaling = errorsmod.Register("wasm-hooks", 3, "cannot marshal the ICS20 packet") + ErrInvalidPacket = errorsmod.Register("wasm-hooks", 4, "invalid packet data") + ErrBadResponse = errorsmod.Register("wasm-hooks", 5, "cannot create response") + ErrWasmError = errorsmod.Register("wasm-hooks", 6, "wasm error") + ErrBadSender = errorsmod.Register("wasm-hooks", 7, "bad sender") + ErrAckFromContract = errorsmod.Register("wasm-hooks", 8, "contract returned error ack") + ErrAsyncAckNotAllowed = errorsmod.Register("wasm-hooks", 9, "contract not allowed to send async acks") + ErrAckPacketMismatch = errorsmod.Register("wasm-hooks", 10, "packet does not match the expected packet") + ErrInvalidContractAddr = errorsmod.Register("wasm-hooks", 11, "invalid contract address") ) diff --git a/x/ibc-hooks/types/expected_keepers.go b/x/ibc-hooks/types/expected_keepers.go new file mode 100644 index 00000000000..0110dcd9c8e --- /dev/null +++ b/x/ibc-hooks/types/expected_keepers.go @@ -0,0 +1,16 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v4/modules/core/exported" +) + +type ChannelKeeper interface { + GetChannel(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) + GetPacketCommitment(ctx sdk.Context, portID, channelID string, sequence uint64) []byte + GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) + LookupModuleByChannel(ctx sdk.Context, portID, channelID string) (string, *capabilitytypes.Capability, error) + WriteAcknowledgement(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet exported.PacketI, acknowledgement exported.Acknowledgement) error +} diff --git a/x/ibc-hooks/types/genesis.go b/x/ibc-hooks/types/genesis.go new file mode 100644 index 00000000000..dd111be6489 --- /dev/null +++ b/x/ibc-hooks/types/genesis.go @@ -0,0 +1,16 @@ +package types + +// DefaultGenesis returns the default GenesisState for the concentrated-liquidity module. +func DefaultGenesis() *GenesisState { + return &GenesisState{ + Params: DefaultParams(), + } +} + +// Validate performs basic genesis state validation returning an error upon any failure. +func (gs GenesisState) Validate() error { + if err := gs.Params.Validate(); err != nil { + return err + } + return nil +} diff --git a/x/ibc-hooks/types/genesis.pb.go b/x/ibc-hooks/types/genesis.pb.go new file mode 100644 index 00000000000..51b10276c82 --- /dev/null +++ b/x/ibc-hooks/types/genesis.pb.go @@ -0,0 +1,322 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/ibc-hooks/genesis.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type GenesisState struct { + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_a335b3a5deaed556, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "osmosis.ibchooks.GenesisState") +} + +func init() { proto.RegisterFile("osmosis/ibc-hooks/genesis.proto", fileDescriptor_a335b3a5deaed556) } + +var fileDescriptor_a335b3a5deaed556 = []byte{ + // 212 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xcf, 0x2f, 0xce, 0xcd, + 0x2f, 0xce, 0x2c, 0xd6, 0xcf, 0x4c, 0x4a, 0xd6, 0xcd, 0xc8, 0xcf, 0xcf, 0x2e, 0xd6, 0x4f, 0x4f, + 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x80, 0x2a, 0xd0, + 0xcb, 0x4c, 0x4a, 0x06, 0xcb, 0x4b, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x25, 0xf5, 0x41, 0x2c, + 0x88, 0x3a, 0x29, 0xc9, 0x64, 0xb0, 0xc2, 0x78, 0x88, 0x04, 0x84, 0x03, 0x95, 0x92, 0xc3, 0xb4, + 0xa3, 0x20, 0xb1, 0x28, 0x31, 0x17, 0x2a, 0xaf, 0xe4, 0xc6, 0xc5, 0xe3, 0x0e, 0xb1, 0x33, 0xb8, + 0x24, 0xb1, 0x24, 0x55, 0xc8, 0x8c, 0x8b, 0x0d, 0x22, 0x2f, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x6d, + 0x24, 0xa1, 0x87, 0xee, 0x06, 0xbd, 0x00, 0xb0, 0xbc, 0x13, 0xcb, 0x89, 0x7b, 0xf2, 0x0c, 0x41, + 0x50, 0xd5, 0x4e, 0xfe, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, + 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0x65, 0x9a, + 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x0f, 0x35, 0x4b, 0x37, 0x27, 0x31, + 0xa9, 0x18, 0xc6, 0xd1, 0x2f, 0x33, 0x34, 0xd7, 0xaf, 0x40, 0x72, 0x5f, 0x49, 0x65, 0x41, 0x6a, + 0x71, 0x12, 0x1b, 0xd8, 0x7d, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x94, 0x67, 0x2a, 0x86, + 0x25, 0x01, 0x00, 0x00, +} + +func (m *GenesisState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/ibc-hooks/types/keys.go b/x/ibc-hooks/types/keys.go index 9a3e7ded1bc..db7527bd1fb 100644 --- a/x/ibc-hooks/types/keys.go +++ b/x/ibc-hooks/types/keys.go @@ -1,8 +1,17 @@ package types const ( - ModuleName = "ibchooks" - StoreKey = "hooks-for-ibc" // not using the module name because of collisions with key "ibc" + ModuleName = "ibchooks" + RouterKey = ModuleName + StoreKey = "hooks-for-ibc" // not using the module name because of collisions with key "ibc" + IBCCallbackKey = "ibc_callback" - SenderPrefix = "ibc-wasm-hook-intermediary" + IBCAsyncAckKey = "ibc_async_ack" + + MsgEmitAckKey = "emit_ack" + AttributeSender = "sender" + AttributeChannel = "channel" + AttributePacketSequence = "sequence" + + SenderPrefix = "ibc-wasm-hook-intermediary" ) diff --git a/x/ibc-hooks/types/msgs.go b/x/ibc-hooks/types/msgs.go new file mode 100644 index 00000000000..85ccb3b8cb3 --- /dev/null +++ b/x/ibc-hooks/types/msgs.go @@ -0,0 +1,32 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// constants. +const ( + TypeMsgEmitIBCAck = "emit-ibc-ack" +) + +var _ sdk.Msg = &MsgEmitIBCAck{} + +func (m MsgEmitIBCAck) Route() string { return RouterKey } +func (m MsgEmitIBCAck) Type() string { return TypeMsgEmitIBCAck } +func (m MsgEmitIBCAck) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(m.Sender) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err) + } + return nil +} + +func (m MsgEmitIBCAck) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&m)) +} + +func (m MsgEmitIBCAck) GetSigners() []sdk.AccAddress { + sender, _ := sdk.AccAddressFromBech32(m.Sender) + return []sdk.AccAddress{sender} +} diff --git a/x/ibc-hooks/types/params.go b/x/ibc-hooks/types/params.go new file mode 100644 index 00000000000..091fbb14683 --- /dev/null +++ b/x/ibc-hooks/types/params.go @@ -0,0 +1,62 @@ +package types + +import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +) + +// Parameter store keys. +var ( + KeyAsyncAckAllowList = []byte("AsyncAckAllowList") + + _ paramtypes.ParamSet = &Params{} +) + +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) +} + +func NewParams(allowedAsyncAckContracts []string) Params { + return Params{ + AllowedAsyncAckContracts: allowedAsyncAckContracts, + } +} + +// DefaultParams returns default concentrated-liquidity module parameters. +func DefaultParams() Params { + return Params{ + AllowedAsyncAckContracts: []string{}, + } +} + +// ParamSetPairs implements params.ParamSet. +func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { + return paramtypes.ParamSetPairs{ + paramtypes.NewParamSetPair(KeyAsyncAckAllowList, &p.AllowedAsyncAckContracts, validateAsyncAckAllowList), + } +} + +// Validate params. +func (p Params) Validate() error { + if err := validateAsyncAckAllowList(p.AllowedAsyncAckContracts); err != nil { + return err + } + return nil +} + +func validateAsyncAckAllowList(i interface{}) error { + allowedContracts, ok := i.([]string) + + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + for _, contract := range allowedContracts { + if _, err := sdk.AccAddressFromBech32(contract); err != nil { + return err + } + } + + return nil +} diff --git a/x/ibc-hooks/types/params.pb.go b/x/ibc-hooks/types/params.pb.go new file mode 100644 index 00000000000..6ab409300d2 --- /dev/null +++ b/x/ibc-hooks/types/params.pb.go @@ -0,0 +1,327 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/ibc-hooks/params.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + _ "github.com/gogo/protobuf/types" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Params struct { + AllowedAsyncAckContracts []string `protobuf:"bytes,1,rep,name=allowed_async_ack_contracts,json=allowedAsyncAckContracts,proto3" json:"allowed_async_ack_contracts,omitempty" yaml:"allowed_async_ack_contracts"` +} + +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_a8a3c4779e5e4552, []int{0} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetAllowedAsyncAckContracts() []string { + if m != nil { + return m.AllowedAsyncAckContracts + } + return nil +} + +func init() { + proto.RegisterType((*Params)(nil), "osmosis.ibchooks.Params") +} + +func init() { proto.RegisterFile("osmosis/ibc-hooks/params.proto", fileDescriptor_a8a3c4779e5e4552) } + +var fileDescriptor_a8a3c4779e5e4552 = []byte{ + // 254 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xcb, 0x2f, 0xce, 0xcd, + 0x2f, 0xce, 0x2c, 0xd6, 0xcf, 0x4c, 0x4a, 0xd6, 0xcd, 0xc8, 0xcf, 0xcf, 0x2e, 0xd6, 0x2f, 0x48, + 0x2c, 0x4a, 0xcc, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x80, 0xca, 0xeb, 0x65, + 0x26, 0x25, 0x83, 0xa5, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x92, 0xfa, 0x20, 0x16, 0x44, + 0x9d, 0x94, 0x64, 0x32, 0x58, 0x61, 0x3c, 0x44, 0x02, 0xc2, 0x81, 0x4a, 0xc9, 0xa5, 0xe7, 0xe7, + 0xa7, 0xe7, 0xa4, 0xea, 0x83, 0x79, 0x49, 0xa5, 0x69, 0xfa, 0x29, 0xa5, 0x45, 0x89, 0x25, 0x99, + 0xf9, 0x79, 0x10, 0x79, 0xa5, 0x7c, 0x2e, 0xb6, 0x00, 0xb0, 0x95, 0x42, 0xa9, 0x5c, 0xd2, 0x89, + 0x39, 0x39, 0xf9, 0xe5, 0xa9, 0x29, 0xf1, 0x89, 0xc5, 0x95, 0x79, 0xc9, 0xf1, 0x89, 0xc9, 0xd9, + 0xf1, 0xc9, 0xf9, 0x79, 0x25, 0x45, 0x89, 0xc9, 0x25, 0xc5, 0x12, 0x8c, 0x0a, 0xcc, 0x1a, 0x9c, + 0x4e, 0x6a, 0x9f, 0xee, 0xc9, 0x2b, 0x55, 0x26, 0xe6, 0xe6, 0x58, 0x29, 0xe1, 0x51, 0xac, 0x14, + 0x24, 0x01, 0x95, 0x75, 0x04, 0x49, 0x3a, 0x26, 0x67, 0x3b, 0xc3, 0xa4, 0x9c, 0xfc, 0x4f, 0x3c, + 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, + 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0xca, 0x34, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, + 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0xea, 0x71, 0xdd, 0x9c, 0xc4, 0xa4, 0x62, 0x18, 0x47, 0xbf, 0xcc, + 0xd0, 0x5c, 0xbf, 0x02, 0x29, 0xac, 0x4a, 0x2a, 0x0b, 0x52, 0x8b, 0x93, 0xd8, 0xc0, 0x1e, 0x31, + 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x54, 0xb5, 0xc2, 0xdb, 0x4d, 0x01, 0x00, 0x00, +} + +func (m *Params) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AllowedAsyncAckContracts) > 0 { + for iNdEx := len(m.AllowedAsyncAckContracts) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.AllowedAsyncAckContracts[iNdEx]) + copy(dAtA[i:], m.AllowedAsyncAckContracts[iNdEx]) + i = encodeVarintParams(dAtA, i, uint64(len(m.AllowedAsyncAckContracts[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintParams(dAtA []byte, offset int, v uint64) int { + offset -= sovParams(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.AllowedAsyncAckContracts) > 0 { + for _, s := range m.AllowedAsyncAckContracts { + l = len(s) + n += 1 + l + sovParams(uint64(l)) + } + } + return n +} + +func sovParams(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozParams(x uint64) (n int) { + return sovParams(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Params) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowedAsyncAckContracts", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AllowedAsyncAckContracts = append(m.AllowedAsyncAckContracts, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipParams(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthParams + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipParams(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowParams + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowParams + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowParams + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthParams + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupParams + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthParams + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthParams = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowParams = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupParams = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/ibc-hooks/types/tx.pb.go b/x/ibc-hooks/types/tx.pb.go new file mode 100644 index 00000000000..ba689b967dc --- /dev/null +++ b/x/ibc-hooks/types/tx.pb.go @@ -0,0 +1,724 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: osmosis/ibc-hooks/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type MsgEmitIBCAck struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + PacketSequence uint64 `protobuf:"varint,2,opt,name=packet_sequence,json=packetSequence,proto3" json:"packet_sequence,omitempty" yaml:"packet_sequence"` + Channel string `protobuf:"bytes,3,opt,name=channel,proto3" json:"channel,omitempty" yaml:"channel"` +} + +func (m *MsgEmitIBCAck) Reset() { *m = MsgEmitIBCAck{} } +func (m *MsgEmitIBCAck) String() string { return proto.CompactTextString(m) } +func (*MsgEmitIBCAck) ProtoMessage() {} +func (*MsgEmitIBCAck) Descriptor() ([]byte, []int) { + return fileDescriptor_93268c51ed820a58, []int{0} +} +func (m *MsgEmitIBCAck) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgEmitIBCAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgEmitIBCAck.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgEmitIBCAck) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgEmitIBCAck.Merge(m, src) +} +func (m *MsgEmitIBCAck) XXX_Size() int { + return m.Size() +} +func (m *MsgEmitIBCAck) XXX_DiscardUnknown() { + xxx_messageInfo_MsgEmitIBCAck.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgEmitIBCAck proto.InternalMessageInfo + +func (m *MsgEmitIBCAck) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgEmitIBCAck) GetPacketSequence() uint64 { + if m != nil { + return m.PacketSequence + } + return 0 +} + +func (m *MsgEmitIBCAck) GetChannel() string { + if m != nil { + return m.Channel + } + return "" +} + +type MsgEmitIBCAckResponse struct { + ContractResult string `protobuf:"bytes,1,opt,name=contract_result,json=contractResult,proto3" json:"contract_result,omitempty" yaml:"contract_result"` + IbcAck string `protobuf:"bytes,2,opt,name=ibc_ack,json=ibcAck,proto3" json:"ibc_ack,omitempty" yaml:"ibc_ack"` +} + +func (m *MsgEmitIBCAckResponse) Reset() { *m = MsgEmitIBCAckResponse{} } +func (m *MsgEmitIBCAckResponse) String() string { return proto.CompactTextString(m) } +func (*MsgEmitIBCAckResponse) ProtoMessage() {} +func (*MsgEmitIBCAckResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_93268c51ed820a58, []int{1} +} +func (m *MsgEmitIBCAckResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgEmitIBCAckResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgEmitIBCAckResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgEmitIBCAckResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgEmitIBCAckResponse.Merge(m, src) +} +func (m *MsgEmitIBCAckResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgEmitIBCAckResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgEmitIBCAckResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgEmitIBCAckResponse proto.InternalMessageInfo + +func (m *MsgEmitIBCAckResponse) GetContractResult() string { + if m != nil { + return m.ContractResult + } + return "" +} + +func (m *MsgEmitIBCAckResponse) GetIbcAck() string { + if m != nil { + return m.IbcAck + } + return "" +} + +func init() { + proto.RegisterType((*MsgEmitIBCAck)(nil), "osmosis.ibchooks.MsgEmitIBCAck") + proto.RegisterType((*MsgEmitIBCAckResponse)(nil), "osmosis.ibchooks.MsgEmitIBCAckResponse") +} + +func init() { proto.RegisterFile("osmosis/ibc-hooks/tx.proto", fileDescriptor_93268c51ed820a58) } + +var fileDescriptor_93268c51ed820a58 = []byte{ + // 367 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0xcf, 0x4a, 0x2b, 0x31, + 0x14, 0xc6, 0x9b, 0xdb, 0x4b, 0xcb, 0x0d, 0xb4, 0xf7, 0xde, 0x41, 0xa5, 0xcc, 0x62, 0xa6, 0x64, + 0x63, 0x45, 0x3b, 0x83, 0x8a, 0x08, 0xee, 0x3a, 0xc5, 0x85, 0x8b, 0x22, 0x44, 0x70, 0x21, 0x48, + 0x99, 0xc4, 0x30, 0x0d, 0xf3, 0x27, 0xe3, 0x24, 0x95, 0xf6, 0x11, 0xdc, 0xf9, 0x22, 0xbe, 0x87, + 0xcb, 0x2e, 0x5d, 0x15, 0x69, 0xdf, 0xa0, 0x4f, 0x20, 0x9d, 0xc9, 0x40, 0xdb, 0x8d, 0xbb, 0xe4, + 0xfb, 0x7e, 0x27, 0xf9, 0xce, 0x49, 0xa0, 0x29, 0x64, 0x2c, 0x24, 0x97, 0x2e, 0x27, 0xb4, 0x3b, + 0x12, 0x22, 0x94, 0xae, 0x9a, 0x38, 0x69, 0x26, 0x94, 0x30, 0xfe, 0x69, 0xcf, 0xe1, 0x84, 0xe6, + 0x96, 0xb9, 0x17, 0x88, 0x40, 0xe4, 0xa6, 0xbb, 0x5e, 0x15, 0x1c, 0x7a, 0x07, 0xb0, 0x31, 0x90, + 0xc1, 0x75, 0xcc, 0xd5, 0x8d, 0xd7, 0xef, 0xd1, 0xd0, 0x38, 0x82, 0x35, 0xc9, 0x92, 0x27, 0x96, + 0xb5, 0x40, 0x1b, 0x74, 0xfe, 0x78, 0xff, 0x57, 0x73, 0xbb, 0x31, 0xf5, 0xe3, 0xe8, 0x0a, 0x15, + 0x3a, 0xc2, 0x1a, 0x30, 0xfa, 0xf0, 0x6f, 0xea, 0xd3, 0x90, 0xa9, 0xa1, 0x64, 0xcf, 0x63, 0x96, + 0x50, 0xd6, 0xfa, 0xd5, 0x06, 0x9d, 0xdf, 0x9e, 0xb9, 0x9a, 0xdb, 0x07, 0x45, 0xcd, 0x0e, 0x80, + 0x70, 0xb3, 0x50, 0xee, 0xb4, 0x60, 0x9c, 0xc0, 0x3a, 0x1d, 0xf9, 0x49, 0xc2, 0xa2, 0x56, 0x35, + 0xbf, 0xd0, 0x58, 0xcd, 0xed, 0x66, 0x51, 0xac, 0x0d, 0x84, 0x4b, 0x04, 0xbd, 0x02, 0xb8, 0xbf, + 0x95, 0x17, 0x33, 0x99, 0x8a, 0x44, 0xb2, 0x75, 0x18, 0x2a, 0x12, 0x95, 0xf9, 0x54, 0x0d, 0x33, + 0x26, 0xc7, 0x91, 0xd2, 0x0d, 0x6c, 0x84, 0xd9, 0x01, 0x10, 0x6e, 0x96, 0x0a, 0xce, 0x05, 0xe3, + 0x18, 0xd6, 0x39, 0xa1, 0x43, 0x9f, 0x86, 0x79, 0x27, 0x5b, 0x61, 0xb4, 0x81, 0x70, 0x8d, 0x13, + 0xda, 0xa3, 0xe1, 0xd9, 0x23, 0xac, 0x0e, 0x64, 0x60, 0xdc, 0x43, 0xb8, 0x31, 0x3e, 0xdb, 0xd9, + 0x9d, 0xbc, 0xb3, 0x95, 0xd7, 0x3c, 0xfc, 0x01, 0x28, 0x1b, 0xf2, 0x6e, 0x3f, 0x16, 0x16, 0x98, + 0x2d, 0x2c, 0xf0, 0xb5, 0xb0, 0xc0, 0xdb, 0xd2, 0xaa, 0xcc, 0x96, 0x56, 0xe5, 0x73, 0x69, 0x55, + 0x1e, 0x2e, 0x02, 0xae, 0x46, 0x63, 0xe2, 0x50, 0x11, 0xbb, 0xfa, 0xb0, 0x6e, 0xe4, 0x13, 0x59, + 0x6e, 0xdc, 0x97, 0xd3, 0x4b, 0x77, 0xb2, 0xf9, 0x2d, 0xa6, 0x29, 0x93, 0xa4, 0x96, 0x3f, 0xf9, + 0xf9, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6c, 0xeb, 0x3e, 0x0e, 0x38, 0x02, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + // EmitIBCAck checks the sender can emit the ack and writes the IBC + // acknowledgement + EmitIBCAck(ctx context.Context, in *MsgEmitIBCAck, opts ...grpc.CallOption) (*MsgEmitIBCAckResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) EmitIBCAck(ctx context.Context, in *MsgEmitIBCAck, opts ...grpc.CallOption) (*MsgEmitIBCAckResponse, error) { + out := new(MsgEmitIBCAckResponse) + err := c.cc.Invoke(ctx, "/osmosis.ibchooks.Msg/EmitIBCAck", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // EmitIBCAck checks the sender can emit the ack and writes the IBC + // acknowledgement + EmitIBCAck(context.Context, *MsgEmitIBCAck) (*MsgEmitIBCAckResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) EmitIBCAck(ctx context.Context, req *MsgEmitIBCAck) (*MsgEmitIBCAckResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method EmitIBCAck not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_EmitIBCAck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgEmitIBCAck) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).EmitIBCAck(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.ibchooks.Msg/EmitIBCAck", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).EmitIBCAck(ctx, req.(*MsgEmitIBCAck)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "osmosis.ibchooks.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "EmitIBCAck", + Handler: _Msg_EmitIBCAck_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "osmosis/ibc-hooks/tx.proto", +} + +func (m *MsgEmitIBCAck) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgEmitIBCAck) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgEmitIBCAck) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Channel) > 0 { + i -= len(m.Channel) + copy(dAtA[i:], m.Channel) + i = encodeVarintTx(dAtA, i, uint64(len(m.Channel))) + i-- + dAtA[i] = 0x1a + } + if m.PacketSequence != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.PacketSequence)) + i-- + dAtA[i] = 0x10 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgEmitIBCAckResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgEmitIBCAckResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgEmitIBCAckResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.IbcAck) > 0 { + i -= len(m.IbcAck) + copy(dAtA[i:], m.IbcAck) + i = encodeVarintTx(dAtA, i, uint64(len(m.IbcAck))) + i-- + dAtA[i] = 0x12 + } + if len(m.ContractResult) > 0 { + i -= len(m.ContractResult) + copy(dAtA[i:], m.ContractResult) + i = encodeVarintTx(dAtA, i, uint64(len(m.ContractResult))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgEmitIBCAck) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.PacketSequence != 0 { + n += 1 + sovTx(uint64(m.PacketSequence)) + } + l = len(m.Channel) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgEmitIBCAckResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ContractResult) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.IbcAck) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgEmitIBCAck) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgEmitIBCAck: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgEmitIBCAck: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PacketSequence", wireType) + } + m.PacketSequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PacketSequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Channel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Channel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgEmitIBCAckResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgEmitIBCAckResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgEmitIBCAckResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractResult", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContractResult = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IbcAck", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IbcAck = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/ibc-hooks/types/types.go b/x/ibc-hooks/types/types.go new file mode 100644 index 00000000000..c2afc5e3e89 --- /dev/null +++ b/x/ibc-hooks/types/types.go @@ -0,0 +1,84 @@ +package types + +import ( + "encoding/json" + channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" +) + +// Async: The following types represent the response sent by a contract on OnRecvPacket when it wants the ack to be async + +// OnRecvPacketAsyncAckResponse the response a contract sends to instruct the module to make the ack async +type OnRecvPacketAsyncAckResponse struct { + IsAsyncAck bool `json:"is_async_ack"` +} + +// Async The following types are used to ask a contract that has sent a packet to generate an ack for it + +// RequestAckI internals of IBCAsync +type RequestAckI struct { + PacketSequence uint64 `json:"packet_sequence"` + SourceChannel string `json:"source_channel"` +} + +// RequestAck internals of IBCAsync +type RequestAck struct { + RequestAckI `json:"request_ack"` +} + +// IBCAsync is the sudo message to be sent to the contract for it to generate an ack for a sent packet +type IBCAsync struct { + RequestAck `json:"ibc_async"` +} + +// General + +// ContractAck is the response to be stored when a wasm hook is executed +type ContractAck struct { + ContractResult []byte `json:"contract_result"` + IbcAck []byte `json:"ibc_ack"` +} + +// IBCAckResponse is the response that a contract returns from the sudo() call on OnRecvPacket or RequestAck +type IBCAckResponse struct { + Packet channeltypes.Packet `json:"packet"` + ContractAck ContractAck `json:"contract_ack"` +} + +// IBCAckError is the error that a contract returns from the sudo() call on RequestAck +type IBCAckError struct { + Packet channeltypes.Packet `json:"packet"` + ErrorDescription string `json:"error_description"` + ErrorResponse string `json:"error_response"` +} + +type IBCAck struct { + Type string `json:"type"` + Content json.RawMessage `json:"content"` + // Note: These two fields have to be pointers so that they can be null + // If they are not pointers, they will be empty structs when null, + // which will cause issues with json.Unmarshal. + AckResponse *IBCAckResponse `json:"response,omitempty"` + AckError *IBCAckError `json:"error,omitempty"` +} + +func UnmarshalIBCAck(bz []byte) (*IBCAck, error) { + var ack IBCAck + if err := json.Unmarshal(bz, &ack); err != nil { + return nil, err + } + + switch ack.Type { + case "ack_response": + ack.AckResponse = &IBCAckResponse{} + if err := json.Unmarshal(ack.Content, ack.AckResponse); err != nil { + return nil, err + } + case "ack_error": + ack.AckError = &IBCAckError{} + if err := json.Unmarshal(ack.Content, ack.AckError); err != nil { + return nil, err + } + } + + return &ack, nil +} diff --git a/x/ibc-hooks/wasm_hook.go b/x/ibc-hooks/wasm_hook.go index 75ed8e5ef5b..8c169649b31 100644 --- a/x/ibc-hooks/wasm_hook.go +++ b/x/ibc-hooks/wasm_hook.go @@ -3,18 +3,17 @@ package ibc_hooks import ( "encoding/json" "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" errorsmod "cosmossdk.io/errors" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" - capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" "github.com/osmosis-labs/osmosis/x/ibc-hooks/keeper" - wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" - "github.com/osmosis-labs/osmosis/osmoutils" - sdk "github.com/cosmos/cosmos-sdk/types" transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" ibcexported "github.com/cosmos/ibc-go/v4/modules/core/exported" @@ -22,11 +21,6 @@ import ( "github.com/osmosis-labs/osmosis/x/ibc-hooks/types" ) -type ContractAck struct { - ContractResult []byte `json:"contract_result"` - IbcAck []byte `json:"ibc_ack"` -} - type WasmHooks struct { ContractKeeper *wasmkeeper.PermissionedKeeper ibcHooksKeeper *keeper.Keeper @@ -117,7 +111,24 @@ func (h WasmHooks) OnRecvPacketOverride(im IBCMiddleware, ctx sdk.Context, packe return osmoutils.NewEmitErrorAcknowledgement(ctx, types.ErrWasmError, err.Error()) } - fullAck := ContractAck{ContractResult: response.Data, IbcAck: ack.Acknowledgement()} + // Check if the contract is requesting for the ack to be async. + var asyncAckRequest types.OnRecvPacketAsyncAckResponse + err = json.Unmarshal(response.Data, &asyncAckRequest) + if err == nil { + // If unmarshalling succeeds, the contract is requesting for the ack to be async. + if asyncAckRequest.IsAsyncAck { // in which case IsAsyncAck is expected to be set to true + if !h.ibcHooksKeeper.IsInAllowList(ctx, contractAddr.String()) { + // Only allowed contracts can send async acks + return osmoutils.NewEmitErrorAcknowledgement(ctx, types.ErrAsyncAckNotAllowed) + } + // Store the contract as the packet's ack actor and return nil + h.ibcHooksKeeper.StorePacketAckActor(ctx, packet, contractAddr.String()) + return nil + } + } + + // If the ack is not async, we continue generating the ack and return it + fullAck := types.ContractAck{ContractResult: response.Data, IbcAck: ack.Acknowledgement()} bz, err = json.Marshal(fullAck) if err != nil { return osmoutils.NewEmitErrorAcknowledgement(ctx, types.ErrBadResponse, err.Error()) diff --git a/x/ibc-rate-limit/bytecode/rate_limiter.wasm b/x/ibc-rate-limit/bytecode/rate_limiter.wasm index 9dee6eabb6f..30b545e0612 100644 Binary files a/x/ibc-rate-limit/bytecode/rate_limiter.wasm and b/x/ibc-rate-limit/bytecode/rate_limiter.wasm differ diff --git a/x/ibc-rate-limit/contracts/rate-limiter/src/sudo.rs b/x/ibc-rate-limit/contracts/rate-limiter/src/sudo.rs index 80e6985d578..c42b5ea7e2d 100644 --- a/x/ibc-rate-limit/contracts/rate-limiter/src/sudo.rs +++ b/x/ibc-rate-limit/contracts/rate-limiter/src/sudo.rs @@ -21,7 +21,8 @@ pub fn process_packet( #[cfg(test)] channel_value_mock: Option, ) -> Result { let (channel_id, denom) = packet.path_data(&direction); - let path = &Path::new(channel_id, denom); + #[allow(clippy::needless_borrow)] + let path = &Path::new(&channel_id, &denom); let funds = packet.get_funds(); #[cfg(test)] @@ -144,7 +145,8 @@ fn add_rate_limit_attributes(response: Response, result: &RateLimit) -> Response pub fn undo_send(deps: DepsMut, packet: Packet) -> Result { // Sudo call. Only go modules should be allowed to access this let (channel_id, denom) = packet.path_data(&FlowType::Out); // Sends have direction out. - let path = &Path::new(channel_id, &denom); + #[allow(clippy::needless_borrow)] + let path = &Path::new(&channel_id, &denom); let any_path = Path::new("any", &denom); let funds = packet.get_funds();