diff --git a/app/app.go b/app/app.go index 6ac41a8..8df453e 100644 --- a/app/app.go +++ b/app/app.go @@ -131,6 +131,14 @@ import ( "github.com/dymensionxyz/dymension-rdk/x/hub" hubkeeper "github.com/dymensionxyz/dymension-rdk/x/hub/keeper" hubtypes "github.com/dymensionxyz/dymension-rdk/x/hub/types" + + "github.com/dymensionxyz/rollapp-wasm/x/callback" + callbackKeeper "github.com/dymensionxyz/rollapp-wasm/x/callback/keeper" + callbackTypes "github.com/dymensionxyz/rollapp-wasm/x/callback/types" + + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors" + cwerrorsKeeper "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/keeper" + cwerrorsTypes "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" ) const ( @@ -150,6 +158,8 @@ var ( ibctransfertypes.StoreKey, capabilitytypes.StoreKey, wasmtypes.StoreKey, gaslesstypes.StoreKey, hubtypes.StoreKey, + callbackTypes.StoreKey, + cwerrorsTypes.StoreKey, } ) @@ -196,6 +206,8 @@ var ( hubgenesis.AppModuleBasic{}, wasm.AppModuleBasic{}, hub.AppModuleBasic{}, + callback.AppModuleBasic{}, + cwerrors.AppModuleBasic{}, ) // module account permissions @@ -211,6 +223,7 @@ var ( wasmtypes.ModuleName: {authtypes.Burner}, hubgentypes.ModuleName: {authtypes.Minter}, gaslesstypes.ModuleName: nil, + callbackTypes.ModuleName: nil, } // module accounts that are allowed to receive tokens @@ -270,6 +283,8 @@ type App struct { WasmKeeper wasmkeeper.Keeper FeeGrantKeeper feegrantkeeper.Keeper GaslessKeeper gaslesskeeper.Keeper + CallbackKeeper callbackKeeper.Keeper + CWErrorsKeeper cwerrorsKeeper.Keeper // make scoped keepers public for test purposes ScopedIBCKeeper capabilitykeeper.ScopedKeeper @@ -321,7 +336,7 @@ func NewRollapp( keys := sdk.NewKVStoreKeys( kvstorekeys..., ) - tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) + tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey, cwerrorsTypes.TStoreKey) memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) // load state streaming if enabled @@ -523,6 +538,21 @@ func NewRollapp( app.BankKeeper, ) + app.CallbackKeeper = callbackKeeper.NewKeeper( + appCodec, + keys[callbackTypes.StoreKey], + &app.WasmKeeper, + app.BankKeeper, + ) + + app.CWErrorsKeeper = cwerrorsKeeper.NewKeeper( + appCodec, + keys[cwerrorsTypes.StoreKey], + tkeys[cwerrorsTypes.TStoreKey], + &app.WasmKeeper, + app.BankKeeper, + ) + wasmDir := filepath.Join(homePath, "wasm") wasmConfig, err := wasm.ReadWasmConfig(appOpts) if err != nil { @@ -534,6 +564,11 @@ func NewRollapp( govRouter.AddRoute(wasm.RouterKey, wasm.NewWasmProposalHandler(app.WasmKeeper, enabledProposals)) } + // Include the x/cwerrors query to stargate queries + wasmOpts = append(wasmOpts, wasmkeeper.WithQueryPlugins(&wasmkeeper.QueryPlugins{ + Stargate: wasmkeeper.AcceptListStargateQuerier(getAcceptedStargateQueries(), app.GRPCQueryRouter(), appCodec), + })) + // The last arguments can contain custom message handlers, and custom query handlers, // if we want to allow any custom callbacks availableCapabilities := strings.Join(AllCapabilities(), ",") @@ -604,6 +639,8 @@ func NewRollapp( upgrade.NewAppModule(app.UpgradeKeeper), hubgenesis.NewAppModule(appCodec, app.HubGenesisKeeper), hub.NewAppModule(appCodec, app.HubKeeper), + callback.NewAppModule(app.appCodec, app.CallbackKeeper, app.WasmKeeper, app.CWErrorsKeeper), + cwerrors.NewAppModule(app.appCodec, app.CWErrorsKeeper, app.WasmKeeper), } app.mm = module.NewManager(modules...) @@ -635,6 +672,8 @@ func NewRollapp( hubgentypes.ModuleName, hubtypes.ModuleName, wasm.ModuleName, + callbackTypes.ModuleName, + cwerrorsTypes.ModuleName, // does not have begin blocker } app.mm.SetOrderBeginBlockers(beginBlockersList...) @@ -660,6 +699,8 @@ func NewRollapp( hubgentypes.ModuleName, hubtypes.ModuleName, wasm.ModuleName, + callbackTypes.ModuleName, + cwerrorsTypes.ModuleName, } app.mm.SetOrderEndBlockers(endBlockersList...) @@ -691,6 +732,8 @@ func NewRollapp( hubgentypes.ModuleName, hubtypes.ModuleName, wasm.ModuleName, + callbackTypes.ModuleName, + cwerrorsTypes.ModuleName, } app.mm.SetOrderInitGenesis(initGenesisList...) @@ -1017,5 +1060,13 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino paramsKeeper.Subspace(gaslesstypes.ModuleName) paramsKeeper.Subspace(wasmtypes.ModuleName) + paramsKeeper.Subspace(callbackTypes.ModuleName) + paramsKeeper.Subspace(cwerrorsTypes.ModuleName) return paramsKeeper } + +func getAcceptedStargateQueries() wasmkeeper.AcceptedStargateQueries { + return wasmkeeper.AcceptedStargateQueries{ + "/rollapp.cwerrors.v1.Query/Errors": &cwerrorsTypes.QueryErrorsRequest{}, + } +} diff --git a/app/test_app.go b/app/test_app.go new file mode 100644 index 0000000..9e52f54 --- /dev/null +++ b/app/test_app.go @@ -0,0 +1,36 @@ +package app + +import ( + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtypes "github.com/tendermint/tendermint/types" + "time" + + // unnamed import of statik for swagger UI support + _ "github.com/cosmos/cosmos-sdk/client/docs/statik" +) + +var DefaultConsensusParams = &abci.ConsensusParams{ + Block: &abci.BlockParams{ + MaxBytes: 200000, + MaxGas: -1, + }, + Evidence: &tmproto.EvidenceParams{ + MaxAgeNumBlocks: 302400, + MaxAgeDuration: 504 * time.Hour, // 3 weeks is the max duration + MaxBytes: 10000, + }, + Validator: &tmproto.ValidatorParams{ + PubKeyTypes: []string{ + tmtypes.ABCIPubKeyTypeEd25519, + }, + }, +} + +// EmptyAppOptions is a stub implementing AppOptions +type EmptyAppOptions struct{} + +// Get implements AppOptions +func (ao EmptyAppOptions) Get(o string) interface{} { + return nil +} diff --git a/app/wasm.go b/app/wasm.go index 566901c..7407991 100644 --- a/app/wasm.go +++ b/app/wasm.go @@ -43,5 +43,6 @@ func AllCapabilities() []string { "stargate", "cosmwasm_1_1", "cosmwasm_1_2", + "cosmwasm_1_3", } } diff --git a/contracts/callback-test/.editorconfig b/contracts/callback-test/.editorconfig new file mode 100644 index 0000000..3d36f20 --- /dev/null +++ b/contracts/callback-test/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.rs] +indent_size = 4 diff --git a/contracts/callback-test/.gitignore b/contracts/callback-test/.gitignore new file mode 100644 index 0000000..9095dea --- /dev/null +++ b/contracts/callback-test/.gitignore @@ -0,0 +1,16 @@ +# Build results +/target +/schema + +# Cargo+Git helper file (https://github.com/rust-lang/cargo/blob/0.44.1/src/cargo/sources/git/utils.rs#L320-L327) +.cargo-ok + +# Text file backups +**/*.rs.bk + +# macOS +.DS_Store + +# IDEs +*.iml +.idea diff --git a/contracts/callback-test/Cargo.lock b/contracts/callback-test/Cargo.lock new file mode 100644 index 0000000..a7e1e9a --- /dev/null +++ b/contracts/callback-test/Cargo.lock @@ -0,0 +1,806 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bnum" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab9008b6bb9fc80b5277f2fe481c09e828743d9151203e804583eb4c9e15b31d" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "callback-test" +version = "0.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-multi-test", + "cw-storage-plus", + "cw2", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + +[[package]] +name = "cosmwasm-crypto" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8bb3c77c3b7ce472056968c745eb501c440fbc07be5004eba02782c35bfbbe3" +dependencies = [ + "digest 0.10.7", + "ecdsa", + "ed25519-zebra", + "k256", + "rand_core 0.6.4", + "thiserror", +] + +[[package]] +name = "cosmwasm-derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea73e9162e6efde00018d55ed0061e93a108b5d6ec4548b4f8ce3c706249687" +dependencies = [ + "syn 1.0.109", +] + +[[package]] +name = "cosmwasm-schema" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df41ea55f2946b6b43579659eec048cc2f66e8c8e2e3652fc5e5e476f673856" +dependencies = [ + "cosmwasm-schema-derive", + "schemars", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cosmwasm-schema-derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43609e92ce1b9368aa951b334dd354a2d0dd4d484931a5f83ae10e12a26c8ba9" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "cosmwasm-std" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04d6864742e3a7662d024b51a94ea81c9af21db6faea2f9a6d2232bb97c6e53e" +dependencies = [ + "base64", + "bech32", + "bnum", + "cosmwasm-crypto", + "cosmwasm-derive", + "derivative", + "forward_ref", + "hex", + "schemars", + "serde", + "serde-json-wasm", + "sha2 0.10.8", + "static_assertions", + "thiserror", +] + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "cw-multi-test" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d818f5323c80ed4890db7f89d65eda3f0261fe21878e628c27ea2d8de4b7ba4" +dependencies = [ + "anyhow", + "cosmwasm-std", + "cw-storage-plus", + "cw-utils", + "derivative", + "itertools", + "prost", + "schemars", + "serde", + "sha2 0.10.8", + "thiserror", +] + +[[package]] +name = "cw-storage-plus" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5ff29294ee99373e2cd5fd21786a3c0ced99a52fec2ca347d565489c61b723c" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", +] + +[[package]] +name = "cw-utils" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c4a657e5caacc3a0d00ee96ca8618745d050b8f757c709babafb81208d4239c" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw2", + "schemars", + "semver", + "serde", + "thiserror", +] + +[[package]] +name = "cw2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6c120b24fbbf5c3bedebb97f2cc85fbfa1c3287e09223428e7e597b5293c1fa" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "schemars", + "semver", + "serde", + "thiserror", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dyn-clone" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519-zebra" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +dependencies = [ + "curve25519-dalek", + "hashbrown", + "hex", + "rand_core 0.6.4", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "forward_ref" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "k256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f01b677d82ef7a676aa37e099defd83a28e15687112cafdd112d60236b6115b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2 0.10.8", + "signature", +] + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.40", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "schemars" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-json-wasm" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.40", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.40", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/contracts/callback-test/Cargo.toml b/contracts/callback-test/Cargo.toml new file mode 100644 index 0000000..41322da --- /dev/null +++ b/contracts/callback-test/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "callback-test" +version = "0.1.0" +authors = ["Spoorthi "] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["cdylib", "rlib"] + +[profile.release] +opt-level = 3 +debug = false +rpath = false +lto = true +debug-assertions = false +codegen-units = 1 +panic = 'abort' +incremental = false +overflow-checks = true + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] + +[package.metadata.scripts] +optimize = """docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/optimizer:0.15.0 +""" + +[dependencies] +cosmwasm-schema = "1.5.0" +cosmwasm-std = { version = "1.5.0", features = [ + "cosmwasm_1_3", + # Enable this if you only deploy to chains that have CosmWasm 1.4 or higher + # "cosmwasm_1_4", +] } +cw-storage-plus = "1.1.0" +cw2 = "1.1.1" +schemars = "0.8.15" +serde = { version = "1.0.189", default-features = false, features = ["derive"] } +thiserror = { version = "1.0.49" } + +[dev-dependencies] +cw-multi-test = "0.17.0" diff --git a/contracts/callback-test/README.md b/contracts/callback-test/README.md new file mode 100644 index 0000000..bab47dd --- /dev/null +++ b/contracts/callback-test/README.md @@ -0,0 +1,65 @@ +# callback-test + +This contract is a sample contract which is used to test the x/callback module functionalty. + +The implementation is based on the sample counter contract and has been modified to have callback msg in the Sudo entrypoint. + +The following changes have been made + +```rust +// msg.rs + +#[cw_serde] +pub enum SudoMsg { + Callback { + job_id: u64 + }, + Error { + module_name: String, + error_code: u32, + contract_address: String, + input_payload: String, + error_message: String, + }, +} +``` + +```rust +//contract.rs + +pub mod sudo { + use super::*; + use std::u64; + + pub fn handle_callback(deps: DepsMut, job_id: u64) -> Result { + STATE.update(deps.storage, |mut state| -> Result<_, ContractError> { + if job_id == 0 { + state.count -= 1; // Decrement the count + }; + if job_id == 1 { + state.count += 1; // Increment the count + }; + if job_id == 2 { + return Err(ContractError::SomeError {}); // Throw an error + } + // else do nothing + Ok(state) + })?; + + Ok(Response::new().add_attribute("action", "handle_callback")) + } + + pub fn handle_error(deps: DepsMut, module_name: String, error_code: u32, _contract_address: String, _input_payload: String, _error_message: String) -> Result { + STATE.update(deps.storage, |mut state| -> Result<_, ContractError> { + if module_name == "callback" && error_code == 2 { + state.count = 0; // reset the counter + } + Ok(state) + })?; + + Ok(Response::new().add_attribute("action", "handle_error")) + } +} +``` + +Relevant test has been added as well in contract.rs and the default counter init/execute tests removed \ No newline at end of file diff --git a/contracts/callback-test/artifacts/callback_test.wasm b/contracts/callback-test/artifacts/callback_test.wasm new file mode 100644 index 0000000..be82625 Binary files /dev/null and b/contracts/callback-test/artifacts/callback_test.wasm differ diff --git a/contracts/callback-test/artifacts/checksums.txt b/contracts/callback-test/artifacts/checksums.txt new file mode 100644 index 0000000..26659a0 --- /dev/null +++ b/contracts/callback-test/artifacts/checksums.txt @@ -0,0 +1 @@ +136f06a7a6d9192218dde561353c6c76e2f54fa96355cc4e1038ddd161e53df7 callback_test.wasm diff --git a/contracts/callback-test/src/bin/schema.rs b/contracts/callback-test/src/bin/schema.rs new file mode 100644 index 0000000..ada7b40 --- /dev/null +++ b/contracts/callback-test/src/bin/schema.rs @@ -0,0 +1,12 @@ +use cosmwasm_schema::write_api; + +use callback_test::msg::{ExecuteMsg, InstantiateMsg, QueryMsg, SudoMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + execute: ExecuteMsg, + query: QueryMsg, + sudo: SudoMsg, + } +} diff --git a/contracts/callback-test/src/contract.rs b/contracts/callback-test/src/contract.rs new file mode 100644 index 0000000..d8b2419 --- /dev/null +++ b/contracts/callback-test/src/contract.rs @@ -0,0 +1,192 @@ +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; +use cw2::set_contract_version; + +use crate::error::ContractError; +use crate::msg::{ExecuteMsg, GetCountResponse, InstantiateMsg, QueryMsg, SudoMsg}; +use crate::state::{State, STATE}; + +// version info for migration info +const CONTRACT_NAME: &str = "crates.io:callback-test"; +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + let state = State { + count: msg.count, + owner: info.sender.clone(), + }; + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + STATE.save(deps.storage, &state)?; + + Ok(Response::new() + .add_attribute("method", "instantiate") + .add_attribute("owner", info.sender) + .add_attribute("count", msg.count.to_string())) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + _env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + match msg { + ExecuteMsg::Increment {} => execute::increment(deps), + ExecuteMsg::Reset { count } => execute::reset(deps, info, count), + } +} + +pub mod execute { + use super::*; + + pub fn increment(deps: DepsMut) -> Result { + STATE.update(deps.storage, |mut state| -> Result<_, ContractError> { + state.count += 1; + Ok(state) + })?; + + Ok(Response::new().add_attribute("action", "increment")) + } + + pub fn reset(deps: DepsMut, info: MessageInfo, count: i32) -> Result { + STATE.update(deps.storage, |mut state| -> Result<_, ContractError> { + if info.sender != state.owner { + return Err(ContractError::Unauthorized {}); + } + state.count = count; + Ok(state) + })?; + Ok(Response::new().add_attribute("action", "reset")) + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::GetCount {} => to_json_binary(&query::count(deps)?), + } +} + +pub mod query { + use super::*; + + pub fn count(deps: Deps) -> StdResult { + let state = STATE.load(deps.storage)?; + Ok(GetCountResponse { count: state.count }) + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn sudo( + deps: DepsMut, + _env: Env, + msg: SudoMsg, +) -> Result { + match msg { + SudoMsg::Callback { job_id } => sudo::handle_callback(deps, job_id), + SudoMsg::Error { + module_name, + error_code, + contract_address, + input_payload, + error_message, + } => sudo::handle_error(deps, module_name, error_code, contract_address, input_payload, error_message), + } +} + +pub mod sudo { + use super::*; + use std::u64; + + pub fn handle_callback(deps: DepsMut, job_id: u64) -> Result { + STATE.update(deps.storage, |mut state| -> Result<_, ContractError> { + if job_id == 0 { + state.count -= 1; + }; + if job_id == 1 { + state.count += 1; + }; + if job_id == 2 { + return Err(ContractError::SomeError {}); + } + Ok(state) + })?; + + Ok(Response::new().add_attribute("action", "handle_callback")) + } + + pub fn handle_error(deps: DepsMut, module_name: String, error_code: u32, _contract_address: String, _input_payload: String, _error_message: String) -> Result { + STATE.update(deps.storage, |mut state| -> Result<_, ContractError> { + if module_name == "callback" && error_code == 1 { + state.count = 0; // reset the counter when out of gas error + } + Ok(state) + })?; + + Ok(Response::new().add_attribute("action", "handle_error")) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cosmwasm_std::{coins, from_json}; + + use crate::contract::sudo::{handle_callback, handle_error}; + + #[test] + fn callback() { + let mut deps = mock_dependencies(); + + let msg = InstantiateMsg { count: 100 }; + let info = mock_info("creator", &coins(2, "token")); + let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); + + // decrement the counter + let _res = handle_callback(deps.as_mut(), 0); + let res = query(deps.as_ref(), mock_env(), QueryMsg::GetCount {}).unwrap(); + let value: GetCountResponse = from_json(&res).unwrap(); + assert_eq!(99, value.count); + + // increment the counter + let _res = handle_callback(deps.as_mut(), 1); + let res = query(deps.as_ref(), mock_env(), QueryMsg::GetCount {}).unwrap(); + let value: GetCountResponse = from_json(&res).unwrap(); + assert_eq!(100, value.count); + + // return error + let res = handle_callback(deps.as_mut(), 2); + match res { + Err(ContractError::SomeError {}) => {} + _ => panic!("Must return some error"), + } + + // do nothing + let _res = handle_callback(deps.as_mut(), 3); + let res = query(deps.as_ref(), mock_env(), QueryMsg::GetCount {}).unwrap(); + let value: GetCountResponse = from_json(&res).unwrap(); + assert_eq!(100, value.count); + + // error callback - unknown module and error code - do nothing + let _res = handle_error(deps.as_mut(), "unknown".to_string(), 2, "contract".to_string(), "".to_string(), "".to_string()); + let res = query(deps.as_ref(), mock_env(), QueryMsg::GetCount {}).unwrap(); + let value: GetCountResponse = from_json(&res).unwrap(); + assert_eq!(100, value.count); + + // error callback - when module is "callback" and error_code is 1, reset the counter + let _res = handle_error(deps.as_mut(), "callback".to_string(), 1, "contract".to_string(), "".to_string(), "".to_string()); + let res = query(deps.as_ref(), mock_env(), QueryMsg::GetCount {}).unwrap(); + let value: GetCountResponse = from_json(&res).unwrap(); + assert_eq!(0, value.count); + + } +} diff --git a/contracts/callback-test/src/error.rs b/contracts/callback-test/src/error.rs new file mode 100644 index 0000000..efb75c9 --- /dev/null +++ b/contracts/callback-test/src/error.rs @@ -0,0 +1,14 @@ +use cosmwasm_std::StdError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ContractError { + #[error("{0}")] + Std(#[from] StdError), + + #[error("Unauthorized")] + Unauthorized {}, + + #[error("SomeError")] + SomeError {}, +} diff --git a/contracts/callback-test/src/lib.rs b/contracts/callback-test/src/lib.rs new file mode 100644 index 0000000..dfedc9d --- /dev/null +++ b/contracts/callback-test/src/lib.rs @@ -0,0 +1,6 @@ +pub mod contract; +mod error; +pub mod msg; +pub mod state; + +pub use crate::error::ContractError; diff --git a/contracts/callback-test/src/msg.rs b/contracts/callback-test/src/msg.rs new file mode 100644 index 0000000..578f005 --- /dev/null +++ b/contracts/callback-test/src/msg.rs @@ -0,0 +1,39 @@ +use cosmwasm_schema::{cw_serde, QueryResponses}; + +#[cw_serde] +pub struct InstantiateMsg { + pub count: i32, +} + +#[cw_serde] +pub enum ExecuteMsg { + Increment {}, + Reset { count: i32 }, +} + +#[cw_serde] +pub enum SudoMsg { + Callback { + job_id: u64, + }, + Error { + module_name: String, + error_code: u32, + contract_address: String, + input_payload: String, + error_message: String, + }, +} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryMsg { + // GetCount returns the current count as a json-encoded number + #[returns(GetCountResponse)] + GetCount {}, +} + +#[cw_serde] +pub struct GetCountResponse { + pub count: i32, +} diff --git a/contracts/callback-test/src/state.rs b/contracts/callback-test/src/state.rs new file mode 100644 index 0000000..bad9202 --- /dev/null +++ b/contracts/callback-test/src/state.rs @@ -0,0 +1,13 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use cosmwasm_std::Addr; +use cw_storage_plus::Item; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +pub struct State { + pub count: i32, + pub owner: Addr, +} + +pub const STATE: Item = Item::new("state"); diff --git a/e2e/testing/chain.go b/e2e/testing/chain.go new file mode 100644 index 0000000..f9cede7 --- /dev/null +++ b/e2e/testing/chain.go @@ -0,0 +1,633 @@ +// DONTCOVER +package e2eTesting + +import ( + "encoding/json" + "math/rand" + "strconv" + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + + seqtypes "github.com/dymensionxyz/dymension-rdk/x/sequencers/types" + + "github.com/CosmWasm/wasmd/x/wasm" + "github.com/cosmos/cosmos-sdk/codec" + codecTypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptoCodec "github.com/cosmos/cosmos-sdk/crypto/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptoTypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authsign "github.com/cosmos/cosmos-sdk/x/auth/signing" + authTypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bankTypes "github.com/cosmos/cosmos-sdk/x/bank/types" + slashingTypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/ibc-go/v6/testing/mock" + "github.com/golang/protobuf/proto" //nolint:staticcheck + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/log" + tmProto "github.com/tendermint/tendermint/proto/tendermint/types" + tmTypes "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/dymensionxyz/rollapp-wasm/app" +) + +var ( + TestAccountAddr = sdk.AccAddress("test") + ProposerPK = simapp.CreateTestPubKeys(1)[0] + OperatorPK = secp256k1.GenPrivKey().PubKey() +) + +// TestChain keeps a test chain state and provides helper functions to simulate various operations. +// Heavily inspired by the TestChain from the ibc-go repo (https://github.com/cosmos/ibc-go/blob/main/testing/chain.go). +// Reasons for creating a custom TestChain rather than using the ibc-go's one are: to simplify it, +// add contract related helpers and fix errors caused by x/gastracker module (ibc-go version starts at block 2). +type TestChain struct { + t *testing.T + + cfg chainConfig + app *app.App // main application + lastHeader tmProto.Header // header for the last committed block + curHeader tmProto.Header // header for the current block + txConfig client.TxConfig // config to sing TXs + valSet *tmTypes.ValidatorSet // validator set for the current block + valSigners []tmTypes.PrivValidator // validator signers for the current block + accPrivKeys []cryptoTypes.PrivKey // genesis account private keys +} + +// NewTestChain creates a new TestChain with the default amount of genesis accounts and validators. +func NewTestChain(t *testing.T, chainIdx int, opts ...interface{}) *TestChain { + chainid := "test-" + strconv.Itoa(chainIdx) + + pk, err := cryptocodec.ToTmProtoPublicKey(ProposerPK) + require.NoError(t, err) + + // Split options by groups (each group is applied in a different init step) + var chainCfgOpts []TestChainConfigOption + var consensusParamsOpts []TestChainConsensusParamsOption + var genStateOpts []TestChainGenesisOption + for i, opt := range opts { + switch opt := opt.(type) { + case TestChainConfigOption: + chainCfgOpts = append(chainCfgOpts, opt) + case TestChainConsensusParamsOption: + consensusParamsOpts = append(consensusParamsOpts, opt) + case TestChainGenesisOption: + genStateOpts = append(genStateOpts, opt) + default: + require.Fail(t, "Unknown chain option type", "optionIdx", i) + } + } + + // Define chain config + chainCfg := defaultChainConfig() + for _, opt := range chainCfgOpts { + opt(&chainCfg) + } + + // Create an app and a default genesis state + encCfg := app.MakeEncodingConfig() + + // Pick your poison here =) + logger := log.NewNopLogger() + if chainCfg.LoggerEnabled { + logger = log.TestingLogger() + } + + var emptyWasmOpts []wasm.Option + rollApp := app.NewRollapp( + logger, + dbm.NewMemDB(), + nil, + true, map[int64]bool{}, + app.DefaultNodeHome, + 1, + encCfg, + app.GetEnabledProposals(), + app.EmptyAppOptions{}, + emptyWasmOpts, + ) + genState := app.NewDefaultGenesisState(rollApp.AppCodec()) + + // Generate validators + validators := make([]*tmTypes.Validator, 0, chainCfg.ValidatorsNum) + valSigners := make([]tmTypes.PrivValidator, 0, chainCfg.ValidatorsNum) + for i := 0; i < chainCfg.ValidatorsNum; i++ { + valPrivKey := mock.NewPV() + valPubKey, err := valPrivKey.GetPubKey() + require.NoError(t, err) + + validators = append(validators, tmTypes.NewValidator(valPubKey, 1)) + valSigners = append(valSigners, valPrivKey) + } + validatorSet := tmTypes.NewValidatorSet(validators) + + // Generate genesis accounts, gen and bond coins + genAccs := make([]authTypes.GenesisAccount, 0, chainCfg.GenAccountsNum) + genAccPrivKeys := make([]cryptoTypes.PrivKey, 0, chainCfg.GenAccountsNum) + for i := 0; i < chainCfg.GenAccountsNum; i++ { + accPrivKey := secp256k1.GenPrivKey() + acc := authTypes.NewBaseAccount(accPrivKey.PubKey().Address().Bytes(), accPrivKey.PubKey(), uint64(i), 0) + + genAccs = append(genAccs, acc) + genAccPrivKeys = append(genAccPrivKeys, accPrivKey) + } + if chainCfg.DummyTestAddr { + genAccs = append(genAccs, authTypes.NewBaseAccount(TestAccountAddr, nil, uint64(len(genAccs))-1, 0)) // deterministic account for testing purposes + } + + genAmt, ok := sdk.NewIntFromString(chainCfg.GenBalanceAmount) + require.True(t, ok) + genCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, genAmt)) + + bondAmt, ok := sdk.NewIntFromString(chainCfg.BondAmount) + require.True(t, ok) + bondCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)) + + seqGenesis := seqtypes.GenesisState{ + Params: seqtypes.DefaultParams(), + GenesisOperatorAddress: sdk.ValAddress(OperatorPK.Address()).String(), + } + genState[seqtypes.ModuleName] = rollApp.AppCodec().MustMarshalJSON(&seqGenesis) + + // Update the x/auth genesis with gen accounts + authGenesis := authTypes.NewGenesisState(authTypes.DefaultParams(), genAccs) + genState[authTypes.ModuleName] = rollApp.AppCodec().MustMarshalJSON(authGenesis) + + // Update the x/staking genesis (every gen account is a corresponding validator's delegator) + stakingValidators := make([]stakingTypes.Validator, 0, len(validatorSet.Validators)) + stakingDelegations := make([]stakingTypes.Delegation, 0, len(validatorSet.Validators)) + for i, val := range validatorSet.Validators { + valPubKey, err := cryptoCodec.FromTmPubKeyInterface(val.PubKey) + require.NoError(t, err) + + valPubKeyAny, err := codecTypes.NewAnyWithValue(valPubKey) + require.NoError(t, err) + + validator := stakingTypes.Validator{ + OperatorAddress: sdk.ValAddress(val.Address).String(), + ConsensusPubkey: valPubKeyAny, + Jailed: false, + Status: stakingTypes.Bonded, + Tokens: bondAmt, + DelegatorShares: sdk.OneDec(), + Description: stakingTypes.Description{}, + UnbondingHeight: int64(0), + UnbondingTime: time.Unix(0, 0).UTC(), + Commission: stakingTypes.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), + MinSelfDelegation: sdk.ZeroInt(), + } + + stakingValidators = append(stakingValidators, validator) + stakingDelegations = append(stakingDelegations, stakingTypes.NewDelegation(genAccs[i].GetAddress(), val.Address.Bytes(), sdk.OneDec())) + } + + stakingGenesis := stakingTypes.NewGenesisState(stakingTypes.DefaultParams(), stakingValidators, stakingDelegations) + genState[stakingTypes.ModuleName] = rollApp.AppCodec().MustMarshalJSON(stakingGenesis) + + // Update x/bank genesis with total supply, gen account balances and bonding pool balance + totalSupply := sdk.NewCoins() + bondedPoolCoins := sdk.NewCoins() + balances := make([]bankTypes.Balance, 0, chainCfg.GenAccountsNum) + for i := 0; i < chainCfg.GenAccountsNum; i++ { + accGenCoins := genCoins + // Lower genesis balance for validator account + if i < chainCfg.ValidatorsNum { + accGenCoins = accGenCoins.Sub(bondCoins...) + bondedPoolCoins = bondedPoolCoins.Add(bondCoins...) + } + + balances = append(balances, bankTypes.Balance{ + Address: genAccs[i].GetAddress().String(), + Coins: accGenCoins, + }) + totalSupply = totalSupply.Add(genCoins...) + } + + if chainCfg.DummyTestAddr { + balances = append(balances, bankTypes.Balance{ + Address: TestAccountAddr.String(), // add some balances to our dummy + Coins: genCoins, + }) + totalSupply = totalSupply.Add(genCoins...) + } + + balances = append(balances, bankTypes.Balance{ + Address: authTypes.NewModuleAddress(stakingTypes.BondedPoolName).String(), + Coins: bondedPoolCoins, + }) + + bankGenesis := bankTypes.NewGenesisState(bankTypes.DefaultGenesisState().Params, balances, totalSupply, []bankTypes.Metadata{}) + genState[bankTypes.ModuleName] = rollApp.AppCodec().MustMarshalJSON(bankGenesis) + + signInfo := make([]slashingTypes.SigningInfo, len(validatorSet.Validators)) + for i, v := range validatorSet.Validators { + signInfo[i] = slashingTypes.SigningInfo{ + Address: sdk.ConsAddress(v.Address).String(), + ValidatorSigningInfo: slashingTypes.ValidatorSigningInfo{ + Address: sdk.ConsAddress(v.Address).String(), + }, + } + } + genState[slashingTypes.ModuleName] = rollApp.AppCodec().MustMarshalJSON(slashingTypes.NewGenesisState(slashingTypes.DefaultParams(), signInfo, nil)) + + // Apply genesis options + for _, opt := range genStateOpts { + opt(rollApp.AppCodec(), genState) + } + + // Apply consensus params options + consensusParams := app.DefaultConsensusParams + // for _, opt := range consensusParamsOpts { + // opt(consensusParams) + // } + + // Init chain + genStateBytes, err := json.MarshalIndent(genState, "", " ") + require.NoError(t, err) + + rollApp.InitChain( + abci.RequestInitChain{ + Time: time.Time{}, + ChainId: chainid, + ConsensusParams: consensusParams, + Validators: []abci.ValidatorUpdate{ + {PubKey: pk, Power: 1}, + }, + AppStateBytes: genStateBytes, + InitialHeight: 0, + }, + ) + + // Create a chain and finalize the 1st block + chain := TestChain{ + t: t, + cfg: chainCfg, + app: rollApp, + curHeader: tmProto.Header{ + ChainID: chainid, + Time: time.Unix(0, 0).UTC(), + }, + txConfig: encCfg.TxConfig, + valSet: validatorSet, + valSigners: valSigners, + accPrivKeys: genAccPrivKeys, + } + chain.BeginBlock() + chain.EndBlock() + + // Start a new block + chain.BeginBlock() + return &chain +} + +// GetAccount returns account address and private key with the given index. +func (chain *TestChain) GetAccount(idx int) Account { + t := chain.t + + require.Less(t, idx, len(chain.accPrivKeys)) + privKey := chain.accPrivKeys[idx] + + return Account{ + Address: sdk.AccAddress(privKey.PubKey().Address().Bytes()), + PrivKey: privKey, + } +} + +// GetBalance returns the balance of the given account. +func (chain *TestChain) GetBalance(accAddr sdk.AccAddress) sdk.Coins { + return chain.app.BankKeeper.GetAllBalances(chain.GetContext(), accAddr) +} + +// GetModuleBalance returns the balance of the given module. +func (chain *TestChain) GetModuleBalance(moduleName string) sdk.Coins { + ctx := chain.GetContext() + moduleAcc := chain.app.AccountKeeper.GetModuleAccount(ctx, moduleName) + + return chain.app.BankKeeper.GetAllBalances(chain.GetContext(), moduleAcc.GetAddress()) +} + +// GetContext returns a context for the current block. +func (chain *TestChain) GetContext() sdk.Context { + ctx := chain.app.BaseApp.NewContext(false, chain.curHeader) + + blockGasMeter := sdk.NewInfiniteGasMeter() + blockMaxGas := chain.app.GetConsensusParams(ctx).Block.MaxGas + if blockMaxGas >= 0 { + blockGasMeter = sdk.NewGasMeter(sdk.Gas(blockMaxGas)) + } + + return ctx.WithBlockGasMeter(blockGasMeter) +} + +// GetAppCodec returns the application codec. +func (chain *TestChain) GetAppCodec() codec.Codec { + return chain.app.AppCodec() +} + +// GetChainID returns the chain ID. +func (chain *TestChain) GetChainID() string { + return chain.curHeader.ChainID +} + +// GetBlockTime returns the current block time. +func (chain *TestChain) GetBlockTime() time.Time { + return chain.curHeader.Time +} + +// GetBlockHeight returns the current block height. +func (chain *TestChain) GetBlockHeight() int64 { + return chain.app.LastBlockHeight() +} + +// GetUnbondingTime returns x/staking validator unbonding time. +func (chain *TestChain) GetUnbondingTime() time.Duration { + return chain.app.StakingKeeper.UnbondingTime(chain.GetContext()) +} + +// GetApp returns the application. +func (chain *TestChain) GetApp() *app.App { + return chain.app +} + +// NextBlock starts a new block with options time shift. +func (chain *TestChain) NextBlock(skipTime time.Duration) []abci.Event { + ebEvents := chain.EndBlock() + + chain.curHeader.Time = chain.curHeader.Time.Add(skipTime) + bbEvents := chain.BeginBlock() + + return append(ebEvents, bbEvents...) +} + +func (chain *TestChain) GoToHeight(height int64, skipTime time.Duration) { + if chain.GetBlockHeight() > height { + panic("can't go to past height") + } + for chain.GetBlockHeight() < height { + chain.NextBlock(skipTime) + } +} + +// BeginBlock begins a new block. +func (chain *TestChain) BeginBlock() []abci.Event { + const blockDur = 5 * time.Second + + chain.lastHeader = chain.curHeader + + chain.curHeader.Height++ + chain.curHeader.Time = chain.curHeader.Time.Add(blockDur) + chain.curHeader.AppHash = chain.app.LastCommitID().Hash + chain.curHeader.ValidatorsHash = chain.valSet.Hash() + chain.curHeader.NextValidatorsHash = chain.valSet.Hash() + chain.curHeader.ProposerAddress = chain.GetCurrentValSet().Proposer.Address + + voteInfo := make([]abci.VoteInfo, len(chain.GetCurrentValSet().Validators)) + for i, v := range chain.GetCurrentValSet().Validators { + voteInfo[i] = abci.VoteInfo{ + Validator: abci.Validator{ + Address: v.Address, + Power: v.VotingPower, + }, + SignedLastBlock: true, + } + } + + res := chain.app.BeginBlock(abci.RequestBeginBlock{ + Hash: nil, + Header: chain.curHeader, + LastCommitInfo: abci.LastCommitInfo{ + Round: 0, + Votes: voteInfo, + }, + ByzantineValidators: nil, + }) + + return res.Events +} + +// EndBlock finalizes the current block. +func (chain *TestChain) EndBlock() []abci.Event { + res := chain.app.EndBlock(abci.RequestEndBlock{Height: chain.curHeader.Height}) + chain.app.Commit() + + return res.Events +} + +type ( + SendMsgOption func(opt *sendMsgOptions) + + sendMsgOptions struct { + fees sdk.Coins + gasLimit uint64 + noBlockChange bool + simulate bool + granter sdk.AccAddress + } +) + +func WithGranter(granter sdk.AccAddress) SendMsgOption { + return func(opt *sendMsgOptions) { + opt.granter = granter + } +} + +// WithMsgFees option add fees to the transaction. +func WithMsgFees(coins ...sdk.Coin) SendMsgOption { + return func(opt *sendMsgOptions) { + opt.fees = coins + } +} + +// WithTxGasLimit option overrides the default gas limit for the transaction. +func WithTxGasLimit(limit uint64) SendMsgOption { + return func(opt *sendMsgOptions) { + opt.gasLimit = limit + } +} + +// WithoutBlockChange option disables EndBlocker and BeginBlocker after the transaction. +func WithoutBlockChange() SendMsgOption { + return func(opt *sendMsgOptions) { + opt.noBlockChange = true + } +} + +// WithSimulation options estimates gas usage for the transaction. +func WithSimulation() SendMsgOption { + return func(opt *sendMsgOptions) { + opt.simulate = true + opt.noBlockChange = true + } +} + +// SendMsgs sends a series of messages, checks for tx failure and starts a new block. +func (chain *TestChain) SendMsgs(senderAcc Account, expPass bool, msgs []sdk.Msg, opts ...SendMsgOption) (sdk.GasInfo, *sdk.Result, []abci.Event, error) { + var abciEvents []abci.Event + + t := chain.t + + gasInfo, res, err := chain.SendMsgsRaw(senderAcc, msgs, opts...) + if expPass { + require.NoError(t, err) + require.NotNil(t, res) + } else { + require.Error(t, err) + require.Nil(t, res) + } + if res != nil { + abciEvents = append(abciEvents, res.Events...) + } + + if !chain.buildSendMsgOptions(opts...).noBlockChange { + abciEvents = append(abciEvents, chain.EndBlock()...) + abciEvents = append(abciEvents, chain.BeginBlock()...) + } + + return gasInfo, res, abciEvents, err +} + +// SendMsgsRaw sends a series of messages. +func (chain *TestChain) SendMsgsRaw(senderAcc Account, msgs []sdk.Msg, opts ...SendMsgOption) (sdk.GasInfo, *sdk.Result, error) { + t := chain.t + options := chain.buildSendMsgOptions(opts...) + + // Get the sender account + senderAccI := chain.app.AccountKeeper.GetAccount(chain.GetContext(), senderAcc.Address) + require.NotNil(t, senderAccI) + + // Build and sign Tx + r := rand.New(rand.NewSource(time.Now().UnixNano())) + tx, err := genSignedMockTx( + r, + chain.txConfig, + msgs, + options.fees, + options.gasLimit, + chain.GetChainID(), + []uint64{senderAccI.GetAccountNumber()}, + []uint64{senderAccI.GetSequence()}, + []cryptoTypes.PrivKey{senderAcc.PrivKey}, + options, + ) + require.NoError(t, err) + + // Check the Tx + if options.simulate { + txBz, err := chain.txConfig.TxEncoder()(tx) + require.NoError(t, err) + + return chain.app.Simulate(txBz) + } + + // Send the Tx + return chain.app.SimDeliver(chain.txConfig.TxEncoder(), tx) +} + +// ParseSDKResultData converts TX result data into a slice of Msgs. +func (chain *TestChain) ParseSDKResultData(r *sdk.Result) sdk.TxMsgData { + t := chain.t + + require.NotNil(t, r) + + var protoResult sdk.TxMsgData + require.NoError(chain.t, proto.Unmarshal(r.Data, &protoResult)) + + return protoResult +} + +// GetDefaultTxFee returns the default transaction fee (that one is used if SendMsgs has no other options). +func (chain *TestChain) GetDefaultTxFee() sdk.Coins { + t := chain.t + + feeAmt, ok := sdk.NewIntFromString(chain.cfg.DefaultFeeAmt) + require.True(t, ok) + + return sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, feeAmt)) +} + +func (chain *TestChain) buildSendMsgOptions(opts ...SendMsgOption) sendMsgOptions { + options := sendMsgOptions{ + fees: chain.GetDefaultTxFee(), + gasLimit: 10_000_000, + noBlockChange: false, + } + + for _, o := range opts { + o(&options) + } + + return options +} + +func genSignedMockTx(r *rand.Rand, txConfig client.TxConfig, msgs []sdk.Msg, feeAmt sdk.Coins, gas uint64, chainID string, accNums, accSeqs []uint64, priv []cryptoTypes.PrivKey, opt sendMsgOptions) (sdk.Tx, error) { + sigs := make([]signing.SignatureV2, len(priv)) + + // create a random length memo + memo := simulation.RandStringOfLength(r, simulation.RandIntBetween(r, 0, 100)) + + signMode := txConfig.SignModeHandler().DefaultMode() + + // 1st round: set SignatureV2 with empty signatures, to set correct + // signer infos. + for i, p := range priv { + sigs[i] = signing.SignatureV2{ + PubKey: p.PubKey(), + Data: &signing.SingleSignatureData{ + SignMode: signMode, + }, + Sequence: accSeqs[i], + } + } + + tx := txConfig.NewTxBuilder() + err := tx.SetMsgs(msgs...) + if err != nil { + return nil, err + } + err = tx.SetSignatures(sigs...) + if err != nil { + return nil, err + } + tx.SetMemo(memo) + tx.SetFeeAmount(feeAmt) + tx.SetGasLimit(gas) + + if opt.granter != nil { + tx.SetFeeGranter(opt.granter) + } + + // 2nd round: once all signer infos are set, every signer can sign. + for i, p := range priv { + signerData := authsign.SignerData{ + Address: sdk.AccAddress(p.PubKey().Address()).String(), + ChainID: chainID, + AccountNumber: accNums[i], + Sequence: accSeqs[i], + PubKey: p.PubKey(), + } + signBytes, err := txConfig.SignModeHandler().GetSignBytes(signMode, signerData, tx.GetTx()) + if err != nil { + panic(err) + } + sig, err := p.Sign(signBytes) + if err != nil { + panic(err) + } + sigs[i].Data.(*signing.SingleSignatureData).Signature = sig + err = tx.SetSignatures(sigs...) + if err != nil { + panic(err) + } + } + + return tx.GetTx(), nil +} diff --git a/e2e/testing/chain_ops.go b/e2e/testing/chain_ops.go new file mode 100644 index 0000000..44c1150 --- /dev/null +++ b/e2e/testing/chain_ops.go @@ -0,0 +1,55 @@ +package e2eTesting + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + govTypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + "github.com/stretchr/testify/require" +) + +// ExecuteGovProposal submits a new proposal and votes for it. +func (chain *TestChain) ExecuteGovProposal(proposerAcc Account, expPass bool, proposals []sdk.Msg, title string, summary string, metadata string) { + t := chain.t + + // Get params + k := chain.app.GovKeeper + govDepositParams := k.GetDepositParams(chain.GetContext()) + govVotingParams := k.GetVotingParams(chain.GetContext()) + depositCoin := govDepositParams.MinDeposit + votingDur := govVotingParams.VotingPeriod + + // Submit proposal with min deposit to start the voting + msg, err := govTypes.NewMsgSubmitProposal(proposals, depositCoin, proposerAcc.Address.String(), metadata) + require.NoError(t, err) + + _, res, _, err := chain.SendMsgs(proposerAcc, true, []sdk.Msg{msg}) + require.NoError(t, err) + txRes := chain.ParseSDKResultData(res) + require.Len(t, txRes.MsgResponses, 1) + + var resp govTypes.MsgSubmitProposalResponse + require.NoError(t, resp.Unmarshal(txRes.MsgResponses[0].Value)) + proposalID := resp.ProposalId + + // Vote with all validators (delegators) + for i := 0; i < len(chain.valSet.Validators); i++ { + delegatorAcc := chain.GetAccount(i) + + msg := govTypes.NewMsgVote(delegatorAcc.Address, proposalID, govTypes.OptionYes, "metadata") + _, _, _, err = chain.SendMsgs(proposerAcc, true, []sdk.Msg{msg}) + require.NoError(t, err) + } + + // Wait for voting to end + chain.NextBlock(*votingDur) + chain.NextBlock(0) // for the Gov EndBlocker to work + + // Check if proposal was passed + proposal, ok := k.GetProposal(chain.GetContext(), proposalID) + require.True(t, ok) + + if expPass { + require.Equal(t, govTypes.StatusPassed.String(), proposal.Status.String()) + } else { + require.NotEqual(t, govTypes.StatusPassed.String(), proposal.Status.String()) + } +} diff --git a/e2e/testing/chain_ops_contract.go b/e2e/testing/chain_ops_contract.go new file mode 100644 index 0000000..f033728 --- /dev/null +++ b/e2e/testing/chain_ops_contract.go @@ -0,0 +1,124 @@ +package e2eTesting + +import ( + "encoding/json" + "io/ioutil" // nolint: staticcheck + + wasmdTypes "github.com/CosmWasm/wasmd/x/wasm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +// UploadContract uploads a contract and returns the codeID. +func (chain *TestChain) UploadContract(sender Account, wasmPath string, instantiatePerms wasmdTypes.AccessConfig) (codeID uint64) { + t := chain.t + + wasmBlob, err := ioutil.ReadFile(wasmPath) + require.NoError(t, err) + + txMsg := wasmdTypes.MsgStoreCode{ + Sender: sender.Address.String(), + WASMByteCode: wasmBlob, + InstantiatePermission: &instantiatePerms, + } + + _, res, _, err := chain.SendMsgs(sender, true, []sdk.Msg{&txMsg}) + require.NoError(t, err) + + txRes := chain.ParseSDKResultData(res) + require.Len(t, txRes.MsgResponses, 1) + + var resp wasmdTypes.MsgStoreCodeResponse + require.NoError(t, resp.Unmarshal(txRes.MsgResponses[0].Value)) + codeID = resp.CodeID + + return +} + +// InstantiateContract instantiates a contract and returns the contract address. +func (chain *TestChain) InstantiateContract(sender Account, codeID uint64, adminAddr, label string, funds sdk.Coins, msg json.Marshaler) (contractAddr sdk.AccAddress, instResp []byte) { + t := chain.t + + var msgBz []byte + if msg != nil { + bz, err := msg.MarshalJSON() + require.NoError(t, err) + msgBz = bz + } + + txMsg := wasmdTypes.MsgInstantiateContract{ + Sender: sender.Address.String(), + Admin: adminAddr, + CodeID: codeID, + Label: label, + Msg: msgBz, + Funds: funds, + } + + _, res, _, err := chain.SendMsgs(sender, true, []sdk.Msg{&txMsg}) + require.NoError(t, err) + + txRes := chain.ParseSDKResultData(res) + require.Len(t, txRes.MsgResponses, 1) + + var resp wasmdTypes.MsgInstantiateContractResponse + require.NoError(t, resp.Unmarshal(txRes.MsgResponses[0].Value)) + + contractAddr, err = sdk.AccAddressFromBech32(resp.Address) + require.NoError(t, err) + instResp = resp.Data + + return +} + +// SmartQueryContract queries a contract and returns the result. +func (chain *TestChain) SmartQueryContract(contractAddr sdk.AccAddress, expPass bool, msg json.Marshaler) ([]byte, error) { + t := chain.t + + require.NotNil(t, msg) + reqBz, err := msg.MarshalJSON() + require.NoError(t, err) + + resp, err := chain.app.WasmKeeper.QuerySmart(chain.GetContext(), contractAddr, reqBz) + if expPass { + require.NoError(t, err) + return resp, nil + } + require.Error(t, err) + + return nil, err +} + +// GetContractInfo returns a contract info. +func (chain *TestChain) GetContractInfo(contractAddr sdk.AccAddress) wasmdTypes.ContractInfo { + t := chain.t + + info := chain.app.WasmKeeper.GetContractInfo(chain.GetContext(), contractAddr) + require.NotNil(t, info) + + return *info +} + +// // GetContractMetadata returns a contract metadata. +// func (chain *TestChain) GetContractMetadata(contractAddr sdk.AccAddress) rewardsTypes.ContractMetadata { +// t := chain.t + +// metadata, err := chain.app.Keepers.RewardsKeeper.ContractMetadata.Get(chain.GetContext(), contractAddr) +// require.NoError(t, err) + +// return metadata +// } + +// // SetContractMetadata sets a contract metadata. +// func (chain *TestChain) SetContractMetadata(sender Account, contractAddr sdk.AccAddress, metadata rewardsTypes.ContractMetadata) { +// t := chain.t + +// metadata.ContractAddress = contractAddr.String() +// txMsg := rewardsTypes.MsgSetContractMetadata{ +// SenderAddress: sender.Address.String(), +// Metadata: metadata, +// } + +// _, _, _, err := chain.SendMsgs(sender, true, []sdk.Msg{&txMsg}) +// require.NoError(t, err) +// } diff --git a/e2e/testing/chain_ops_ibc.go b/e2e/testing/chain_ops_ibc.go new file mode 100644 index 0000000..acd02db --- /dev/null +++ b/e2e/testing/chain_ops_ibc.go @@ -0,0 +1,240 @@ +package e2eTesting + +import ( + "time" + + abci "github.com/tendermint/tendermint/abci/types" + tmHash "github.com/tendermint/tendermint/crypto/tmhash" + tmProto "github.com/tendermint/tendermint/proto/tendermint/types" + tmProtoVersion "github.com/tendermint/tendermint/proto/tendermint/version" + tmTypes "github.com/tendermint/tendermint/types" + tmVersion "github.com/tendermint/tendermint/version" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingTestUtil "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" + clientTypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" + channelTypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types" + commitmentTypes "github.com/cosmos/ibc-go/v6/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v6/modules/core/24-host" + "github.com/cosmos/ibc-go/v6/modules/core/exported" + ibcTmTypes "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint/types" + "github.com/stretchr/testify/require" +) + +// GetMerklePrefix returns a Merkle tree prefix. +// Used to send IBC ConnectionOpenInit msg. +func (chain *TestChain) GetMerklePrefix() commitmentTypes.MerklePrefix { + return commitmentTypes.NewMerklePrefix( + chain.app.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix().Bytes(), + ) +} + +// GetClientState returns an IBC client state for the provided clientID. +// Used to update an IBC TM client. +func (chain *TestChain) GetClientState(clientID string) exported.ClientState { + t := chain.t + + clientState, found := chain.app.IBCKeeper.ClientKeeper.GetClientState(chain.GetContext(), clientID) + require.True(t, found) + + return clientState +} + +// GetCurrentValSet returns a validator set for the current block height. +// Used to create an IBC TM client header. +func (chain *TestChain) GetCurrentValSet() tmTypes.ValidatorSet { + return *chain.valSet +} + +// GetValSetAtHeight returns a validator set for the specified block height. +// Used to create an IBC TM client header. +func (chain *TestChain) GetValSetAtHeight(height int64) tmTypes.ValidatorSet { + t := chain.t + + histInfo, ok := chain.app.StakingKeeper.GetHistoricalInfo(chain.GetContext(), height) + require.True(t, ok) + + validators := stakingTypes.Validators(histInfo.Valset) + tmValidators, err := stakingTestUtil.ToTmValidators(validators, sdk.DefaultPowerReduction) + require.NoError(t, err) + + valSet := tmTypes.NewValidatorSet(tmValidators) + + return *valSet +} + +// GetProofAtHeight returns the proto encoded merkle proof by key for the specified height. +// Used to establish an IBC connection. +func (chain *TestChain) GetProofAtHeight(key []byte, height uint64) ([]byte, clientTypes.Height) { + t := chain.t + + res := chain.app.Query(abci.RequestQuery{ + Path: "store/ibc/key", + Height: int64(height) - 1, + Data: key, + Prove: true, + }) + + merkleProof, err := commitmentTypes.ConvertProofs(res.ProofOps) + require.NoError(t, err) + + proof, err := chain.GetAppCodec().Marshal(&merkleProof) + require.NoError(t, err) + + revision := clientTypes.ParseChainID(chain.GetChainID()) + + // Proof height + 1 is returned as the proof created corresponds to the height the proof + // was created in the IAVL tree. Tendermint and subsequently the clients that rely on it + // have heights 1 above the IAVL tree. + + return proof, clientTypes.NewHeight(revision, uint64(res.Height)+1) +} + +// SendIBCPacket send an IBC packet and commits chain state. +func (chain *TestChain) SendIBCPacket(packet exported.PacketI) { + t := chain.t + + require.NotNil(t, packet) + + capPath := host.ChannelCapabilityPath(packet.GetSourcePort(), packet.GetSourceChannel()) + cap, ok := chain.app.ScopedIBCKeeper.GetCapability(chain.GetContext(), capPath) + require.True(t, ok) + + timeout := clientTypes.Height{ + RevisionNumber: packet.GetTimeoutHeight().GetRevisionNumber(), + RevisionHeight: packet.GetTimeoutHeight().GetRevisionHeight(), + } + _, err := chain.app.IBCKeeper.ChannelKeeper.SendPacket(chain.GetContext(), cap, packet.GetSourcePort(), packet.GetSourceChannel(), timeout, packet.GetTimeoutTimestamp(), packet.GetData()) + require.NoError(t, err) + + chain.NextBlock(0) +} + +// GetIBCPacketCommitment returns an IBC packet commitment hash (nil if not committed). +func (chain *TestChain) GetIBCPacketCommitment(packet channelTypes.Packet) []byte { + return chain.app.IBCKeeper.ChannelKeeper.GetPacketCommitment( + chain.GetContext(), + packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), + ) +} + +// GetNextIBCPacketSequence returns the next IBC packet sequence. +func (chain *TestChain) GetNextIBCPacketSequence(portID, channelID string) uint64 { + t := chain.t + + seq, ok := chain.app.IBCKeeper.ChannelKeeper.GetNextSequenceRecv(chain.GetContext(), portID, channelID) + require.True(t, ok) + + return seq +} + +// GetTMClientLastHeader creates an IBC TM client header from the last committed block. +// Used to create a new client. +func (chain *TestChain) GetTMClientLastHeader() ibcTmTypes.Header { + return chain.createTMClientHeader( + chain.GetChainID(), + chain.lastHeader.Height, + clientTypes.Height{}, + chain.lastHeader.Time, + chain.valSet, + nil, + chain.valSigners, + ) +} + +// GetTMClientHeaderUpdate creates an IBC TM client header to update an existing client on the source chain. +func (chain *TestChain) GetTMClientHeaderUpdate(counterpartyChain *TestChain, clientID string, blockHeightTrusted clientTypes.Height) ibcTmTypes.Header { + t := chain.t + + header := counterpartyChain.GetTMClientLastHeader() + + if blockHeightTrusted.IsZero() { + blockHeightTrusted = chain.GetClientState(clientID).GetLatestHeight().(clientTypes.Height) + } + + // Once we get TrustedHeight from client, we must query the validators from the counterparty chain + // If the LatestHeight == LastHeader.Height, then TrustedValidators are current validators + // If LatestHeight < LastHeader.Height, we can query the historical validator set from HistoricalInfo + var valSetTrusted tmTypes.ValidatorSet + if blockHeightTrusted == header.GetHeight() { + valSetTrusted = counterpartyChain.GetCurrentValSet() + } else { + // NOTE: We need to get validators from counterparty at height: trustedHeight+1 + // since the last trusted validators for a header at height h + // is the NextValidators at h+1 committed to in header h by + // NextValidatorsHash + valSetTrusted = counterpartyChain.GetValSetAtHeight(int64(blockHeightTrusted.RevisionHeight + 1)) + } + + // Update trusted fields assuming revision number is 0 + header.TrustedHeight = blockHeightTrusted + + valSetTrustedProto, err := valSetTrusted.ToProto() + require.NoError(t, err) + header.TrustedValidators = valSetTrustedProto + + return header +} + +// createTMClientHeader creates a valid TM client header. +func (chain *TestChain) createTMClientHeader(chainID string, blockHeight int64, blockHeightTrusted clientTypes.Height, blockTime time.Time, valSet, valSetTrusted *tmTypes.ValidatorSet, valSigners []tmTypes.PrivValidator) ibcTmTypes.Header { + t := chain.t + + require.NotNil(t, valSet) + + valSetHash := valSet.Hash() + + header := tmTypes.Header{ + Version: tmProtoVersion.Consensus{Block: tmVersion.BlockProtocol, App: 2}, + ChainID: chainID, + Height: blockHeight, + Time: blockTime, + LastBlockID: tmTypes.BlockID{ + Hash: make([]byte, tmHash.Size), + PartSetHeader: tmTypes.PartSetHeader{ + Total: 10_000, + Hash: make([]byte, tmHash.Size), + }, + }, + LastCommitHash: chain.app.LastCommitID().Hash, + DataHash: tmHash.Sum([]byte("data_hash")), + ValidatorsHash: valSetHash, + NextValidatorsHash: valSetHash, + ConsensusHash: tmHash.Sum([]byte("consensus_hash")), + AppHash: chain.lastHeader.AppHash, + LastResultsHash: tmHash.Sum([]byte("last_results_hash")), + EvidenceHash: tmHash.Sum([]byte("evidence_hash")), + ProposerAddress: valSet.Proposer.Address, + } + + blockID := tmTypes.BlockID{ + Hash: header.Hash(), + PartSetHeader: tmTypes.PartSetHeader{ + Total: 3, + Hash: tmHash.Sum([]byte("part_set")), + }, + } + voteSet := tmTypes.NewVoteSet(chainID, blockHeight, 1, tmProto.PrecommitType, valSet) + + commit, err := tmTypes.MakeCommit(blockID, blockHeight, 1, voteSet, valSigners, blockTime) + require.NoError(t, err) + + valSetProto, err := valSet.ToProto() + require.NoError(t, err) + + var valSetTrustedProto *tmProto.ValidatorSet + if valSetTrusted != nil { + valSetTrustedProto, err = valSetTrusted.ToProto() + require.NoError(t, err) + } + + return ibcTmTypes.Header{ + SignedHeader: &tmProto.SignedHeader{ + Header: header.ToProto(), + Commit: commit.ToProto(), + }, + ValidatorSet: valSetProto, + TrustedHeight: blockHeightTrusted, + TrustedValidators: valSetTrustedProto, + } +} diff --git a/e2e/testing/chain_options.go b/e2e/testing/chain_options.go new file mode 100644 index 0000000..1636680 --- /dev/null +++ b/e2e/testing/chain_options.go @@ -0,0 +1,108 @@ +package e2eTesting + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + mintTypes "github.com/cosmos/cosmos-sdk/x/mint/types" + "github.com/dymensionxyz/rollapp-wasm/app" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" +) + +// chainConfig is a TestChain config which can be adjusted using options. +type chainConfig struct { + ValidatorsNum int + GenAccountsNum int + GenBalanceAmount string + BondAmount string + LoggerEnabled bool + DefaultFeeAmt string + DummyTestAddr bool +} + +type ( + TestChainConfigOption func(cfg *chainConfig) + + TestChainGenesisOption func(cdc codec.Codec, genesis app.GenesisState) + + TestChainConsensusParamsOption func(params *tmproto.ConsensusParams) +) + +// defaultChainConfig builds chain default config. +func defaultChainConfig() chainConfig { + return chainConfig{ + ValidatorsNum: 1, + GenAccountsNum: 5, + GenBalanceAmount: sdk.DefaultPowerReduction.MulRaw(100).String(), + BondAmount: sdk.DefaultPowerReduction.MulRaw(1).String(), + DefaultFeeAmt: sdk.DefaultPowerReduction.QuoRaw(10).String(), // 0.1 + DummyTestAddr: false, + } +} + +func WithValidatorsNum(num int) TestChainConfigOption { + return func(cfg *chainConfig) { + cfg.ValidatorsNum = num + } +} + +func WithDummyTestAddress() TestChainConfigOption { + return func(cfg *chainConfig) { + cfg.DummyTestAddr = true + } +} + +// WithGenAccounts sets the number of genesis accounts +func WithGenAccounts(num int) TestChainConfigOption { + return func(cfg *chainConfig) { + cfg.GenAccountsNum = num + } +} + +// WithGenDefaultCoinBalance sets the genesis account balance for the default token (stake). +func WithGenDefaultCoinBalance(amount string) TestChainConfigOption { + return func(cfg *chainConfig) { + cfg.GenBalanceAmount = amount + } +} + +// WithDefaultFeeAmount sets the default fee amount which is used for sending Msgs with no fee specified. +func WithDefaultFeeAmount(amount string) TestChainConfigOption { + return func(cfg *chainConfig) { + cfg.DefaultFeeAmt = amount + } +} + +// WithBondAmount sets the amount of coins to bond for each validator. +func WithBondAmount(amount string) TestChainConfigOption { + return func(cfg *chainConfig) { + cfg.BondAmount = amount + } +} + +// WithLogger enables the app console logger. +func WithLogger() TestChainConfigOption { + return func(cfg *chainConfig) { + cfg.LoggerEnabled = true + } +} + +// WithBlockGasLimit sets the block gas limit (not set by default). +func WithBlockGasLimit(gasLimit int64) TestChainConsensusParamsOption { + return func(params *tmproto.ConsensusParams) { + params.Block.MaxGas = gasLimit + } +} + +// WithMintParams sets x/mint inflation calculation parameters. +func WithMintParams(inflationMin, inflationMax sdk.Dec, blocksPerYear uint64) TestChainGenesisOption { + return func(cdc codec.Codec, genesis app.GenesisState) { + var mintGenesis mintTypes.GenesisState + cdc.MustUnmarshalJSON(genesis[mintTypes.ModuleName], &mintGenesis) + + mintGenesis.Params.InflationMin = inflationMin + mintGenesis.Params.InflationMax = inflationMax + mintGenesis.Params.BlocksPerYear = blocksPerYear + + genesis[mintTypes.ModuleName] = cdc.MustMarshalJSON(&mintGenesis) + } +} diff --git a/e2e/testing/client.go b/e2e/testing/client.go new file mode 100644 index 0000000..373987e --- /dev/null +++ b/e2e/testing/client.go @@ -0,0 +1,44 @@ +package e2eTesting + +import ( + "context" + "fmt" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/codec" + "google.golang.org/grpc" + + "github.com/dymensionxyz/rollapp-wasm/app" +) + +var _ grpc.ClientConnInterface = (*grpcClient)(nil) + +type grpcClient struct { + app *app.App +} + +func (c grpcClient) Invoke(ctx context.Context, method string, args, reply interface{}, opts ...grpc.CallOption) error { + req := args.(codec.ProtoMarshaler) + resp := c.app.Query(abci.RequestQuery{ + Data: c.app.AppCodec().MustMarshal(req), + Path: method, + Height: 0, // TODO: heightened queries + Prove: false, + }) + + if resp.Code != abci.CodeTypeOK { + return fmt.Errorf(resp.Log) + } + + c.app.AppCodec().MustUnmarshal(resp.Value, reply.(codec.ProtoMarshaler)) + + return nil +} + +func (c grpcClient) NewStream(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) { + panic("not supported") +} + +func (chain *TestChain) Client() grpc.ClientConnInterface { + return grpcClient{app: chain.app} +} diff --git a/e2e/testing/common.go b/e2e/testing/common.go new file mode 100644 index 0000000..61ab936 --- /dev/null +++ b/e2e/testing/common.go @@ -0,0 +1,96 @@ +package e2eTesting + +import ( + "fmt" + "strconv" + "strings" + + wasmKeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// GetStringEventAttribute returns TX response event attribute string value by type and attribute key. +func GetStringEventAttribute(events []abci.Event, eventType, attrKey string) string { + for _, event := range events { + if event.Type != eventType { + continue + } + + for _, attr := range event.Attributes { + if string(attr.Key) != attrKey { + continue + } + + attrValue := string(attr.Value) + if valueUnquoted, err := strconv.Unquote(string(attr.Value)); err == nil { + attrValue = valueUnquoted + } + + return attrValue + } + } + + return "" +} + +// GenAccounts generates a list of accounts and private keys for them. +func GenAccounts(num uint) ([]sdk.AccAddress, []cryptotypes.PrivKey) { + addrs := make([]sdk.AccAddress, 0, num) + privKeys := make([]cryptotypes.PrivKey, 0, num) + + for i := 0; i < cap(addrs); i++ { + privKey := secp256k1.GenPrivKey() + + addrs = append(addrs, sdk.AccAddress(privKey.PubKey().Address())) + privKeys = append(privKeys, privKey) + } + + return addrs, privKeys +} + +// GenContractAddresses generates a list of contract addresses (codeID and instanceID are sequential). +func GenContractAddresses(num uint) []sdk.AccAddress { + addrs := make([]sdk.AccAddress, 0, num) + + for i := 0; i < cap(addrs); i++ { + contractAddr := wasmKeeper.BuildContractAddressClassic(uint64(i), uint64(i)) + addrs = append(addrs, contractAddr) + } + + return addrs +} + +// HumanizeCoins returns the sdk.Coins string representation with a number of decimals specified. +// 1123000stake -> 1.123stake with 6 decimals (3 numbers after the dot is hardcoded). +func HumanizeCoins(decimals uint8, coins ...sdk.Coin) string { + baseDec := sdk.NewDecWithPrec(1, int64(decimals)) + + strs := make([]string, 0, len(coins)) + for _, coin := range coins { + amtDec := sdk.NewDecFromInt(coin.Amount).Mul(baseDec) + amtFloat, _ := amtDec.Float64() + + strs = append(strs, fmt.Sprintf("%.03f%s", amtFloat, coin.Denom)) + } + + return strings.Join(strs, ",") +} + +// HumanizeDecCoins returns the sdk.DecCoins string representation. +// 1000.123456789stake -> 1.123456stake with 3 decimals (6 numbers after the dot is hardcoded). +func HumanizeDecCoins(decimals uint8, coins ...sdk.DecCoin) string { + baseDec := sdk.NewDecWithPrec(1, int64(decimals)) + + strs := make([]string, 0, len(coins)) + for _, coin := range coins { + amtDec := coin.Amount.Mul(baseDec) + amtFloat, _ := amtDec.Float64() + + strs = append(strs, fmt.Sprintf("%.06f%s", amtFloat, coin.Denom)) + } + + return strings.Join(strs, ",") +} diff --git a/e2e/testing/ibc_path.go b/e2e/testing/ibc_path.go new file mode 100644 index 0000000..25851d3 --- /dev/null +++ b/e2e/testing/ibc_path.go @@ -0,0 +1,581 @@ +package e2eTesting + +import ( + "bytes" + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + + clientTypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" + connectionTypes "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types" + channelTypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types" + commitmentTypes "github.com/cosmos/ibc-go/v6/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v6/modules/core/24-host" + "github.com/cosmos/ibc-go/v6/modules/core/exported" + + ibcTmTypes "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint/types" + "github.com/stretchr/testify/require" +) + +type ( + // IBCPath keeps IBC endpoints in sync and provides helper relayer functions. + // Heavily inspired by the Path from the ibc-go repo (https://github.com/cosmos/ibc-go/blob/main/testing/path.go). + // Reasons for no using the ibc-go's version are: simplify it, custom TestChain usage, hiding some public methods + // making it more usable without knowing how it works under the hood. + IBCPath struct { + t *testing.T + + a *IBCEndpoint + b *IBCEndpoint + } + + IBCEndpoint struct { + t *testing.T + + srcChain *TestChain + dstEndpoint *IBCEndpoint + + chPort string + chVersion string + chOrder channelTypes.Order + + clientID string + connectionID string + channelID string + } +) + +// NewIBCPath builds a new IBCPath relayer creating a connection between two IBC endpoints. +func NewIBCPath(t *testing.T, srcChain, dstChain *TestChain, srcChPort, dstChPort, chVersion string, chOrder channelTypes.Order) *IBCPath { + require.True(t, chOrder == channelTypes.ORDERED || chOrder == channelTypes.UNORDERED) + + p := IBCPath{ + t: t, + a: &IBCEndpoint{ + t: t, + srcChain: srcChain, + chPort: srcChPort, + chVersion: chVersion, + chOrder: chOrder, + }, + b: &IBCEndpoint{ + t: t, + srcChain: dstChain, + chPort: dstChPort, + chVersion: chVersion, + chOrder: chOrder, + }, + } + p.a.dstEndpoint, p.b.dstEndpoint = p.b, p.a + + // Skip a block to ensure lastHeader is set + p.a.srcChain.NextBlock(0) + p.b.srcChain.NextBlock(0) + + // Create clients + p.a.createIBCClient() + p.b.createIBCClient() + + // Create connection + p.a.sendConnectionOpenInit() + p.b.sendConnectionOpenTry() + p.a.sendConnectionOpenAck() + p.b.sendConnectionOpenConfirm() + p.a.updateIBCClient() + + // Create channel + p.a.sendChannelOpenInit() + p.b.sendChannelOpenTry() + p.a.sendChannelOpenAck() + p.b.sendChannelOpenConfirm() + p.a.updateIBCClient() + + return &p +} + +// EndpointA returns the IBC endpoint A. +func (p *IBCPath) EndpointA() *IBCEndpoint { + return p.a +} + +// EndpointB returns the IBC endpoint A. +func (p *IBCPath) EndpointB() *IBCEndpoint { + return p.b +} + +// RelayPacket attempts to relay a packet first on EndpointA and then on EndpointB. +// Packet must be committed. +func (p *IBCPath) RelayPacket(packet channelTypes.Packet, ack []byte) { + packetCommitmentRcvA := p.a.srcChain.GetIBCPacketCommitment(packet) + packetCommitmentExpA := channelTypes.CommitPacket(p.a.srcChain.GetAppCodec(), packet) + if bytes.Equal(packetCommitmentRcvA, packetCommitmentExpA) { + // Packet found, relay from A to B + p.b.updateIBCClient() + p.b.sendPacketReceive(packet) + p.a.sendPacketAck(packet, ack) + return + } + + packetCommitmentRcvB := p.b.srcChain.GetIBCPacketCommitment(packet) + packetCommitmentExpB := channelTypes.CommitPacket(p.b.srcChain.GetAppCodec(), packet) + if bytes.Equal(packetCommitmentRcvB, packetCommitmentExpB) { + // Packet found, relay from B to A + p.a.updateIBCClient() + p.a.sendPacketReceive(packet) + p.b.sendPacketAck(packet, ack) + return + } + + require.Fail(p.t, "packet commitment does not exist on either endpoint for provided packet") +} + +// TimeoutPacket timeouts a packet for the specified endpoint skipping block/time. +// Packet must be committed. +func (p *IBCPath) TimeoutPacket(packet channelTypes.Packet, endpoint *IBCEndpoint) { + t := p.t + + require.True(t, p.a == endpoint || p.b == endpoint) + + // Skip blocks + if targetBlock := int64(packet.TimeoutHeight.RevisionHeight); targetBlock > 0 { + curBlock := endpoint.srcChain.GetBlockHeight() + require.Greater(t, targetBlock, curBlock) + + p.SkipBlocks(targetBlock - curBlock) + } + + // Skip time + if targetTime := int64(packet.TimeoutTimestamp); targetTime > 0 { + curTime := endpoint.srcChain.GetBlockTime().UnixNano() + require.Greater(t, targetTime, curTime) + + p.SkipTime(endpoint, time.Duration(targetTime-curTime)) + } + + endpoint.sendPacketTimeout(packet) +} + +// SkipBlocks skips a number of blocks for both endpoints updating IBC clients. +func (p *IBCPath) SkipBlocks(n int64) { + for i := int64(0); i < n; i++ { + p.a.updateIBCClient() + p.b.updateIBCClient() + } +} + +// SkipTime skips a period for both endpoints updating IBC clients. +func (p *IBCPath) SkipTime(endpoint *IBCEndpoint, dur time.Duration) { + t := p.t + + require.True(t, p.a == endpoint || p.b == endpoint) + + startTime := endpoint.srcChain.GetBlockTime().UnixNano() + for { + endpoint.dstEndpoint.updateIBCClient() + endpoint.updateIBCClient() + + curTime := endpoint.srcChain.GetBlockTime().UnixNano() + if curTime > startTime+int64(dur) { + break + } + } +} + +// ChannelID returns the endpoint IBC channel ID. +func (e *IBCEndpoint) ChannelID() string { + return e.channelID +} + +// sendPacket sends an IBC packet through the channel keeper and updates the counterparty client. +// +// nolint: unused +func (e *IBCEndpoint) sendPacket(packet exported.PacketI) { + e.srcChain.SendIBCPacket(packet) + e.dstEndpoint.updateIBCClient() + e.updateIBCClient() +} + +// sendPacketReceive receives a packet on the source chain and updates the counterparty client. +func (e *IBCEndpoint) sendPacketReceive(packet channelTypes.Packet) { + srcChain, dstChain := e.srcChain, e.dstEndpoint.srcChain + + packetKey := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + proof, proofHeight := dstChain.GetProofAtHeight(packetKey, uint64(dstChain.GetBlockHeight())) + + srcChainSenderAcc := srcChain.GetAccount(0) + msg := channelTypes.NewMsgRecvPacket( + packet, + proof, + proofHeight, + srcChainSenderAcc.Address.String(), + ) + + _, _, _, err := srcChain.SendMsgs(srcChainSenderAcc, true, []sdk.Msg{msg}) + require.NoError(e.t, err) + + e.dstEndpoint.updateIBCClient() +} + +// sendPacketAck sends a MsgAcknowledgement to the channel associated with the source chain. +func (e *IBCEndpoint) sendPacketAck(packet channelTypes.Packet, ack []byte) { + srcChain, dstChain := e.srcChain, e.dstEndpoint.srcChain + + packetKey := host.PacketAcknowledgementKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + proof, proofHeight := dstChain.GetProofAtHeight(packetKey, uint64(dstChain.GetBlockHeight())) + + srcChainSenderAcc := srcChain.GetAccount(0) + msg := channelTypes.NewMsgAcknowledgement( + packet, + ack, + proof, + proofHeight, + srcChainSenderAcc.Address.String(), + ) + + _, _, _, err := srcChain.SendMsgs(srcChainSenderAcc, true, []sdk.Msg{msg}) + require.NoError(e.t, err) +} + +// sendPacketTimeout sends a MsgTimeout to the channel associated with the source chain. +func (e *IBCEndpoint) sendPacketTimeout(packet channelTypes.Packet) { + srcChain, dstChain := e.srcChain, e.dstEndpoint.srcChain + srcPortID, srcChannelID := e.chPort, e.channelID + + var packetKey []byte + switch e.chOrder { + case channelTypes.ORDERED: + packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) + case channelTypes.UNORDERED: + packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + } + + proof, proofHeight := dstChain.GetProofAtHeight(packetKey, uint64(dstChain.GetBlockHeight())) + nextSeqRecv := dstChain.GetNextIBCPacketSequence(srcPortID, srcChannelID) + + srcChainSenderAcc := srcChain.GetAccount(0) + msg := channelTypes.NewMsgTimeout( + packet, + nextSeqRecv, + proof, + proofHeight, + srcChainSenderAcc.Address.String(), + ) + + _, _, _, err := srcChain.SendMsgs(srcChainSenderAcc, true, []sdk.Msg{msg}) + require.NoError(e.t, err) +} + +// createIBCClient creates a new IBC TM client. +func (e *IBCEndpoint) createIBCClient() { + const ( + tmClientTrustPeriod = 7 * 24 * time.Hour + tmClientMaxClockDrift = 10 * time.Second + tmClientAllowUpdateAfterExpiry = false + tmClientAllowUpdateAfterMisbehaviour = false + ) + var ( + tmClientTrustLevel = ibcTmTypes.DefaultTrustLevel + tmClientUpgradePath = []string{"upgrade", "upgradedIBCState"} + ) + + t, srcChain, dstChain := e.t, e.srcChain, e.dstEndpoint.srcChain + + dstChainLastTMHeader := dstChain.GetTMClientLastHeader() + + clientState := ibcTmTypes.NewClientState( + dstChain.GetChainID(), + tmClientTrustLevel, + tmClientTrustPeriod, + dstChain.GetUnbondingTime(), + tmClientMaxClockDrift, + dstChainLastTMHeader.GetHeight().(clientTypes.Height), + commitmentTypes.GetSDKSpecs(), + tmClientUpgradePath, + tmClientAllowUpdateAfterExpiry, + tmClientAllowUpdateAfterMisbehaviour, + ) + + srcChainSenderAcc := srcChain.GetAccount(0) + msg, err := clientTypes.NewMsgCreateClient( + clientState, + dstChainLastTMHeader.ConsensusState(), + srcChainSenderAcc.Address.String(), + ) + require.NoError(t, err) + + _, res, _, err := srcChain.SendMsgs(srcChainSenderAcc, true, []sdk.Msg{msg}) + require.NoError(t, err) + + clientID := GetStringEventAttribute(res.Events, clientTypes.EventTypeCreateClient, clientTypes.AttributeKeyClientID) + require.NotEmpty(t, clientID) + e.clientID = clientID +} + +// updateIBCClient updates an IBC TM client. +func (e *IBCEndpoint) updateIBCClient() { + t, srcChain, dstChain := e.t, e.srcChain, e.dstEndpoint.srcChain + srcChainClientID := e.clientID + + dstChain.NextBlock(0) + + header := srcChain.GetTMClientHeaderUpdate(dstChain, srcChainClientID, clientTypes.ZeroHeight()) + + srcChainSenderAcc := srcChain.GetAccount(0) + msg, err := clientTypes.NewMsgUpdateClient( + e.clientID, + &header, + srcChainSenderAcc.Address.String(), + ) + require.NoError(t, err) + + _, _, _, err = srcChain.SendMsgs(srcChainSenderAcc, true, []sdk.Msg{msg}) + require.NoError(e.t, err) +} + +// sendConnectionOpenInit sends a ConnectionOpenInit message to the source chain. +func (e *IBCEndpoint) sendConnectionOpenInit() { + const ( + defDelayPeriod uint64 = 0 + ) + version := connectionTypes.ExportedVersionsToProto(connectionTypes.GetCompatibleVersions())[0] + + t, srcChain, dstChain := e.t, e.srcChain, e.dstEndpoint.srcChain + srcChainClientID, dstChainClientID := e.clientID, e.dstEndpoint.clientID + + srcChainSenderAcc := srcChain.GetAccount(0) + msg := connectionTypes.NewMsgConnectionOpenInit( + srcChainClientID, + dstChainClientID, + dstChain.GetMerklePrefix(), + version, + defDelayPeriod, + srcChainSenderAcc.Address.String(), + ) + + _, res, _, err := srcChain.SendMsgs(srcChainSenderAcc, true, []sdk.Msg{msg}) + require.NoError(t, err) + + e.connectionID = GetStringEventAttribute(res.Events, connectionTypes.EventTypeConnectionOpenInit, connectionTypes.AttributeKeyConnectionID) + require.NotEmpty(t, e.connectionID) +} + +// sendConnectionOpenTry sends a ConnectionOpenTry message to the source chain. +func (e *IBCEndpoint) sendConnectionOpenTry() { + const ( + defDelayPeriod uint64 = 0 + ) + version := connectionTypes.ExportedVersionsToProto(connectionTypes.GetCompatibleVersions())[0] + + t, srcChain, dstChain := e.t, e.srcChain, e.dstEndpoint.srcChain + srcChainClientID, dstChainClientID, srcChainConnectionID, dstChainConnectionID := e.clientID, e.dstEndpoint.clientID, e.connectionID, e.dstEndpoint.connectionID + + e.updateIBCClient() + + clientState, proofClient, proofConsensus, proofInit, consensusHeight, proofHeight := e.getConnectionHandshakeProof() + + srcChainSenderAcc := srcChain.GetAccount(0) + msg := connectionTypes.NewMsgConnectionOpenTry( + srcChainClientID, + dstChainConnectionID, + dstChainClientID, + clientState, + dstChain.GetMerklePrefix(), + []*connectionTypes.Version{version}, + defDelayPeriod, + proofInit, + proofClient, + proofConsensus, + proofHeight, + consensusHeight, + srcChainSenderAcc.Address.String(), + ) + + _, res, _, err := srcChain.SendMsgs(srcChainSenderAcc, true, []sdk.Msg{msg}) + require.NoError(t, err) + + if srcChainConnectionID == "" { + e.connectionID = GetStringEventAttribute(res.Events, connectionTypes.EventTypeConnectionOpenTry, connectionTypes.AttributeKeyConnectionID) + require.NotEmpty(t, e.connectionID) + } +} + +// sendConnectionOpenAck sends a ConnectionOpenAck message to the source chain. +func (e *IBCEndpoint) sendConnectionOpenAck() { + version := connectionTypes.ExportedVersionsToProto(connectionTypes.GetCompatibleVersions())[0] + + srcChain := e.srcChain + srcChainConnectionID, dstChainConnectionID := e.connectionID, e.dstEndpoint.connectionID + + e.updateIBCClient() + + clientState, proofClient, proofConsensus, proofTry, consensusHeight, proofHeight := e.getConnectionHandshakeProof() + + srcChainSenderAcc := srcChain.GetAccount(0) + msg := connectionTypes.NewMsgConnectionOpenAck( + srcChainConnectionID, + dstChainConnectionID, + clientState, + proofTry, + proofClient, + proofConsensus, + proofHeight, + consensusHeight, + version, + srcChainSenderAcc.Address.String(), + ) + + _, _, _, err := srcChain.SendMsgs(srcChainSenderAcc, true, []sdk.Msg{msg}) + require.NoError(e.t, err) +} + +// sendConnectionOpenConfirm sends a ConnectionOpenConfirm message to the source chain. +func (e *IBCEndpoint) sendConnectionOpenConfirm() { + srcChain, dstChain := e.srcChain, e.dstEndpoint.srcChain + srcChainConnectionID, dstChainConnectionID := e.connectionID, e.dstEndpoint.connectionID + + e.updateIBCClient() + + connectionKey := host.ConnectionKey(dstChainConnectionID) + proof, height := dstChain.GetProofAtHeight(connectionKey, uint64(dstChain.GetBlockHeight())) + + srcChainSenderAcc := srcChain.GetAccount(0) + msg := connectionTypes.NewMsgConnectionOpenConfirm( + srcChainConnectionID, + proof, + height, + srcChainSenderAcc.Address.String(), + ) + + _, _, _, err := srcChain.SendMsgs(srcChainSenderAcc, true, []sdk.Msg{msg}) + require.NoError(e.t, err) +} + +// sendChannelOpenInit sends a ChannelOpenInit message to the source chain. +func (e *IBCEndpoint) sendChannelOpenInit() { + t, srcChain, srcConnectionID := e.t, e.srcChain, e.connectionID + srcChPort, dstChPort, chVersion, chOrder := e.chPort, e.dstEndpoint.chPort, e.chVersion, e.chOrder + + srcChainSenderAcc := srcChain.GetAccount(0) + msg := channelTypes.NewMsgChannelOpenInit( + srcChPort, + chVersion, + chOrder, + []string{srcConnectionID}, + dstChPort, + srcChainSenderAcc.Address.String(), + ) + + _, res, _, err := srcChain.SendMsgs(srcChainSenderAcc, true, []sdk.Msg{msg}) + require.NoError(t, err) + + e.channelID = GetStringEventAttribute(res.Events, channelTypes.EventTypeChannelOpenInit, channelTypes.AttributeKeyChannelID) + require.NotEmpty(t, e.channelID) +} + +// sendChannelOpenTry sends a ChannelOpenTry message to the source chain. +func (e *IBCEndpoint) sendChannelOpenTry() { + e.updateIBCClient() + + t, srcChain, dstChain, srcConnectionID := e.t, e.srcChain, e.dstEndpoint.srcChain, e.connectionID + srcChPort, dstChPort, dstChannelID, chVersion, chOrder := e.chPort, e.dstEndpoint.chPort, e.dstEndpoint.channelID, e.chVersion, e.chOrder + + dstChannelKey := host.ChannelKey(dstChPort, dstChannelID) + proof, height := dstChain.GetProofAtHeight(dstChannelKey, uint64(dstChain.GetBlockHeight())) + + srcChainSenderAcc := srcChain.GetAccount(0) + msg := channelTypes.NewMsgChannelOpenTry( + srcChPort, + chVersion, + chOrder, + []string{srcConnectionID}, + dstChPort, + dstChannelID, + chVersion, + proof, + height, + srcChainSenderAcc.Address.String(), + ) + + _, res, _, err := srcChain.SendMsgs(srcChainSenderAcc, true, []sdk.Msg{msg}) + require.NoError(t, err) + + if e.channelID == "" { + e.channelID = GetStringEventAttribute(res.Events, channelTypes.EventTypeChannelOpenTry, channelTypes.AttributeKeyChannelID) + require.NotEmpty(t, e.channelID) + } +} + +// sendChannelOpenAck sends a ChannelOpenAck message to the source chain. +func (e *IBCEndpoint) sendChannelOpenAck() { + e.updateIBCClient() + + srcChain, dstChain := e.srcChain, e.dstEndpoint.srcChain + srcChPort, dstChPort, srcChannelID, dstChannelID, chVersion := e.chPort, e.dstEndpoint.chPort, e.channelID, e.dstEndpoint.channelID, e.chVersion + + dstChannelKey := host.ChannelKey(dstChPort, dstChannelID) + proof, height := dstChain.GetProofAtHeight(dstChannelKey, uint64(dstChain.GetBlockHeight())) + + srcChainSenderAcc := srcChain.GetAccount(0) + msg := channelTypes.NewMsgChannelOpenAck( + srcChPort, + srcChannelID, + dstChannelID, + chVersion, + proof, + height, + srcChainSenderAcc.Address.String(), + ) + + _, _, _, err := srcChain.SendMsgs(srcChainSenderAcc, true, []sdk.Msg{msg}) + require.NoError(e.t, err) +} + +// sendChannelOpenConfirm sends a ChannelOpenConfirm message to the source chain. +func (e *IBCEndpoint) sendChannelOpenConfirm() { + e.updateIBCClient() + + srcChain, dstChain := e.srcChain, e.dstEndpoint.srcChain + srcChPort, dstChPort, srcChannelID, dstChannelID := e.chPort, e.dstEndpoint.chPort, e.channelID, e.dstEndpoint.channelID + + dstChannelKey := host.ChannelKey(dstChPort, dstChannelID) + proof, height := dstChain.GetProofAtHeight(dstChannelKey, uint64(dstChain.GetBlockHeight())) + + srcChainSenderAcc := srcChain.GetAccount(0) + msg := channelTypes.NewMsgChannelOpenConfirm( + srcChPort, + srcChannelID, + proof, + height, + srcChainSenderAcc.Address.String(), + ) + + _, _, _, err := srcChain.SendMsgs(srcChainSenderAcc, true, []sdk.Msg{msg}) + require.NoError(e.t, err) +} + +// getConnectionHandshakeProof returns all the proofs necessary to execute OpenTry or OpenAck during the handshake +// from the counterparty chain. +func (e *IBCEndpoint) getConnectionHandshakeProof() ( + dstClientState exported.ClientState, + dstProofClient, dstProofConsensus, dstProofConnection []byte, + dstConsensusHeight, dstProofHeight clientTypes.Height, +) { + srcChain, dstChain, srcChainClientID, dstChainClientID, dstChainConnectionID := e.srcChain, e.dstEndpoint.srcChain, e.clientID, e.dstEndpoint.clientID, e.dstEndpoint.connectionID + + srcClientState := srcChain.GetClientState(srcChainClientID) + dstClientState = dstChain.GetClientState(dstChainClientID) + + srcClientKey := host.FullClientStateKey(srcChainClientID) + dstProofClient, dstProofHeight = dstChain.GetProofAtHeight(srcClientKey, srcClientState.GetLatestHeight().GetRevisionHeight()) + + dstConsensusHeight = dstClientState.GetLatestHeight().(clientTypes.Height) + + dstConsensusKey := host.FullConsensusStateKey(dstChainClientID, dstConsensusHeight) + dstProofConsensus, _ = dstChain.GetProofAtHeight(dstConsensusKey, dstProofHeight.GetRevisionHeight()) + + dstConnectionKey := host.ConnectionKey(dstChainConnectionID) + dstProofConnection, _ = dstChain.GetProofAtHeight(dstConnectionKey, dstProofHeight.GetRevisionHeight()) + + return +} diff --git a/e2e/testing/types.go b/e2e/testing/types.go new file mode 100644 index 0000000..58c6d94 --- /dev/null +++ b/e2e/testing/types.go @@ -0,0 +1,12 @@ +package e2eTesting + +import ( + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Account keeps a genesis account data. +type Account struct { + Address sdk.AccAddress + PrivKey cryptotypes.PrivKey +} diff --git a/go.mod b/go.mod index 0468249..97d7a1e 100644 --- a/go.mod +++ b/go.mod @@ -3,24 +3,36 @@ module github.com/dymensionxyz/rollapp-wasm go 1.22.4 require ( + cosmossdk.io/collections v0.4.0 + cosmossdk.io/core v0.10.0 cosmossdk.io/errors v1.0.1 github.com/CosmWasm/wasmd v0.33.0 + github.com/CosmWasm/wasmvm v1.3.0 github.com/bcdevtools/block-explorer-rpc-cosmos v1.2.3 github.com/bcdevtools/wasm-block-explorer-rpc-cosmos v1.1.1 + github.com/cosmos/cosmos-proto v1.0.0-beta.3 github.com/cosmos/cosmos-sdk v0.46.16 + github.com/cosmos/gogoproto v1.4.11 github.com/cosmos/ibc-go/v6 v6.3.0 + github.com/dvsekhvalnov/jose2go v1.5.0 github.com/dymensionxyz/dymension-rdk v1.6.1-0.20240708144829-21faaaf4ce25 github.com/dymensionxyz/dymint v1.1.3-rc04 github.com/ethereum/go-ethereum v1.12.0 github.com/evmos/evmos/v12 v12.1.6 + github.com/gogo/protobuf v1.3.3 + github.com/golang/protobuf v1.5.4 github.com/gorilla/mux v1.8.1 + github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.18.0 github.com/rakyll/statik v0.1.7 github.com/spf13/cast v1.5.1 github.com/spf13/cobra v1.8.0 + github.com/spf13/pflag v1.0.5 + github.com/stretchr/testify v1.9.0 github.com/tendermint/tendermint v0.34.29 github.com/tendermint/tm-db v0.6.8-0.20220506192307-f628bb5dc95b + google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 google.golang.org/grpc v1.64.0 ) @@ -31,13 +43,14 @@ require ( cloud.google.com/go/iam v1.1.6 // indirect cloud.google.com/go/storage v1.38.0 // indirect code.cloudfoundry.org/go-diodes v0.0.0-20220725190411-383eb6634c40 // indirect + cosmossdk.io/api v0.7.0 // indirect + cosmossdk.io/depinject v1.0.0-alpha.4 // indirect cosmossdk.io/math v1.3.0 // indirect filippo.io/edwards25519 v1.0.0-rc.1 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect - github.com/CosmWasm/wasmvm v1.3.0 // indirect - github.com/DataDog/zstd v1.5.2 // indirect + github.com/DataDog/zstd v1.5.5 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/VictoriaMetrics/fastcache v1.6.0 // indirect github.com/Workiva/go-datastructures v1.0.53 // indirect @@ -79,9 +92,8 @@ require ( github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect - github.com/cosmos/cosmos-proto v1.0.0-beta.3 // indirect + github.com/cosmos/cosmos-db v1.0.0 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect - github.com/cosmos/gogoproto v1.4.11 // indirect github.com/cosmos/gorocksdb v1.2.0 // indirect github.com/cosmos/iavl v0.19.6 // indirect github.com/cosmos/ledger-cosmos-go v0.12.4 // indirect @@ -103,7 +115,6 @@ require ( github.com/docker/distribution v2.8.2+incompatible // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1-0.20200219035652-afde56e7acac // indirect - github.com/dvsekhvalnov/jose2go v1.5.0 // indirect github.com/dymensionxyz/cosmosclient v0.4.2-beta // indirect github.com/dymensionxyz/dymension/v3 v3.1.0-rc03.0.20240411195658-f7cd96f53b56 // indirect github.com/dymensionxyz/gerr-cosmos v1.0.0 // indirect @@ -116,7 +127,7 @@ require ( github.com/francoispqt/gojay v1.2.13 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect - github.com/getsentry/sentry-go v0.18.0 // indirect + github.com/getsentry/sentry-go v0.23.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect @@ -129,10 +140,8 @@ require ( github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/gateway v1.1.0 // indirect - github.com/gogo/protobuf v1.3.3 // indirect github.com/golang/glog v1.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/btree v1.1.2 // indirect github.com/google/flatbuffers v2.0.8+incompatible // indirect @@ -149,7 +158,6 @@ require ( github.com/gorilla/rpc v1.2.0 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect - github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect @@ -263,10 +271,8 @@ require ( github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.9.3 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.15.0 // indirect github.com/status-im/keycard-go v0.2.0 // indirect - github.com/stretchr/testify v1.9.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/go-amino v0.16.0 // indirect @@ -311,7 +317,6 @@ require ( google.golang.org/api v0.169.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 3f43516..5b54023 100644 --- a/go.sum +++ b/go.sum @@ -195,6 +195,14 @@ cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoIS code.cloudfoundry.org/go-diodes v0.0.0-20220725190411-383eb6634c40 h1:wzkYwwcf4uMGcDpn48WAbq8GtoqDny49tdQ4zJVAsmo= code.cloudfoundry.org/go-diodes v0.0.0-20220725190411-383eb6634c40/go.mod h1:Nx9ASXN4nIlRDEXv+qXE3dpuhnTnO28Lxl/bMUd6BMc= collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= +cosmossdk.io/api v0.7.0 h1:QsEMIWuv9xWDbF2HZnW4Lpu1/SejCztPu0LQx7t6MN4= +cosmossdk.io/api v0.7.0/go.mod h1:kJFAEMLN57y0viszHDPLMmieF0471o5QAwwApa+270M= +cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= +cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= +cosmossdk.io/core v0.10.0 h1:NP28Ol9YyRODmZLJg2ko/mUl40hMegeMzhJnG+XPkcY= +cosmossdk.io/core v0.10.0/go.mod h1:MygXNld9DvMgYY4yE76DM/mdZpgfeyRjy6FPjEEehlY= +cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc= +cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE= @@ -230,8 +238,8 @@ github.com/CosmWasm/wasmvm v1.3.0/go.mod h1:vW/E3h8j9xBQs9bCoijDuawKo9kCtxOaS8N8 github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= -github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= @@ -453,6 +461,8 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= +github.com/cosmos/cosmos-db v1.0.0 h1:EVcQZ+qYag7W6uorBKFPvX6gRjw6Uq2hIh4hCWjuQ0E= +github.com/cosmos/cosmos-db v1.0.0/go.mod h1:iBvi1TtqaedwLdcrZVYRSSCb6eSy61NLj4UNmdIgs0U= github.com/cosmos/cosmos-proto v1.0.0-beta.3 h1:VitvZ1lPORTVxkmF2fAp3IiA61xVwArQYKXTdEcpW6o= github.com/cosmos/cosmos-proto v1.0.0-beta.3/go.mod h1:t8IASdLaAq+bbHbjq4p960BvcTqtwuAxid3b/2rOD6I= github.com/cosmos/cosmos-sdk v0.46.16 h1:RVGv1+RulLZeNyfCaPZrZtv0kY7ZZNAI6JGpub0Uh6o= @@ -632,8 +642,8 @@ github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqG github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= -github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= -github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= +github.com/getsentry/sentry-go v0.23.0 h1:dn+QRCeJv4pPt9OjVXiMcGIBIefaTJPw/h0bZWO05nE= +github.com/getsentry/sentry-go v0.23.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -2380,6 +2390,9 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= +gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -2395,8 +2408,8 @@ lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1 nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= -pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA= -pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= +pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= +pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= diff --git a/internal/collcompat/collcompat.go b/internal/collcompat/collcompat.go new file mode 100644 index 0000000..8473aef --- /dev/null +++ b/internal/collcompat/collcompat.go @@ -0,0 +1,113 @@ +package collcompat + +import ( + "context" + + collcodec "cosmossdk.io/collections/codec" + "cosmossdk.io/core/store" + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/gogo/protobuf/proto" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func NewKVStoreService(storeKey storetypes.StoreKey) store.KVStoreService { + return &kvStoreService{key: storeKey} +} + +type kvStoreService struct { + key storetypes.StoreKey +} + +func (k kvStoreService) OpenKVStore(ctx context.Context) store.KVStore { + return newKVStore(sdk.UnwrapSDKContext(ctx).KVStore(k.key)) +} + +// CoreKVStore is a wrapper of Core/Store kvstore interface +// Remove after https://github.com/cosmos/cosmos-sdk/issues/14714 is closed +type coreKVStore struct { + kvStore storetypes.KVStore +} + +// NewKVStore returns a wrapper of Core/Store kvstore interface +// Remove once store migrates to core/store kvstore interface +func newKVStore(store storetypes.KVStore) store.KVStore { + return coreKVStore{kvStore: store} +} + +// Get returns nil iff key doesn't exist. Errors on nil key. +func (store coreKVStore) Get(key []byte) ([]byte, error) { + return store.kvStore.Get(key), nil +} + +// Has checks if a key exists. Errors on nil key. +func (store coreKVStore) Has(key []byte) (bool, error) { + return store.kvStore.Has(key), nil +} + +// Set sets the key. Errors on nil key or value. +func (store coreKVStore) Set(key, value []byte) error { + store.kvStore.Set(key, value) + return nil +} + +// Delete deletes the key. Errors on nil key. +func (store coreKVStore) Delete(key []byte) error { + store.kvStore.Delete(key) + return nil +} + +// Iterator iterates over a domain of keys in ascending order. End is exclusive. +// Start must be less than end, or the Iterator is invalid. +// Iterator must be closed by caller. +// To iterate over entire domain, use store.Iterator(nil, nil) +// CONTRACT: No writes may happen within a domain while an iterator exists over it. +// Exceptionally allowed for cachekv.Store, safe to write in the modules. +func (store coreKVStore) Iterator(start, end []byte) (store.Iterator, error) { + return store.kvStore.Iterator(start, end), nil +} +func (store coreKVStore) ReverseIterator(start, end []byte) (store.Iterator, error) { + return store.kvStore.ReverseIterator(start, end), nil +} + +type protoMessage[T any] interface { + *T + codec.ProtoMarshaler +} + +// ProtoValue inits a collections.ValueCodec for a generic gogo protobuf message. +func ProtoValue[T any, PT protoMessage[T]](cdc codec.BinaryCodec) collcodec.ValueCodec[T] { + return &collValue[T, PT]{cdc.(codec.Codec), proto.MessageName(PT(new(T)))} +} + +type collValue[T any, PT protoMessage[T]] struct { + cdc codec.Codec + messageName string +} + +func (c collValue[T, PT]) Encode(value T) ([]byte, error) { + return c.cdc.Marshal(PT(&value)) +} + +func (c collValue[T, PT]) Decode(b []byte) (value T, err error) { + err = c.cdc.Unmarshal(b, PT(&value)) + return value, err +} + +func (c collValue[T, PT]) EncodeJSON(value T) ([]byte, error) { + return c.cdc.MarshalJSON(PT(&value)) +} + +func (c collValue[T, PT]) DecodeJSON(b []byte) (value T, err error) { + err = c.cdc.UnmarshalJSON(b, PT(&value)) + return +} + +func (c collValue[T, PT]) Stringify(value T) string { + return PT(&value).String() +} + +func (c collValue[T, PT]) ValueType() string { + return "github.com/cosmos/gogoproto/" + c.messageName +} diff --git a/pkg/basic_types.go b/pkg/basic_types.go new file mode 100644 index 0000000..5156f0c --- /dev/null +++ b/pkg/basic_types.go @@ -0,0 +1,6 @@ +package pkg + +// Uint64Ptr returns a pointer to the given uint64 value. +func Uint64Ptr(v uint64) *uint64 { + return &v +} diff --git a/pkg/cli_args.go b/pkg/cli_args.go new file mode 100644 index 0000000..3843e60 --- /dev/null +++ b/pkg/cli_args.go @@ -0,0 +1,47 @@ +package pkg + +import ( + "fmt" + "strconv" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// ParseAccAddressArg is a helper function to parse an account address CLI argument. +func ParseAccAddressArg(argName, argValue string) (sdk.AccAddress, error) { + addr, err := sdk.AccAddressFromBech32(argValue) + if err != nil { + return sdk.AccAddress{}, fmt.Errorf("parsing %s argument: invalid address: %w", argName, err) + } + + return addr, nil +} + +// ParseUint64Arg is a helper function to parse uint64 CLI argument. +func ParseUint64Arg(argName, argValue string) (uint64, error) { + v, err := strconv.ParseUint(argValue, 10, 64) + if err != nil { + return 0, fmt.Errorf("parsing %s argument: invalid uint64 value: %w", argName, err) + } + + return v, nil +} + +// ParseInt64Arg is a helper function to parse int64 CLI argument. +func ParseInt64Arg(argName, argValue string) (int64, error) { + v, err := strconv.ParseInt(argValue, 10, 64) + if err != nil { + return 0, fmt.Errorf("parsing %s argument: invalid uint64 value: %w", argName, err) + } + + return v, nil +} + +// ParseCoinArg is a helper function to parse uint64 CLI argument. +func ParseCoinArg(argName, argValue string) (sdk.Coin, error) { + deposit, err := sdk.ParseCoinNormalized(argValue) + if err != nil { + return deposit, fmt.Errorf("parsing %s argument: invalid sdk.Coin value: %w", argName, err) + } + return deposit, nil +} diff --git a/pkg/cli_flags.go b/pkg/cli_flags.go new file mode 100644 index 0000000..9661bae --- /dev/null +++ b/pkg/cli_flags.go @@ -0,0 +1,125 @@ +package pkg + +import ( + "fmt" + "strconv" + + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/dvsekhvalnov/jose2go/base64url" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +// ReadPageRequest reads and builds the necessary page request flags for pagination. +// This is fixed version of the client.ReadPageRequest function. +// The original version uses the "--page-key" flag as is, instead of base64 decoding it. +func ReadPageRequest(flagSet *pflag.FlagSet) (*query.PageRequest, error) { + pageKeyBz, _ := flagSet.GetString(flags.FlagPageKey) + offset, _ := flagSet.GetUint64(flags.FlagOffset) + limit, _ := flagSet.GetUint64(flags.FlagLimit) + countTotal, _ := flagSet.GetBool(flags.FlagCountTotal) + page, _ := flagSet.GetUint64(flags.FlagPage) + reverse, _ := flagSet.GetBool(flags.FlagReverse) + + if page > 1 && offset > 0 { + return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "page and offset cannot be used together") + } + + if page > 1 { + offset = (page - 1) * limit + } + + var pageKey []byte + if pageKeyBz != "" { + key, err := base64url.Decode(pageKeyBz) + if err != nil { + return nil, fmt.Errorf("parsing page key: %w", err) + } + pageKey = key + } + + return &query.PageRequest{ + Key: pageKey, + Offset: offset, + Limit: limit, + CountTotal: countTotal, + Reverse: reverse, + }, nil +} + +// ParseAccAddressFlag is a helper function to parse an account address CLI flag. +func ParseAccAddressFlag(cmd *cobra.Command, flagName string, isRequired bool) (*sdk.AccAddress, error) { + v, err := cmd.Flags().GetString(flagName) + if err != nil { + return nil, fmt.Errorf("parsing %s flag: %w", flagName, err) + } + + if v == "" { + if isRequired { + return nil, fmt.Errorf("parsing %s flag: value is required", flagName) + } + return nil, nil + } + + addr, err := sdk.AccAddressFromBech32(v) + if err != nil { + return nil, fmt.Errorf("parsing %s flag: invalid address: %w", flagName, err) + } + + return &addr, nil +} + +// GetUint64Flag is a helper function to get a uint64 CLI flag. +func GetUint64Flag(cmd *cobra.Command, flagName string, canBeEmpty bool) (uint64, error) { + v, err := cmd.Flags().GetUint64(flagName) + if err != nil { + return 0, fmt.Errorf("parsing %s flag: %w", flagName, err) + } + + if v == 0 && !canBeEmpty { + return 0, fmt.Errorf("parsing %s flag: value is required", flagName) + } + + return v, nil +} + +// GetStringSliceFlag is a helper function to get a slice of strings CLI flag. +func GetStringSliceFlag(cmd *cobra.Command, flagName string, canBeEmpty bool) ([]string, error) { + v, err := cmd.Flags().GetStringSlice(flagName) + if err != nil { + return nil, fmt.Errorf("parsing %s flag: %w", flagName, err) + } + + if len(v) == 0 && !canBeEmpty { + return nil, fmt.Errorf("parsing %s flag: can not be empty", flagName) + } + + return v, nil +} + +// GetUint64SliceFlag is a helper function to get a slice of uint64 CLI flag. +func GetUint64SliceFlag(cmd *cobra.Command, flagName string, canBeEmpty bool) ([]uint64, error) { + v, err := cmd.Flags().GetStringSlice(flagName) + if err != nil { + return nil, fmt.Errorf("parsing %s flag: %w", flagName, err) + } + + if len(v) == 0 && !canBeEmpty { + return nil, fmt.Errorf("parsing %s flag: can not be empty", flagName) + } + + var result []uint64 + for _, valueBz := range v { + value, err := strconv.ParseUint(valueBz, 10, 64) + if err != nil { + return nil, fmt.Errorf("parsing %s flag: invalid value (%s): %w", flagName, valueBz, err) + } + result = append(result, value) + } + + return result, nil +} diff --git a/pkg/coin.go b/pkg/coin.go new file mode 100644 index 0000000..cbe2dc7 --- /dev/null +++ b/pkg/coin.go @@ -0,0 +1,64 @@ +package pkg + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// CoinIsZero checks if sdk.Coin is set (not panics in case Amount is nil). +func CoinIsZero(coin sdk.Coin) bool { + if coin.Amount.IsNil() { + return true + } + + return coin.IsZero() +} + +// DecCoinIsZero checks if sdk.DecCoin is set (not panics in case Amount is nil). +func DecCoinIsZero(coin sdk.DecCoin) bool { + if coin.Amount.IsNil() { + return true + } + + return coin.IsZero() +} + +// DecCoinIsNegative checks if sdk.DecCoin is negative (not panics in case Amount is nil). +func DecCoinIsNegative(coin sdk.DecCoin) bool { + if coin.Amount.IsNil() { + return true + } + + return coin.IsNegative() +} + +// ValidateCoin performs a stricter validation of sdk.Coin comparing to the SDK version. +func ValidateCoin(coin sdk.Coin) error { + if err := sdk.ValidateDenom(coin.Denom); err != nil { + return fmt.Errorf("denom: %w", err) + } + if coin.Amount.IsNil() { + return fmt.Errorf("amount: nil") + } + if coin.IsNegative() { + return fmt.Errorf("amount: is negative") + } + + return nil +} + +// ValidateDecCoin performs a stricter validation of sdk.DecCoin comparing to the SDK version. +func ValidateDecCoin(coin sdk.DecCoin) error { + if err := sdk.ValidateDenom(coin.Denom); err != nil { + return fmt.Errorf("denom: %w", err) + } + if coin.Amount.IsNil() { + return fmt.Errorf("amount: nil") + } + if coin.IsNegative() { + return fmt.Errorf("amount: is negative") + } + + return nil +} diff --git a/pkg/coins.go b/pkg/coins.go new file mode 100644 index 0000000..98b22c4 --- /dev/null +++ b/pkg/coins.go @@ -0,0 +1,25 @@ +package pkg + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// SplitCoins splits coins in a proportion defined by the ratio. +// CONTRACT: inputs must be valid. +func SplitCoins(coins sdk.Coins, ratio sdk.Dec) (stack1, stack2 sdk.Coins) { + stack1 = sdk.NewCoins() + stack2 = sdk.NewCoins() + + for _, coin := range coins { + stack1Coin := sdk.Coin{ + Denom: coin.Denom, + Amount: sdk.NewDecFromInt(coin.Amount).Mul(ratio).TruncateInt(), + } + stack2Coin := coin.Sub(stack1Coin) + + stack1 = stack1.Add(stack1Coin) + stack2 = stack2.Add(stack2Coin) + } + + return +} diff --git a/pkg/coins_test.go b/pkg/coins_test.go new file mode 100644 index 0000000..3e88ae3 --- /dev/null +++ b/pkg/coins_test.go @@ -0,0 +1,79 @@ +package pkg + +import ( + "fmt" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSplitCoins(t *testing.T) { + type testCase struct { + coins string + ratio string + stack1Expected string + stack2Expected string + } + + testCases := []testCase{ + { + coins: "100uatom", + ratio: "0.5", + stack1Expected: "50uatom", + stack2Expected: "50uatom", + }, + { + coins: "100uatom", + ratio: "0.75", + stack1Expected: "75uatom", + stack2Expected: "25uatom", + }, + { + coins: "11uatom", + ratio: "0.50", + stack1Expected: "5uatom", + stack2Expected: "6uatom", + }, + { + coins: "13uatom,20ubtc", + ratio: "0.25", + stack1Expected: "3uatom,5ubtc", + stack2Expected: "10uatom,15ubtc", + }, + { + coins: "13uatom,20ubtc", + ratio: "1.0", + stack1Expected: "13uatom,20ubtc", + stack2Expected: "", + }, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("%s -> {%s , %s} with %s", tc.coins, tc.stack1Expected, tc.stack2Expected, tc.ratio), func(t *testing.T) { + coins, err := sdk.ParseCoinsNormalized(tc.coins) + require.NoError(t, err) + + ratio, err := sdk.NewDecFromStr(tc.ratio) + require.NoError(t, err) + + stack1Expected, err := sdk.ParseCoinsNormalized(tc.stack1Expected) + require.NoError(t, err) + + stack2Expected, err := sdk.ParseCoinsNormalized(tc.stack2Expected) + require.NoError(t, err) + + stack1Received, stack2Received := SplitCoins(coins, ratio) + if tc.stack1Expected == "" { + assert.True(t, stack1Received.Empty()) + } + if tc.stack2Expected == "" { + assert.True(t, stack2Received.Empty()) + } + + assert.ElementsMatch(t, stack1Expected, stack1Received) + assert.ElementsMatch(t, stack2Expected, stack2Received) + }) + } +} diff --git a/pkg/dec.go b/pkg/dec.go new file mode 100644 index 0000000..18e4540 --- /dev/null +++ b/pkg/dec.go @@ -0,0 +1,8 @@ +package pkg + +import sdk "github.com/cosmos/cosmos-sdk/types" + +// NewDecFromUint64 converts a uint64 value to the sdk.Dec. +func NewDecFromUint64(v uint64) sdk.Dec { + return sdk.NewDecFromInt(sdk.NewIntFromUint64(v)) +} diff --git a/pkg/openapiconsole/console.go b/pkg/openapiconsole/console.go new file mode 100644 index 0000000..6c430d9 --- /dev/null +++ b/pkg/openapiconsole/console.go @@ -0,0 +1,25 @@ +package openapiconsole + +import ( + "embed" + "html/template" + "net/http" +) + +//go:embed index.tpl +var index embed.FS + +// Handler returns an http handler that servers OpenAPI console for an OpenAPI spec at specURL. +func Handler(title, specURL string) http.HandlerFunc { + t, _ := template.ParseFS(index, "index.tpl") + + return func(w http.ResponseWriter, req *http.Request) { + _ = t.Execute(w, struct { + Title string + URL string + }{ + title, + specURL, + }) + } +} diff --git a/pkg/openapiconsole/index.tpl b/pkg/openapiconsole/index.tpl new file mode 100644 index 0000000..d88cd5b --- /dev/null +++ b/pkg/openapiconsole/index.tpl @@ -0,0 +1,25 @@ + + + + + {{ .Title }} + + + + +
+ + + + + \ No newline at end of file diff --git a/pkg/testutils/ante.go b/pkg/testutils/ante.go new file mode 100644 index 0000000..c5fd2ab --- /dev/null +++ b/pkg/testutils/ante.go @@ -0,0 +1,10 @@ +package testutils + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// NoopAnteHandler implements the no-op AnteHandler. +func NoopAnteHandler(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) { + return ctx, nil +} diff --git a/pkg/testutils/msg.go b/pkg/testutils/msg.go new file mode 100644 index 0000000..4409e84 --- /dev/null +++ b/pkg/testutils/msg.go @@ -0,0 +1,36 @@ +package testutils + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var _ sdk.Msg = (*MockMsg)(nil) + +// MockMsg is a dummy sdk.Msg. +type MockMsg struct{} + +// NewMockMsg creates a new MockMsg. +func NewMockMsg() *MockMsg { + return &MockMsg{} +} + +// Reset implements the proto.Message interface. +func (msg MockMsg) Reset() {} + +// String implements the proto.Message interface. +func (msg MockMsg) String() string { + return "" +} + +// ProtoMessage implements the proto.Message interface. +func (msg MockMsg) ProtoMessage() {} + +// ValidateBasic implements the sdk.Msg interface. +func (msg MockMsg) ValidateBasic() error { + return nil +} + +// GetSigners implements the sdk.Msg interface. +func (msg MockMsg) GetSigners() []sdk.AccAddress { + return nil +} diff --git a/pkg/testutils/tx.go b/pkg/testutils/tx.go new file mode 100644 index 0000000..7ac5dbc --- /dev/null +++ b/pkg/testutils/tx.go @@ -0,0 +1,87 @@ +package testutils + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var _ sdk.FeeTx = MockFeeTx{} + +// MockFeeTx is a mock implementation of sdk.FeeTx. +type MockFeeTx struct { + fees sdk.Coins + gas uint64 + msgs []sdk.Msg + feePayer sdk.AccAddress + feeGranter sdk.AccAddress +} + +type MockFeeTxOption func(tx *MockFeeTx) + +// WithMockFeeTxFees option sets the fees of the MockFeeTx. +func WithMockFeeTxFees(fees sdk.Coins) MockFeeTxOption { + return func(tx *MockFeeTx) { + tx.fees = fees + } +} + +// WithMockFeeTxMsgs option sets the msgs of the MockFeeTx. +func WithMockFeeTxMsgs(msgs ...sdk.Msg) MockFeeTxOption { + return func(tx *MockFeeTx) { + tx.msgs = msgs + } +} + +// WithMockFeeTxPayer option sets the feePayer of the MockFeeTx. +func WithMockFeeTxPayer(payer sdk.AccAddress) MockFeeTxOption { + return func(tx *MockFeeTx) { + tx.feePayer = payer + } +} + +// WithMockFeeTxGas option sets the gas limit of the MockFeeTx. +func WithMockFeeTxGas(gas uint64) MockFeeTxOption { + return func(tx *MockFeeTx) { + tx.gas = gas + } +} + +// NewMockFeeTx creates a new MockFeeTx instance. +// CONTRACT: tx has no defaults, so it is up to a developer to set options right. +func NewMockFeeTx(opts ...MockFeeTxOption) MockFeeTx { + tx := MockFeeTx{} + for _, opt := range opts { + opt(&tx) + } + + return tx +} + +// GetMsgs implemets the sdk.Tx interface. +func (tx MockFeeTx) GetMsgs() []sdk.Msg { + return tx.msgs +} + +// ValidateBasic implemets the sdk.Tx interface. +func (tx MockFeeTx) ValidateBasic() error { + return nil +} + +// GetGas implements the sdk.FeeTx interface. +func (tx MockFeeTx) GetGas() uint64 { + return tx.gas +} + +// GetFee implements the sdk.FeeTx interface. +func (tx MockFeeTx) GetFee() sdk.Coins { + return tx.fees +} + +// FeePayer implements the sdk.FeeTx interface. +func (tx MockFeeTx) FeePayer() sdk.AccAddress { + return tx.feePayer +} + +// FeeGranter implements the sdk.FeeTx interface. +func (tx MockFeeTx) FeeGranter() sdk.AccAddress { + return tx.feeGranter +} diff --git a/pkg/testutils/wasmd.go b/pkg/testutils/wasmd.go new file mode 100644 index 0000000..dd83dc5 --- /dev/null +++ b/pkg/testutils/wasmd.go @@ -0,0 +1,70 @@ +package testutils + +import ( + wasmKeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + wasmdTypes "github.com/CosmWasm/wasmd/x/wasm/types" + wasmVmTypes "github.com/CosmWasm/wasmvm/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var _ wasmKeeper.Messenger = (*MockMessenger)(nil) + +// MockContractViewer mocks x/wasmd module dependency. +// Mock returns a contract info if admin is set. +type MockContractViewer struct { + contractAdminSet map[string]string // key: contractAddr, value: adminAddr + returnSudoError error +} + +// NewMockContractViewer creates a new MockContractViewer instance. +func NewMockContractViewer() *MockContractViewer { + return &MockContractViewer{ + contractAdminSet: make(map[string]string), + returnSudoError: nil, + } +} + +// AddContractAdmin adds a contract admin link. +func (v *MockContractViewer) AddContractAdmin(contractAddr, adminAddr string) { + v.contractAdminSet[contractAddr] = adminAddr +} + +// GetContractInfo returns a contract info if admin is set. +func (v MockContractViewer) GetContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) *wasmdTypes.ContractInfo { + adminAddr, found := v.contractAdminSet[contractAddress.String()] + if !found { + return nil + } + + return &wasmdTypes.ContractInfo{ + Admin: adminAddr, + } +} + +// HasContractInfo returns true if admin is set. +func (v MockContractViewer) HasContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) bool { + _, found := v.contractAdminSet[contractAddress.String()] + return found +} + +// Sudo implements the wasmKeeper.ContractInfoViewer interface. +func (v MockContractViewer) Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error) { + return nil, v.returnSudoError +} + +func (v *MockContractViewer) SetReturnSudoError(returnSudoError error) { + v.returnSudoError = returnSudoError +} + +// MockMessenger mocks x/wasmd module dependency. +type MockMessenger struct{} + +// NewMockMessenger creates a new MockMessenger instance. +func NewMockMessenger() *MockMessenger { + return &MockMessenger{} +} + +// DispatchMsg implements the wasmKeeper.Messenger interface. +func (m MockMessenger) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmVmTypes.CosmosMsg) ([]sdk.Event, [][]byte, error) { + return nil, nil, nil +} diff --git a/pkg/utils.go b/pkg/utils.go new file mode 100644 index 0000000..14d81ec --- /dev/null +++ b/pkg/utils.go @@ -0,0 +1,49 @@ +package pkg + +import ( + "fmt" + "os" + + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// ExecuteWithGasLimit executes a function with a gas limit. Taken from: https://github.com/cosmos/cosmos-sdk/pull/18475 +func ExecuteWithGasLimit(ctx sdk.Context, gasLimit uint64, f func(ctx sdk.Context) error) (gasUsed uint64, err error) { + branchedCtx, commit := ctx.CacheContext() + // create a new gas meter + limitedGasMeter := storetypes.NewGasMeter(gasLimit) + // apply gas meter with limit to branched context + branchedCtx = branchedCtx.WithGasMeter(limitedGasMeter) + err = catchOutOfGas(branchedCtx, f) + // even before checking the error, we want to get the gas used + // and apply it to the original context. + gasUsed = limitedGasMeter.GasConsumed() + ctx.GasMeter().ConsumeGas(gasUsed, "branch") + // in case of errors, do not commit the branched context + // return gas used and the error + if err != nil { + return gasUsed, err + } + // if no error, commit the branched context + // and return gas used and no error + commit() + return gasUsed, nil +} + +// catchOutOfGas is a helper function to catch out of gas panics and return them as errors. +func catchOutOfGas(ctx sdk.Context, f func(ctx sdk.Context) error) (err error) { + defer func() { + if r := recover(); r != nil { + // we immediately check if it's an out of error gas. + // if it is not we panic again to propagate it up. + if _, ok := r.(storetypes.ErrorOutOfGas); !ok { + _, _ = fmt.Fprintf(os.Stderr, "recovered: %#v", r) // log to stderr + panic(r) + } + err = sdkerrors.ErrOutOfGas + } + }() + return f(ctx) +} diff --git a/proto/rollapp/callback/v1/callback.proto b/proto/rollapp/callback/v1/callback.proto new file mode 100644 index 0000000..6c52d01 --- /dev/null +++ b/proto/rollapp/callback/v1/callback.proto @@ -0,0 +1,53 @@ +syntax = "proto3"; +package rollapp.callback.v1; + +import "google/protobuf/any.proto"; +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos_proto/cosmos.proto"; + +option go_package = "github.com/dymensionxyz/rollapp-wasm/x/callback/types"; + +// Callback defines the callback structure. +message Callback { + // contract_address is the address of the contract which is requesting the callback (bech32 encoded). + string contract_address = 1; + // job_id is an identifier the callback requestor can pass in to identify the callback when it happens. + uint64 job_id = 2; + // callback_height is the height at which the callback is executed. + int64 callback_height = 3; + // fee_split is the breakdown of the fees paid by the contract to reserve the callback + CallbackFeesFeeSplit fee_split = 4; + // reserved_by is the address which reserved the callback (bech32 encoded). + string reserved_by = 5; + // callback_gas_limit is the maximum gas that can be consumed by this callback. + uint64 max_gas_limit = 6; +} + +// CallbackFeesFeeSplit is the breakdown of all the fees that need to be paid by the contract to reserve a callback +message CallbackFeesFeeSplit { + // transaction_fees is the transaction fees for the callback based on its gas consumption + cosmos.base.v1beta1.Coin transaction_fees = 1; + // block_reservation_fees is the block reservation fees portion of the callback reservation fees + cosmos.base.v1beta1.Coin block_reservation_fees = 2; + // future_reservation_fees is the future reservation fees portion of the callback reservation fees + cosmos.base.v1beta1.Coin future_reservation_fees = 3; + // surplus_fees is any extra fees passed in for the registration of the callback + cosmos.base.v1beta1.Coin surplus_fees = 4; + } + +// Params defines the module parameters. +message Params { + // callback_gas_limit is the maximum gas that can be consumed by a callback. + uint64 callback_gas_limit = 1; + // max_block_reservation_limit is the maximum number of callbacks which can be registered in a given block. + uint64 max_block_reservation_limit = 2; + // max_future_reservation_limit is the maximum number of blocks in the future that a contract can request a callback in. + uint64 max_future_reservation_limit = 3; + // block_reservation_fee_multiplier is used to calculate a part of the reservation fees which will need to be paid when requesting the callback. + string block_reservation_fee_multiplier = 4 [(cosmos_proto.scalar) = "cosmos.Dec", (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; + // future_reservation_fee_multiplier is used to calculate a part of the reservation fees which will need to be paid while requesting the callback. + string future_reservation_fee_multiplier = 5 [(cosmos_proto.scalar) = "cosmos.Dec", (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; + // min_price_of_gas defines the minimum price for each single unit of gas in the network. + cosmos.base.v1beta1.Coin min_price_of_gas = 6 [ (gogoproto.nullable) = false ]; +} \ No newline at end of file diff --git a/proto/rollapp/callback/v1/errors.proto b/proto/rollapp/callback/v1/errors.proto new file mode 100644 index 0000000..278a75a --- /dev/null +++ b/proto/rollapp/callback/v1/errors.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +package rollapp.callback.v1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/dymensionxyz/rollapp-wasm/x/callback/types"; + +// ModuleErrors defines the module level error codes +enum ModuleErrors { + // ERR_UNKNOWN is the default error code + ERR_UNKNOWN = 0; + // ERR_OUT_OF_GAS is the error code when the contract callback exceeds the gas limit allowed by the module + ERR_OUT_OF_GAS = 1; + // ERR_CONTRACT_EXECUTION_FAILED is the error code when the contract callback execution fails + ERR_CONTRACT_EXECUTION_FAILED = 2; +} \ No newline at end of file diff --git a/proto/rollapp/callback/v1/events.proto b/proto/rollapp/callback/v1/events.proto new file mode 100644 index 0000000..94316c4 --- /dev/null +++ b/proto/rollapp/callback/v1/events.proto @@ -0,0 +1,63 @@ +syntax = "proto3"; +package rollapp.callback.v1; + +option go_package = "github.com/dymensionxyz/rollapp-wasm/x/callback/types"; + +import "rollapp/callback/v1/callback.proto"; +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos_proto/cosmos.proto"; + +// CallbackRegisteredEvent is emitted when a callback is registered. +message CallbackRegisteredEvent { + // contract_address is the address of the contract for which callback is being registered (bech32 encoded). + string contract_address = 1; + // job_id is an identifier of the callback. + uint64 job_id = 2; + // callback_height is the height at which the callback is executed. + int64 callback_height = 3; + // fee_split is the breakdown of the fees paid by the contract to reserve the callback + CallbackFeesFeeSplit fee_split = 4; + // reserved_by is the address which reserved the callback (bech32 encoded). + string reserved_by = 5; +} + +// CallbackCancelledEvent is emitted when a callback is cancelled. +message CallbackCancelledEvent { + // cancelled_by is the address of the contract whose callback is being cancelled (bech32 encoded) + string cancelled_by = 1; + // contract_address is the address of the contract (bech32 encoded) + string contract_address = 2; + // job_id is an identifier the callback requestor had passed during registration of the callback + uint64 job_id = 3; + // callback_height is the height at which the callback requestor had registered the callback + int64 callback_height = 4; + // refund_amount is the amount of fees which was refunded on cancellation + cosmos.base.v1beta1.Coin refund_amount = 5 [ (gogoproto.nullable) = false ]; +} + +// CallbackExecutedSuccessEvent is emitted when a callback is executed successfully. +message CallbackExecutedSuccessEvent { + // contract_address is the address of the contract for which callback is being executed (bech32 encoded). + string contract_address = 1; + // job_id is an identifier of the callback. + uint64 job_id = 2; + // sudo_msg is the input passed by the module to the contract + string sudo_msg = 3; + // gas_used is the amount of gas consumed during the callback execution + uint64 gas_used = 4; +} + +// CallbackExecutedFailedEvent is emitted when a callback execution fails. +message CallbackExecutedFailedEvent { + // contract_address is the address of the contract for which callback is being executed (bech32 encoded). + string contract_address = 1; + // job_id is an identifier of the callback. + uint64 job_id = 2; + // sudo_msg is the input passed by the module to the contract + string sudo_msg = 3; + // gas_used is the amount of gas consumed during the callback execution + uint64 gas_used = 4; + // error is the error returned during the callback execution + string error = 5; +} diff --git a/proto/rollapp/callback/v1/genesis.proto b/proto/rollapp/callback/v1/genesis.proto new file mode 100644 index 0000000..c93292f --- /dev/null +++ b/proto/rollapp/callback/v1/genesis.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +package rollapp.callback.v1; + +option go_package = "github.com/dymensionxyz/rollapp-wasm/x/callback/types"; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "rollapp/callback/v1/callback.proto"; + +// GenesisState defines the initial state of the callback module. +message GenesisState { + // params defines all the module parameters. + Params params = 1 [ (gogoproto.nullable) = false ]; + // callbacks defines all the callbacks which are yet to be executed + repeated Callback callbacks = 2; +} diff --git a/proto/rollapp/callback/v1/query.proto b/proto/rollapp/callback/v1/query.proto new file mode 100644 index 0000000..8d7c580 --- /dev/null +++ b/proto/rollapp/callback/v1/query.proto @@ -0,0 +1,60 @@ +syntax = "proto3"; +package rollapp.callback.v1; + +option go_package = "github.com/dymensionxyz/rollapp-wasm/x/callback/types"; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "rollapp/callback/v1/callback.proto"; + +// Query service for the callback module. +service Query { + // Params returns module parameters + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/rollapp/callback/v1/params"; + } + // EstimateCallbackFees returns the total amount of callback fees a contract needs to pay to register the callback + rpc EstimateCallbackFees(QueryEstimateCallbackFeesRequest) returns (QueryEstimateCallbackFeesResponse) { + option (google.api.http).get = "/rollapp/callback/v1/estimate_callback_fees"; + } + // Callbacks returns all the callbacks registered at a given height + rpc Callbacks(QueryCallbacksRequest) returns (QueryCallbacksResponse) { + option (google.api.http).get = "/rollapp/callback/v1/callbacks"; + } +} + +// QueryParamsRequest is the request for Query.Params. +message QueryParamsRequest {} + +// QueryParamsResponse is the response for Query.Params. +message QueryParamsResponse { + // params defines all the module parameters. + Params params = 1 [ (gogoproto.nullable) = false ]; +} + +// QueryEstimateCallbackFeesRequest is the request for Query.EstimateCallbackFees. +message QueryEstimateCallbackFeesRequest{ + // block_height is the height at which to estimate the callback fees + int64 block_height = 1; +} + +// QueryEstimateCallbackFeesResponse is the response for Query.EstimateCallbackFees. +message QueryEstimateCallbackFeesResponse{ + // total_fees is the total fees that needs to be paid by the contract to reserve a callback + cosmos.base.v1beta1.Coin total_fees = 1; + // fee_split is the breakdown of the total_fees + CallbackFeesFeeSplit fee_split = 2; +} + +// QueryCallbacksRequest is the request for Query.Callbacks. +message QueryCallbacksRequest{ + // block_height is the height at which to query the callbacks + int64 block_height = 1; +} + +// QueryCallbacksResponse is the response for Query.Callbacks. +message QueryCallbacksResponse{ + // callbacks is the list of callbacks registered at the given height + repeated Callback callbacks = 1; +} \ No newline at end of file diff --git a/proto/rollapp/callback/v1/tx.proto b/proto/rollapp/callback/v1/tx.proto new file mode 100644 index 0000000..c80b82b --- /dev/null +++ b/proto/rollapp/callback/v1/tx.proto @@ -0,0 +1,57 @@ +syntax = "proto3"; +package rollapp.callback.v1; + +option go_package = "github.com/dymensionxyz/rollapp-wasm/x/callback/types"; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/msg/v1/msg.proto"; +import "rollapp/callback/v1/callback.proto"; + +// Msg defines the module messaging service. +service Msg { + // RequestCallback defines a message for registering a callback at a specific height by a given contract + rpc RequestCallback(MsgRequestCallback) returns (MsgRequestCallbackResponse); + + // CancelCallback defines a message for cancelling an existing callback + rpc CancelCallback(MsgCancelCallback) returns (MsgCancelCallbackResponse); +} + +// MsgRequestCallback is the Msg/RequestCallback request type. +message MsgRequestCallback { + option (cosmos.msg.v1.signer) = "sender"; + // sender is the address who is requesting the callback (bech32 encoded) + string sender = 1; + // contract_address is the address of the contract which is requesting the callback (bech32 encoded) + string contract_address = 2; + // job_id is an identifier the callback requestor can pass in to identify the callback when it happens + uint64 job_id = 3; + // callback_height is the height at which the callback is executed. + int64 callback_height = 4; + // fees is the amount of fees being paid to register the contract + cosmos.base.v1beta1.Coin fees = 5 [ (gogoproto.nullable) = false ]; +} + + +// MsgRequestCallbackResponse defines the response structure for executing a MsgRequestCallback message. +message MsgRequestCallbackResponse {} + +// MsgCancelCallback is the Msg/CancelCallback request type. +message MsgCancelCallback{ + option (cosmos.msg.v1.signer) = "sender"; + // sender is the address of the contract which is cancelling the callback (bech32 encoded) + string sender = 1; + // contract_address is the address of the contract (bech32 encoded) + string contract_address = 2; + // job_id is an identifier the callback requestor had passed during registration of the callback + uint64 job_id = 3; + // callback_height is the height at which the callback requestor had registered the callback + int64 callback_height = 4; +} + + +// MsgCancelCallbackResponse defines the response structure for executing a MsgCancelCallback message. +message MsgCancelCallbackResponse { + // refund is the amount of fees being refunded due to the cancellation of the callback + cosmos.base.v1beta1.Coin refund = 1 [ (gogoproto.nullable) = false ]; +} \ No newline at end of file diff --git a/proto/rollapp/cwerrors/v1/cwerrors.proto b/proto/rollapp/cwerrors/v1/cwerrors.proto new file mode 100644 index 0000000..cca3c56 --- /dev/null +++ b/proto/rollapp/cwerrors/v1/cwerrors.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; +package rollapp.cwerrors.v1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types"; + +// SudoError defines the sudo message for the error callback +message SudoError { + // module_name is the name of the module throwing the error + string module_name = 1; + // error_code is the module level error code + int32 error_code = 2; + // contract_address is the address of the contract which will receive the + // error callback + string contract_address = 3; + // input_payload is any input which caused the error + string input_payload = 4; + // error_message is the error message + string error_message = 5; +} + +// ModuleErrors defines the module level error codes +enum ModuleErrors { + // ERR_UNKNOWN is the default error code + ERR_UNKNOWN = 0; + // ERR_CALLBACK_EXECUTION_FAILED is the error code for when the error callback fails + ERR_CALLBACK_EXECUTION_FAILED = 1; +} \ No newline at end of file diff --git a/proto/rollapp/cwerrors/v1/events.proto b/proto/rollapp/cwerrors/v1/events.proto new file mode 100644 index 0000000..5f92365 --- /dev/null +++ b/proto/rollapp/cwerrors/v1/events.proto @@ -0,0 +1,49 @@ +syntax = "proto3"; +package rollapp.cwerrors.v1; + +import "gogoproto/gogo.proto"; +import "rollapp/cwerrors/v1/params.proto"; +import "rollapp/cwerrors/v1/cwerrors.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types"; + +// ParamsUpdatedEvent defines the event which is thrown when the module +// parameters are updated +message ParamsUpdatedEvent { + // new_params are the new parameters for the module + Params new_params = 1 [ (gogoproto.nullable) = false ]; +} + +// SubscribedToErrorsEvent defines the event which is thrown when a contract +// subscribes to errors +message SubscribedToErrorsEvent { + // sender is the address which initiated the subscription + string sender = 1; + // contract_address is the address of the contract which is subscribed to + // errors + string contract_address = 2; + // fees_paid is the fees paid for the subscription + cosmos.base.v1beta1.Coin fees_paid = 3 [ (gogoproto.nullable) = false ]; + // subscription_valid_till is the block height till which the subscription is + // valid + int64 subscription_valid_till = 4; +} + +// StoringErrorEvent defines the event which is thrown when an error is stored +message StoringErrorEvent { + // error is the error which is stored + SudoError error = 1 [ (gogoproto.nullable) = false ]; + // deletion_block_height is the block height at which the error will be pruned + // from the state + int64 deletion_block_height = 2; +} + +// SudoErrorCallbackFailedEvent defines the event which is thrown when a sudo +// error callback fails +message SudoErrorCallbackFailedEvent { + // error is the error for which the callback is executed + SudoError error = 1 [ (gogoproto.nullable) = false ]; + // callback_error_message is the error message of why the callback failed + string callback_error_message = 2; +} \ No newline at end of file diff --git a/proto/rollapp/cwerrors/v1/genesis.proto b/proto/rollapp/cwerrors/v1/genesis.proto new file mode 100644 index 0000000..1aecaf2 --- /dev/null +++ b/proto/rollapp/cwerrors/v1/genesis.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +package rollapp.cwerrors.v1; + +import "gogoproto/gogo.proto"; +import "rollapp/cwerrors/v1/params.proto"; +import "rollapp/cwerrors/v1/cwerrors.proto"; + +option go_package = "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types"; + +// GenesisState defines the cwerrors module's genesis state. +message GenesisState { + // params defines all the module parameters. + Params params = 1 [ (gogoproto.nullable) = false ]; + // errors defines all the sudo errors currently registered. + repeated SudoError errors = 2 [ (gogoproto.nullable) = false ]; +} \ No newline at end of file diff --git a/proto/rollapp/cwerrors/v1/params.proto b/proto/rollapp/cwerrors/v1/params.proto new file mode 100644 index 0000000..813123b --- /dev/null +++ b/proto/rollapp/cwerrors/v1/params.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; +package rollapp.cwerrors.v1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types"; + +// Params defines the set of parameters for the cwerrors module. +message Params { + // error_stored_time is the block height until which error is stored + int64 error_stored_time = 1; + // subsciption_fee is the fee required to subscribe to error callbacks + cosmos.base.v1beta1.Coin subscription_fee = 2 + [ (gogoproto.nullable) = false ]; + // subscription_period is the period for which the subscription is valid + int64 subscription_period = 3; +} \ No newline at end of file diff --git a/proto/rollapp/cwerrors/v1/query.proto b/proto/rollapp/cwerrors/v1/query.proto new file mode 100644 index 0000000..501df61 --- /dev/null +++ b/proto/rollapp/cwerrors/v1/query.proto @@ -0,0 +1,65 @@ +syntax = "proto3"; +package rollapp.cwerrors.v1; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "rollapp/cwerrors/v1/cwerrors.proto"; +import "rollapp/cwerrors/v1/params.proto"; + +option go_package = "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types"; + +// Query service for the cwerrors module. +service Query { + // Params queries all the module parameters. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/rollapp/cwerrors/v1/params"; + } + + // Errors queries all the errors for a given contract. + rpc Errors(QueryErrorsRequest) returns (QueryErrorsResponse) { + option (google.api.http).get = "/rollapp/cwerrors/v1/errors"; + } + + // IsSubscribed queries if a contract is subscribed to sudo error callbacks. + rpc IsSubscribed(QueryIsSubscribedRequest) + returns (QueryIsSubscribedResponse) { + option (google.api.http).get = "/rollapp/cwerrors/v1/is_subscribed"; + } +} + +// QueryParamsRequest is the request for Query.Params. +message QueryParamsRequest {} + +// QueryParamsResponse is the response for Query.Params. +message QueryParamsResponse { + // params defines all the module parameters. + Params params = 1 [ (gogoproto.nullable) = false ]; +} + +// QueryErrorsRequest is the request for Query.Errors. +message QueryErrorsRequest { + // contract_address is the address of the contract whose errors to query for + string contract_address = 1; +} + +// QueryErrorsResponse is the response for Query.Errors. +message QueryErrorsResponse { + // errors defines all the contract errors which will be returned + repeated SudoError errors = 1 [ (gogoproto.nullable) = false ]; +} + +// QueryIsSubscribedRequest is the request for Query.IsSubscribed. +message QueryIsSubscribedRequest { + // contract_address is the address of the contract to query if subscribed + string contract_address = 1; +} + +// QueryIsSubscribedResponse is the response for Query.IsSubscribed. +message QueryIsSubscribedResponse { + // subscribed defines if the contract is subscribed to sudo error callbacks + bool subscribed = 1; + // subscription_valid_till defines the block height till which the + // subscription is valid + int64 subscription_valid_till = 2; +} \ No newline at end of file diff --git a/proto/rollapp/cwerrors/v1/tx.proto b/proto/rollapp/cwerrors/v1/tx.proto new file mode 100644 index 0000000..1df3036 --- /dev/null +++ b/proto/rollapp/cwerrors/v1/tx.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; +package rollapp.cwerrors.v1; + +import "gogoproto/gogo.proto"; +import "cosmos/msg/v1/msg.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types"; + +// Msg defines the cwerrors Msg service. +service Msg { + // SubscribeToError defines an operation which will register a contract for a + // sudo callback on errors + rpc SubscribeToError(MsgSubscribeToError) + returns (MsgSubscribeToErrorResponse); +} + +// MsgSubscribeToError is the Msg/SubscribeToError request type. +message MsgSubscribeToError { + option (cosmos.msg.v1.signer) = "sender"; + // sender is the address of who is registering the contarcts for callback on + // error + string sender = 1; + // contract is the address of the contract that will be called on error + string contract_address = 2; + // fee is the subscription fee for the feature (current no fee is charged for + // this feature) + cosmos.base.v1beta1.Coin fee = 3 [ (gogoproto.nullable) = false ]; +} + +// MsgSubscribeToErrorResponse defines the response structure for executing a +// MsgSubscribeToError message. +message MsgSubscribeToErrorResponse { + // subscription_valid_till is the block height till which the subscription is + // valid + int64 subscription_valid_till = 1; +} diff --git a/x/callback/abci.go b/x/callback/abci.go new file mode 100644 index 0000000..5f02f5d --- /dev/null +++ b/x/callback/abci.go @@ -0,0 +1,155 @@ +package callback + +import ( + "cosmossdk.io/collections" + errorsmod "cosmossdk.io/errors" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/dymensionxyz/rollapp-wasm/pkg" + "github.com/dymensionxyz/rollapp-wasm/x/callback/keeper" + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +// EndBlocker fetches all the callbacks registered for the current block height and executes them +func EndBlocker(ctx sdk.Context, k keeper.Keeper, wk types.WasmKeeperExpected, ek types.ErrorsKeeperExpected) []abci.ValidatorUpdate { + k.IterateCallbacksByHeight(ctx, ctx.BlockHeight(), callbackExec(ctx, k, wk, ek)) + return nil +} + +// callbackExec returns a function which executes the callback and deletes it from state after execution +func callbackExec(ctx sdk.Context, k keeper.Keeper, wk types.WasmKeeperExpected, ek types.ErrorsKeeperExpected) func(types.Callback) bool { + logger := k.Logger(ctx) + return func(callback types.Callback) bool { + // creating CallbackMsg which is encoded to json and passed as input to contract execution + callbackMsg := types.NewCallbackMsg(callback.JobId) + callbackMsgString := callbackMsg.String() + + logger.Debug( + "executing callback", + "contract_address", callback.ContractAddress, + "job_id", callback.JobId, + "msg", callbackMsgString, + ) + + params, err := k.GetParams(ctx) + if err != nil { + panic(err) + } + + gasUsed, err := pkg.ExecuteWithGasLimit(ctx, callback.MaxGasLimit, func(ctx sdk.Context) error { + // executing the callback on the contract + _, err := wk.Sudo(ctx, sdk.MustAccAddressFromBech32(callback.ContractAddress), callbackMsg.Bytes()) + return err + }) + if err != nil { + logger.Error( + "error executing callback", + "contract_address", callback.ContractAddress, + "job_id", callback.JobId, + "error", err, + ) + // Emit failure event + types.EmitCallbackExecutedFailedEvent( + ctx, + callback.ContractAddress, + callback.JobId, + callbackMsgString, + gasUsed, + err.Error(), + ) + + errorCode := types.ModuleErrors_ERR_UNKNOWN + // check if out of gas error + if errorsmod.IsOf(err, sdkerrors.ErrOutOfGas) { + errorCode = types.ModuleErrors_ERR_OUT_OF_GAS + } + // check if the error was due to contract execution failure + if errorsmod.IsOf(err, wasmtypes.ErrExecuteFailed) { + errorCode = types.ModuleErrors_ERR_CONTRACT_EXECUTION_FAILED + } + + // Save error in the errors keeper + sudoErr := types.NewSudoError( + errorCode, + callback.ContractAddress, + callbackMsgString, + err.Error(), + ) + err := ek.SetError(ctx, sudoErr) + if err != nil { + panic(err) + } + + // This is because gasUsed amount returned is greater than the gas limit. cuz ofc. + // so we set it to callback.MaxGasLimit so when we do txFee refund, we arent trying to refund more than we should + // e.g if callback.MaxGasLimit is 10, but gasUsed is 100, we need to use 10 to calculate txFeeRefund. + // else the module will pay back more than it took from the user 💀 + // TLDR; this ensures in case of "out of gas error", we keep all txFees and refund nothing. + gasUsed = callback.MaxGasLimit + } else { + logger.Info( + "callback executed successfully", + "contract_address", callback.ContractAddress, + "job_id", callback.JobId, + "msg", callbackMsgString, + "gas_used", gasUsed, + ) + // Emit success event + types.EmitCallbackExecutedSuccessEvent( + ctx, + callback.ContractAddress, + callback.JobId, + callbackMsgString, + gasUsed, + ) + } + + logger.Info( + "callback executed with pending gas", + "contract_address", callback.ContractAddress, + "job_id", callback.JobId, + "used_gas", gasUsed, + ) + + // Calculate current tx fees based on gasConsumed. Refund any leftover to the address which reserved the callback + txFeesConsumed := k.CalculateTransactionFees(ctx, gasUsed, params.GetMinPriceOfGas()) + if txFeesConsumed.IsLT(*callback.FeeSplit.TransactionFees) { + refundAmount := callback.FeeSplit.TransactionFees.Sub(txFeesConsumed) + err := k.RefundFromCallbackModule(ctx, callback.ReservedBy, refundAmount) + if err != nil { + panic(err) + } + } else { + // This is to ensure that if the txFeeConsumed is higher due to rise in gas price, + // we dont fund fee_collector more than we should + txFeesConsumed = *callback.FeeSplit.TransactionFees + } + + // Send fees to fee collector + feeCollectorAmount := callback.FeeSplit.BlockReservationFees. + Add(*callback.FeeSplit.FutureReservationFees). + Add(*callback.FeeSplit.SurplusFees). + Add(txFeesConsumed) + err = k.SendToFeeCollector(ctx, feeCollectorAmount) + if err != nil { + panic(err) + } + + // deleting the callback after execution + if err := k.Callbacks.Remove( + ctx, + collections.Join3( + callback.CallbackHeight, + sdk.MustAccAddressFromBech32(callback.ContractAddress).Bytes(), + callback.JobId, + ), + ); err != nil { + panic(err) + } + + return false + } +} diff --git a/x/callback/abci_test.go b/x/callback/abci_test.go new file mode 100644 index 0000000..b63a5cd --- /dev/null +++ b/x/callback/abci_test.go @@ -0,0 +1,200 @@ +package callback_test + +import ( + "encoding/json" + "fmt" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + wasmdTypes "github.com/CosmWasm/wasmd/x/wasm/types" + + e2eTesting "github.com/dymensionxyz/rollapp-wasm/e2e/testing" + callbackKeeper "github.com/dymensionxyz/rollapp-wasm/x/callback/keeper" + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +const ( + DECREMENT_JOBID = 0 + INCREMENT_JOBID = 1 + ERROR_JOBID = 2 + DONOTHING_JOBID = 3 +) + +func TestEndBlocker(t *testing.T) { + chain := e2eTesting.NewTestChain(t, 1) + keeper := chain.GetApp().CallbackKeeper + errorsKeeper := chain.GetApp().CWErrorsKeeper + msgServer := callbackKeeper.NewMsgServer(keeper) + contractAdminAcc := chain.GetAccount(0) + + // Upload and instantiate contract + // The test contract is based on the default counter contract and behaves the following way: + // When job_id = 1, it increments the count value + // When job_id = 0, it decrements the count value + // When job_id = 2, it throws an error + // For any other job_id, it does nothing + codeID := chain.UploadContract(contractAdminAcc, "../../contracts/callback-test/artifacts/callback_test.wasm", wasmdTypes.DefaultUploadAccess) + initMsg := CallbackContractInstantiateMsg{Count: 100} + contractAddr, _ := chain.InstantiateContract(contractAdminAcc, codeID, contractAdminAcc.Address.String(), "callback_test", nil, initMsg) + chain.NextBlock(1) + + testCases := []struct { + testCase string + jobId uint64 + expectedCount int32 + }{ + { + testCase: "Decrement count", + jobId: DECREMENT_JOBID, + expectedCount: initMsg.Count - 1, + }, + { + testCase: "Increment count", + jobId: INCREMENT_JOBID, + expectedCount: initMsg.Count, + }, + { + testCase: "Do nothing", + jobId: DONOTHING_JOBID, + expectedCount: initMsg.Count, + }, + { + testCase: "Throw error", // The contract throws error but the EndBlocker should not. + jobId: ERROR_JOBID, + expectedCount: initMsg.Count, + }, + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("Case: %s", tc.testCase), func(t *testing.T) { + ctx := chain.GetContext() + feesToPay, err := getCallbackRegistrationFees(chain) + require.NoError(t, err) + + reqMsg := &types.MsgRequestCallback{ + ContractAddress: contractAddr.String(), + JobId: tc.jobId, + CallbackHeight: ctx.BlockHeight() + 1, + Sender: contractAdminAcc.Address.String(), + Fees: feesToPay, + } + _, err = msgServer.RequestCallback(sdk.WrapSDKContext(ctx), reqMsg) + require.NoError(t, err) + + // Increment block height and run end blocker at the next block + chain.NextBlock(1) + chain.NextBlock(1) + + // Checking if the count value is as expected + count := getCount(t, chain, contractAddr) + require.Equal(t, tc.expectedCount, count) + }) + } + + // Ensure error is captured by the cwerrors module - the case is when job id = 2 + sudoErrs, err := errorsKeeper.GetErrorsByContractAddress(chain.GetContext(), contractAddr) + require.NoError(t, err) + require.Len(t, sudoErrs, 1) + require.Equal(t, "SomeError: execute wasm contract failed", sudoErrs[0].ErrorMessage) + require.Equal(t, "callback", sudoErrs[0].ModuleName) + require.Equal(t, int32(types.ModuleErrors_ERR_CONTRACT_EXECUTION_FAILED), sudoErrs[0].ErrorCode) + + // Registering the contract for error subscription + _, err = errorsKeeper.SetSubscription(chain.GetContext(), contractAddr, contractAddr, sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)) + require.NoError(t, err) + + params, err := keeper.GetParams(chain.GetContext()) + require.NoError(t, err) + + // TEST CASE: Test CallbackGasLimit limit value reduced + // First we set the params value to default + // Register a callback for next block + feesToPay, err := getCallbackRegistrationFees(chain) + require.NoError(t, err) + reqMsg := &types.MsgRequestCallback{ + ContractAddress: contractAddr.String(), + JobId: INCREMENT_JOBID, + CallbackHeight: chain.GetContext().BlockHeight() + 1, + Sender: contractAdminAcc.Address.String(), + Fees: feesToPay, + } + _, err = msgServer.RequestCallback(sdk.WrapSDKContext(chain.GetContext()), reqMsg) + require.NoError(t, err) + + // Setting the callbackGasLimit param to 1 + params.CallbackGasLimit = 1 + err = keeper.SetParams(chain.GetContext(), params) + require.NoError(t, err) + + // Increment block height and run end blocker + chain.NextBlock(1) + chain.NextBlock(1) + + // Checking if the count value has incremented. + // Should have incremented as the callback should have access to higher gas limit as it was registered before the gas limit was reduced + count := getCount(t, chain, contractAddr) + require.Equal(t, initMsg.Count+1, count) + + // TEST CASE: OUT OF GAS ERROR + // Reserving a callback for next block + // This callback should fail as it consumes more gas than allowed + feesToPay, err = getCallbackRegistrationFees(chain) + require.NoError(t, err) + reqMsg = &types.MsgRequestCallback{ + ContractAddress: contractAddr.String(), + JobId: INCREMENT_JOBID, + CallbackHeight: chain.GetContext().BlockHeight() + 1, + Sender: contractAdminAcc.Address.String(), + Fees: feesToPay, + } + _, err = msgServer.RequestCallback(sdk.WrapSDKContext(chain.GetContext()), reqMsg) + require.NoError(t, err) + + // Increment block height and run end blocker + chain.NextBlock(1) + chain.NextBlock(1) + + // Checking if the count value is zero. should be as error callback rests count when out of gas error + count = getCount(t, chain, contractAddr) + require.Equal(t, int32(0), count) +} + +func getCallbackRegistrationFees(chain *e2eTesting.TestChain) (sdk.Coin, error) { + ctx := chain.GetContext() + currentBlockHeight := ctx.BlockHeight() + callbackHeight := currentBlockHeight + 1 + futureResFee, blockResFee, txFee, err := chain.GetApp().CallbackKeeper.EstimateCallbackFees(ctx, callbackHeight) + if err != nil { + return sdk.Coin{}, err + } + feesToPay := futureResFee.Add(blockResFee).Add(txFee) + return feesToPay, nil +} + +// getCount is a helper function to get the contract's count value +func getCount(t *testing.T, chain *e2eTesting.TestChain, contractAddr sdk.AccAddress) int32 { + getCountQuery := "{\"get_count\":{}}" + resp, err := chain.GetApp().WasmKeeper.QuerySmart(chain.GetContext(), contractAddr, []byte(getCountQuery)) + require.NoError(t, err) + var getCountResp CallbackContractQueryMsg + err = json.Unmarshal(resp, &getCountResp) + require.NoError(t, err) + return getCountResp.Count +} + +type CallbackContractInstantiateMsg struct { + Count int32 `json:"count"` +} + +func (msg CallbackContractInstantiateMsg) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Count int32 `json:"count"` + }{ + Count: msg.Count, + }) +} + +type CallbackContractQueryMsg struct { + Count int32 `json:"count"` +} diff --git a/x/callback/client/cli/query.go b/x/callback/client/cli/query.go new file mode 100644 index 0000000..189de6a --- /dev/null +++ b/x/callback/client/cli/query.go @@ -0,0 +1,112 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/spf13/cobra" + + "github.com/dymensionxyz/rollapp-wasm/pkg" + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +// GetQueryCmd builds query command group for the module. +func GetQueryCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the callback module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + cmd.AddCommand( + getQueryParamsCmd(), + getQueryEstimateCallbackFeesCmd(), + getQueryCallbacksCmd(), + ) + return cmd +} + +func getQueryParamsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Args: cobra.NoArgs, + Short: "Query module parameters", + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.Params(cmd.Context(), &types.QueryParamsRequest{}) + if err != nil { + return err + } + + return clientCtx.PrintProto(&res.Params) + }, + } + flags.AddQueryFlagsToCmd(cmd) + return cmd +} + +func getQueryEstimateCallbackFeesCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "estimate-callback-fees [block-height]", + Aliases: []string{"estimate-fees"}, + Args: cobra.ExactArgs(1), + Short: "Query callback registration fees for a given block height", + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + blockHeight, err := pkg.ParseInt64Arg("block-height", args[0]) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + res, err := queryClient.EstimateCallbackFees(cmd.Context(), &types.QueryEstimateCallbackFeesRequest{ + BlockHeight: blockHeight, + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + flags.AddQueryFlagsToCmd(cmd) + return cmd +} + +func getQueryCallbacksCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "callbacks [block-height]", + Args: cobra.ExactArgs(1), + Short: "Query callbacks for a given block height", + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + blockHeight, err := pkg.ParseInt64Arg("block-height", args[0]) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + res, err := queryClient.Callbacks(cmd.Context(), &types.QueryCallbacksRequest{ + BlockHeight: blockHeight, + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + flags.AddQueryFlagsToCmd(cmd) + return cmd +} diff --git a/x/callback/client/cli/tx.go b/x/callback/client/cli/tx.go new file mode 100644 index 0000000..594a6fd --- /dev/null +++ b/x/callback/client/cli/tx.go @@ -0,0 +1,109 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/spf13/cobra" + + "github.com/dymensionxyz/rollapp-wasm/pkg" + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +// GetTxCmd builds tx command group for the module. +func GetTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Transaction commands for the callbacks module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + cmd.AddCommand( + getTxRequestCallbackCmd(), + getTxCancelCallbackCmd(), + ) + + return cmd +} + +func getTxRequestCallbackCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "request-callback [contract-address] [job-id] [callback-height] [fee-amount]", + Args: cobra.ExactArgs(4), + Short: "Request a new callback for the given contract address and job ID at the given height", + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + senderAddr := clientCtx.GetFromAddress() + + contractAddress, err := pkg.ParseAccAddressArg("contract-address", args[0]) + if err != nil { + return err + } + + jobID, err := pkg.ParseUint64Arg("job-id", args[1]) + if err != nil { + return err + } + + callbackHeight, err := pkg.ParseInt64Arg("callback-height", args[2]) + if err != nil { + return err + } + + fees, err := pkg.ParseCoinArg("fee-amount", args[3]) + if err != nil { + return err + } + + msg := types.NewMsgRequestCallback(senderAddr, contractAddress, jobID, callbackHeight, fees) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +func getTxCancelCallbackCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "cancel-callback [contract-address] [job-id] [callback-height]", + Args: cobra.ExactArgs(3), + Short: "Cancel an existing callback given the contract address and its job ID at the specified height", + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + senderAddr := clientCtx.GetFromAddress() + + contractAddress, err := pkg.ParseAccAddressArg("contract-address", args[0]) + if err != nil { + return err + } + + jobID, err := pkg.ParseUint64Arg("job-id", args[1]) + if err != nil { + return err + } + + callbackHeight, err := pkg.ParseInt64Arg("callback-height", args[2]) + if err != nil { + return err + } + + msg := types.NewMsgCancelCallback(senderAddr, contractAddress, jobID, callbackHeight) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/callback/genesis.go b/x/callback/genesis.go new file mode 100644 index 0000000..f9389de --- /dev/null +++ b/x/callback/genesis.go @@ -0,0 +1,30 @@ +package callback + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/dymensionxyz/rollapp-wasm/x/callback/keeper" + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +// InitGenesis initializes the module genesis state. +func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { + params := genState.Params + err := k.Params.Set(ctx, params) + if err != nil { + panic(err) + } +} + +// ExportGenesis exports the module genesis for the current block. +func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { + params, err := k.Params.Get(ctx) + if err != nil { + panic(err) + } + callbacks, err := k.GetAllCallbacks(ctx) + if err != nil { + panic(err) + } + return types.NewGenesisState(params, callbacks) +} diff --git a/x/callback/genesis_test.go b/x/callback/genesis_test.go new file mode 100644 index 0000000..2e71e36 --- /dev/null +++ b/x/callback/genesis_test.go @@ -0,0 +1,123 @@ +package callback_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + wasmdTypes "github.com/CosmWasm/wasmd/x/wasm/types" + + e2eTesting "github.com/dymensionxyz/rollapp-wasm/e2e/testing" + "github.com/dymensionxyz/rollapp-wasm/x/callback" + callbackKeeper "github.com/dymensionxyz/rollapp-wasm/x/callback/keeper" + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +func TestExportGenesis(t *testing.T) { + chain := e2eTesting.NewTestChain(t, 1) + ctx, keeper := chain.GetContext(), chain.GetApp().CallbackKeeper + msgServer := callbackKeeper.NewMsgServer(keeper) + contractAdminAcc := chain.GetAccount(1) + + // Upload and instantiate contract + codeID := chain.UploadContract(contractAdminAcc, "../../contracts/callback-test/artifacts/callback_test.wasm", wasmdTypes.DefaultUploadAccess) + initMsg := CallbackContractInstantiateMsg{Count: 100} + contractAddr, _ := chain.InstantiateContract(contractAdminAcc, codeID, contractAdminAcc.Address.String(), "callback_test", nil, initMsg) + + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + currentBlockHeight := ctx.BlockHeight() + callbackHeight := currentBlockHeight + 1 + futureResFee, blockResFee, txFee, err := keeper.EstimateCallbackFees(ctx, callbackHeight+5) + require.NoError(t, err) + feesToPay := futureResFee.Add(blockResFee).Add(txFee) + + reqMsg := &types.MsgRequestCallback{ + ContractAddress: contractAddr.String(), + JobId: DECREMENT_JOBID, + CallbackHeight: callbackHeight, + Sender: contractAdminAcc.Address.String(), + Fees: feesToPay, + } + _, err = msgServer.RequestCallback(sdk.WrapSDKContext(ctx), reqMsg) + require.NoError(t, err) + + reqMsg.JobId = INCREMENT_JOBID + _, err = msgServer.RequestCallback(sdk.WrapSDKContext(ctx), reqMsg) + require.NoError(t, err) + + reqMsg.JobId = DONOTHING_JOBID + _, err = msgServer.RequestCallback(sdk.WrapSDKContext(ctx), reqMsg) + require.NoError(t, err) + + reqMsg.CallbackHeight = callbackHeight + 1 + _, err = msgServer.RequestCallback(sdk.WrapSDKContext(ctx), reqMsg) + require.NoError(t, err) + + params := types.Params{ + CallbackGasLimit: 1000000, + MaxBlockReservationLimit: 1, + MaxFutureReservationLimit: 1, + BlockReservationFeeMultiplier: sdk.ZeroDec(), + FutureReservationFeeMultiplier: sdk.ZeroDec(), + } + err = keeper.SetParams(ctx, params) + require.NoError(t, err) + + exportedState := callback.ExportGenesis(ctx, keeper) + require.Equal(t, 4, len(exportedState.Callbacks)) + require.Equal(t, params.CallbackGasLimit, exportedState.Params.CallbackGasLimit) + require.Equal(t, params.MaxBlockReservationLimit, exportedState.Params.MaxBlockReservationLimit) + require.Equal(t, params.MaxFutureReservationLimit, exportedState.Params.MaxFutureReservationLimit) + require.Equal(t, params.BlockReservationFeeMultiplier, exportedState.Params.BlockReservationFeeMultiplier) + require.Equal(t, params.FutureReservationFeeMultiplier, exportedState.Params.FutureReservationFeeMultiplier) +} + +func TestInitGenesis(t *testing.T) { + chain := e2eTesting.NewTestChain(t, 1) + ctx, keeper := chain.GetContext(), chain.GetApp().CallbackKeeper + contractAddr := e2eTesting.GenContractAddresses(1)[0] + validCoin := sdk.NewInt64Coin("stake", 10) + + genParams := types.Params{ + CallbackGasLimit: 1000000, + MaxBlockReservationLimit: 1, + MaxFutureReservationLimit: 1, + BlockReservationFeeMultiplier: sdk.ZeroDec(), + FutureReservationFeeMultiplier: sdk.ZeroDec(), + } + err := keeper.SetParams(ctx, genParams) + require.NoError(t, err) + + genstate := types.GenesisState{ + Params: genParams, + Callbacks: []*types.Callback{ + { + ContractAddress: contractAddr.String(), + JobId: 1, + CallbackHeight: 100, + ReservedBy: contractAddr.String(), + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + }, + } + + callback.InitGenesis(ctx, keeper, genstate) + + callbacks, err := keeper.GetAllCallbacks(ctx) + require.NoError(t, err) + require.Equal(t, 0, len(callbacks)) // Ensuring callbacks are not imported + + params, err := keeper.GetParams(ctx) + require.NoError(t, err) + require.Equal(t, genParams.CallbackGasLimit, params.CallbackGasLimit) + require.Equal(t, genParams.MaxBlockReservationLimit, params.MaxBlockReservationLimit) + require.Equal(t, genParams.MaxFutureReservationLimit, params.MaxFutureReservationLimit) + require.Equal(t, genParams.BlockReservationFeeMultiplier, params.BlockReservationFeeMultiplier) + require.Equal(t, genParams.FutureReservationFeeMultiplier, params.FutureReservationFeeMultiplier) +} diff --git a/x/callback/keeper/callback.go b/x/callback/keeper/callback.go new file mode 100644 index 0000000..e0c89a1 --- /dev/null +++ b/x/callback/keeper/callback.go @@ -0,0 +1,145 @@ +package keeper + +import ( + "strings" + + "cosmossdk.io/collections" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +// GetAllCallbacks lists all the pending callbacks +func (k Keeper) GetAllCallbacks(ctx sdk.Context) (callbacks []types.Callback, err error) { + err = k.Callbacks.Walk(ctx, nil, func(key collections.Triple[int64, []byte, uint64], value types.Callback) (bool, error) { + callbacks = append(callbacks, value) + return false, nil + }) + return callbacks, err +} + +// GetCallbacksByHeight returns the callbacks registered for the given height +func (k Keeper) GetCallbacksByHeight(ctx sdk.Context, height int64) (callbacks []*types.Callback, err error) { + rng := collections.NewPrefixedTripleRange[int64, []byte, uint64](height) + err = k.Callbacks.Walk(ctx, rng, func(key collections.Triple[int64, []byte, uint64], value types.Callback) (bool, error) { + callbacks = append(callbacks, &value) + return false, nil + }) + return callbacks, err +} + +// IterateCallbacksByHeight iterates over callbacks for registered for the given height and executes them +func (k Keeper) IterateCallbacksByHeight(ctx sdk.Context, height int64, exec func(types.Callback) bool) { + rng := collections.NewPrefixedTripleRange[int64, []byte, uint64](height) + _ = k.Callbacks.Walk(ctx, rng, func(key collections.Triple[int64, []byte, uint64], value types.Callback) (bool, error) { + exec(value) + return false, nil + }) +} + +// ExistsCallback returns true if the callback exists for height with same contract address and same job id +func (k Keeper) ExistsCallback(ctx sdk.Context, height int64, contractAddr string, jobID uint64) (bool, error) { + contractAddress, err := sdk.AccAddressFromBech32(contractAddr) + if err != nil { + return false, err + } + return k.Callbacks.Has(ctx, collections.Join3(height, contractAddress.Bytes(), jobID)) +} + +// GetCallback returns the callback given the height, contract address and job id +func (k Keeper) GetCallback(ctx sdk.Context, height int64, contractAddr string, jobID uint64) (types.Callback, error) { + contractAddress, err := sdk.AccAddressFromBech32(contractAddr) + if err != nil { + return types.Callback{}, err + } + + return k.Callbacks.Get(ctx, collections.Join3(height, contractAddress.Bytes(), jobID)) +} + +// DeleteCallback deletes a callback given the height, contract address and job id +func (k Keeper) DeleteCallback(ctx sdk.Context, sender string, callback types.Callback) error { + contractAddress, err := sdk.AccAddressFromBech32(callback.ContractAddress) + if err != nil { + return err + } + // If callback delete is requested by someone who is not authorized, return error + if !isAuthorizedToModify(ctx, k, contractAddress, sender) { + return types.ErrUnauthorized + } + return k.Callbacks.Remove(ctx, collections.Join3(callback.CallbackHeight, contractAddress.Bytes(), callback.JobId)) +} + +// SaveCallback saves a callback given the height, contract address and job id and callback data +func (k Keeper) SaveCallback(ctx sdk.Context, callback types.Callback) error { + contractAddress, err := sdk.AccAddressFromBech32(callback.ContractAddress) + if err != nil { + return err + } + // If contract with given address does not exist, return error + if !k.wasmKeeper.HasContractInfo(ctx, contractAddress) { + return types.ErrContractNotFound + } + // If callback is requested by someone which is not authorized, return error + if !isAuthorizedToModify(ctx, k, contractAddress, callback.ReservedBy) { + return types.ErrUnauthorized + } + // If a callback with same job id exists at same height, return error + exists, err := k.ExistsCallback(ctx, callback.CallbackHeight, contractAddress.String(), callback.JobId) + if err != nil { + return err + } + if exists { + return types.ErrCallbackExists + } + // If callback is requested for height in the past or present, return error + if callback.CallbackHeight <= ctx.BlockHeight() { + return types.ErrCallbackHeightNotInFuture + } + + params, err := k.GetParams(ctx) + if err != nil { + return err + } + // If callback is requested for height which is too far in the future, return error + maxFutureReservationHeight := ctx.BlockHeight() + int64(params.MaxFutureReservationLimit) + if callback.CallbackHeight > maxFutureReservationHeight { + return types.ErrCallbackHeightTooFarInFuture + } + // If there are already too many callbacks registered in a given block, return error + callbacksForBlock, err := k.GetCallbacksByHeight(ctx, callback.CallbackHeight) + if err != nil { + return err + } + if len(callbacksForBlock) >= int(params.MaxBlockReservationLimit) { + return types.ErrBlockFilled + } + + // Setting the callback gas limit to the module param CallbackGasLimit. + // This is to ensure that if the param value is decreased in the future, before the callback is executed, + // it does not fail with "out of gas" error. it wouldnt be fair for the contract to err out of the callback + // if it wouldnt have been expected to at the time of registration + params, err = k.GetParams(ctx) + if err != nil { + return err + } + callback.MaxGasLimit = params.CallbackGasLimit + + return k.Callbacks.Set(ctx, collections.Join3(callback.CallbackHeight, contractAddress.Bytes(), callback.JobId), callback) +} + +func isAuthorizedToModify(ctx sdk.Context, k Keeper, contractAddress sdk.AccAddress, sender string) bool { + if k.bankKeeper.BlockedAddr(sdk.MustAccAddressFromBech32(sender)) { // Blocked addresses cannot create/delete callbacks as we cant refund to these addresses. And they are module accounts anyway + return false + } + + if strings.EqualFold(sender, contractAddress.String()) { // A contract can modify its own callbacks + return true + } + + contractInfo := k.wasmKeeper.GetContractInfo(ctx, contractAddress) + if strings.EqualFold(sender, contractInfo.Admin) { // Admin of the contract can modify its callbacks + return true + } + + return false +} diff --git a/x/callback/keeper/callback_test.go b/x/callback/keeper/callback_test.go new file mode 100644 index 0000000..d649e9d --- /dev/null +++ b/x/callback/keeper/callback_test.go @@ -0,0 +1,563 @@ +package keeper_test + +import ( + "fmt" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + + e2eTesting "github.com/dymensionxyz/rollapp-wasm/e2e/testing" + "github.com/dymensionxyz/rollapp-wasm/pkg/testutils" + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +func (s *KeeperTestSuite) TestSaveCallback() { + // Setting up chain and contract in mock wasm keeper + ctx, keeper := s.chain.GetContext().WithBlockHeight(100), s.chain.GetApp().CallbackKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + validCoin := sdk.NewInt64Coin("stake", 10) + + contractAddresses := e2eTesting.GenContractAddresses(3) + contractAddr := contractAddresses[0] + contractAddr2 := contractAddresses[1] + contractAddr3 := contractAddresses[2] + contractAdminAcc := s.chain.GetAccount(0) + notContractAdminAcc := s.chain.GetAccount(1) + // contractOwnerAcc := s.chain.GetAccount(2) + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + contractViewer.AddContractAdmin( + contractAddr2.String(), + contractAdminAcc.Address.String(), + ) + + // Setting callback module as contract owner + blockedModuleAddr := s.chain.GetApp().AccountKeeper.GetModuleAccount(ctx, types.ModuleName).GetAddress() + s.Require().True(s.chain.GetApp().BankKeeper.BlockedAddr(blockedModuleAddr)) + + params, err := keeper.GetParams(ctx) + s.Require().NoError(err) + + testCases := []struct { + testCase string + callback types.Callback + expectError bool + errorType error + }{ + { + testCase: "FAIL: contract address is invalid", + callback: types.Callback{ + ContractAddress: "👻", + JobId: 1, + CallbackHeight: 101, + ReservedBy: contractAddr.String(), + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + expectError: true, + errorType: fmt.Errorf("decoding bech32 failed: invalid bech32 string length 4"), + }, + { + testCase: "FAIL: contract does not exist", + callback: types.Callback{ + ContractAddress: contractAddr3.String(), + JobId: 1, + CallbackHeight: 101, + ReservedBy: contractAddr.String(), + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + expectError: true, + errorType: types.ErrContractNotFound, + }, + { + testCase: "FAIL: sender not authorized to modify", + callback: types.Callback{ + ContractAddress: contractAddr.String(), + JobId: 1, + CallbackHeight: 101, + ReservedBy: notContractAdminAcc.Address.String(), + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + expectError: true, + errorType: types.ErrUnauthorized, + }, + { + testCase: "FAIL: callback height is in the past", + callback: types.Callback{ + ContractAddress: contractAddr.String(), + JobId: 1, + CallbackHeight: 99, + ReservedBy: contractAddr.String(), + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + expectError: true, + errorType: types.ErrCallbackHeightNotInFuture, + }, + { + testCase: "FAIL: callback height is current height", + callback: types.Callback{ + ContractAddress: contractAddr.String(), + JobId: 1, + CallbackHeight: ctx.BlockHeight(), + ReservedBy: contractAddr.String(), + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + expectError: true, + errorType: types.ErrCallbackHeightNotInFuture, + }, + { + testCase: "FAIL: callback is too far in the future", + callback: types.Callback{ + ContractAddress: contractAddr.String(), + JobId: 1, + CallbackHeight: ctx.BlockHeight() + int64(params.MaxFutureReservationLimit) + 1, + ReservedBy: contractAddr.String(), + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + expectError: true, + errorType: types.ErrCallbackHeightTooFarInFuture, + }, + { + testCase: "FAIL: sender is a blocked address", + callback: types.Callback{ + ContractAddress: contractAddr2.String(), + JobId: 1, + CallbackHeight: 102, + ReservedBy: blockedModuleAddr.String(), + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + expectError: true, + errorType: types.ErrUnauthorized, + }, + { + testCase: "OK: save callback - sender is contract", + callback: types.Callback{ + ContractAddress: contractAddr.String(), + JobId: 1, + CallbackHeight: 101, + ReservedBy: contractAddr.String(), + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + expectError: false, + }, + + { + testCase: "OK: save callback - sender is contract admin", + callback: types.Callback{ + ContractAddress: contractAddr.String(), + JobId: 3, + CallbackHeight: 101, + ReservedBy: contractAdminAcc.Address.String(), + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + expectError: false, + }, + { + testCase: "OK: save callback - sender is contract admin but address is in uppercase", + callback: types.Callback{ + ContractAddress: contractAddr.String(), + JobId: 5, + CallbackHeight: 101, + ReservedBy: strings.ToUpper(contractAdminAcc.Address.String()), + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + expectError: false, + }, + { + testCase: "FAIL: callback already exists", + callback: types.Callback{ + ContractAddress: contractAddr.String(), + JobId: 1, + CallbackHeight: 101, + ReservedBy: contractAddr.String(), + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + expectError: true, + errorType: types.ErrCallbackExists, + }, + { + testCase: "FAIL: block is filled with max number of callbacks", + callback: types.Callback{ + ContractAddress: contractAddr.String(), + JobId: 4, + CallbackHeight: 101, + ReservedBy: contractAddr.String(), + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + expectError: true, + errorType: types.ErrBlockFilled, + }, + } + for _, tc := range testCases { + s.Run(fmt.Sprintf("Case: %s", tc.testCase), func() { + err := keeper.SaveCallback(ctx, tc.callback) + if tc.expectError { + s.Require().Error(err) + s.Assert().ErrorContains(err, tc.errorType.Error()) + } else { + s.Require().NoError(err) + // Ensuring the callback exists now + exists, err := keeper.ExistsCallback(ctx, tc.callback.CallbackHeight, tc.callback.ContractAddress, tc.callback.JobId) + s.Require().NoError(err) + s.Require().True(exists) + } + }) + } +} + +func (s *KeeperTestSuite) TestDeleteCallback() { + ctx, keeper := s.chain.GetContext().WithBlockHeight(100), s.chain.GetApp().CallbackKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + validCoin := sdk.NewInt64Coin("stake", 10) + + contractAddr := e2eTesting.GenContractAddresses(1)[0] + contractAdminAcc := s.chain.GetAccount(0) + notContractAdminAcc := s.chain.GetAccount(1) + // contractOwnerAcc := s.chain.GetAccount(2) + + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + + // Same contract requesting callback at same height with diff job id + callback := types.Callback{ + ContractAddress: contractAddr.String(), + JobId: 1, + CallbackHeight: 101, + ReservedBy: contractAddr.String(), + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + } + err := keeper.SaveCallback(ctx, callback) + s.Require().NoError(err) + callback.JobId = 2 + err = keeper.SaveCallback(ctx, callback) + s.Require().NoError(err) + callback.JobId = 3 + err = keeper.SaveCallback(ctx, callback) + s.Require().NoError(err) + + testCases := []struct { + testCase string + callback types.Callback + expectError bool + errorType error + }{ + { + testCase: "FAIL: Invalid contract address", + callback: types.Callback{ + ContractAddress: "👻", + JobId: 0, + CallbackHeight: 101, + ReservedBy: contractAddr.String(), + }, + expectError: true, + errorType: fmt.Errorf("decoding bech32 failed: invalid bech32 string length 4"), + }, + { + testCase: "FAIL: Not authorized to delete callback", + callback: types.Callback{ + ContractAddress: contractAddr.String(), + JobId: 0, + CallbackHeight: 101, + ReservedBy: notContractAdminAcc.Address.String(), + }, + expectError: true, + errorType: types.ErrUnauthorized, + }, + { + testCase: "FAIL: Callback does not exist", + callback: types.Callback{ + ContractAddress: contractAddr.String(), + JobId: 0, + CallbackHeight: 101, + ReservedBy: contractAddr.String(), + }, + expectError: false, // Should silently fail. MsgSrvr ensures that callback exists before calling keeper + }, + { + testCase: "OK: Success delete - sender is contract", + callback: types.Callback{ + ContractAddress: contractAddr.String(), + JobId: 1, + CallbackHeight: 101, + ReservedBy: contractAddr.String(), + }, + expectError: false, + }, + { + testCase: "OK: Success delete - sender is contract admin", + callback: types.Callback{ + ContractAddress: contractAddr.String(), + JobId: 2, + CallbackHeight: 101, + ReservedBy: contractAdminAcc.Address.String(), + }, + expectError: false, + }, + // { + // testCase: "OK: Success delete - sender is contract owner", + // callback: types.Callback{ + // ContractAddress: contractAddr.String(), + // JobId: 3, + // CallbackHeight: 101, + // ReservedBy: contractOwnerAcc.Address.String(), + // }, + // expectError: false, + // }, + } + for _, tc := range testCases { + s.Run(fmt.Sprintf("Case: %s", tc.testCase), func() { + err := keeper.DeleteCallback(ctx, tc.callback.ReservedBy, tc.callback) + if tc.expectError { + s.Require().Error(err) + s.Assert().ErrorContains(err, tc.errorType.Error()) + } else { + s.Require().NoError(err) + // Ensuring the callback does not exist anymore + exists, err := keeper.ExistsCallback(ctx, tc.callback.CallbackHeight, tc.callback.ContractAddress, tc.callback.JobId) + s.Require().NoError(err) + s.Require().False(exists) + } + }) + } +} + +func (s *KeeperTestSuite) TestGetCallbacksByHeight() { + // Setting up chain and contract in mock wasm keeper + ctx, keeper := s.chain.GetContext().WithBlockHeight(100), s.chain.GetApp().CallbackKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + validCoin := sdk.NewInt64Coin("stake", 10) + + contractAddr := e2eTesting.GenContractAddresses(1)[0] + contractAdminAcc := s.chain.GetAccount(0) + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + + callbackHeight := int64(101) + + // Same contract requesting callback at same height with diff job id + callback := types.Callback{ + ContractAddress: contractAddr.String(), + JobId: 1, + CallbackHeight: callbackHeight, + ReservedBy: contractAddr.String(), + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + } + err := keeper.SaveCallback(ctx, callback) + s.Require().NoError(err) + + callback.JobId = 2 + err = keeper.SaveCallback(ctx, callback) + s.Require().NoError(err) + + callback.JobId = 3 + err = keeper.SaveCallback(ctx, callback) + s.Require().NoError(err) + + s.Run("OK: Get all three existing callbacks at height 101", func() { + callbacks, err := keeper.GetCallbacksByHeight(ctx, callbackHeight) + s.Assert().NoError(err) + s.Assert().Equal(3, len(callbacks)) + }) + s.Run("OK: Get zero existing callbacks at height 102", func() { + callbacks, err := keeper.GetCallbacksByHeight(ctx, callbackHeight+1) + s.Assert().NoError(err) + s.Assert().Equal(0, len(callbacks)) + }) +} + +func (s *KeeperTestSuite) TestGetAllCallbacks() { + // Setting up chain and contract in mock wasm keeper + ctx, keeper := s.chain.GetContext().WithBlockHeight(100), s.chain.GetApp().CallbackKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + validCoin := sdk.NewInt64Coin("stake", 10) + + contractAddr := e2eTesting.GenContractAddresses(1)[0] + contractAdminAcc := s.chain.GetAccount(0) + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + + callbackHeight := int64(105) + + s.Run("OK: Get zero existing callbacks", func() { + callbacks, err := keeper.GetAllCallbacks(ctx) + s.Assert().NoError(err) + s.Assert().Equal(0, len(callbacks)) + }) + + callback := types.Callback{ + ContractAddress: contractAddr.String(), + JobId: 1, + CallbackHeight: callbackHeight, + ReservedBy: contractAddr.String(), + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + } + err := keeper.SaveCallback(ctx, callback) + s.Require().NoError(err) + + callback.JobId = 2 + callback.CallbackHeight = callbackHeight + 1 + err = keeper.SaveCallback(ctx, callback) + s.Require().NoError(err) + + callback.JobId = 3 + callback.CallbackHeight = callbackHeight + 2 + err = keeper.SaveCallback(ctx, callback) + s.Require().NoError(err) + + s.Run("OK: Get all existing callbacks - 3", func() { + callbacks, err := keeper.GetAllCallbacks(ctx) + s.Assert().NoError(err) + s.Assert().Equal(3, len(callbacks)) + }) +} + +func (s *KeeperTestSuite) TestIterateCallbacksByHeight() { + // Setting up chain and contract in mock wasm keeper + ctx, keeper := s.chain.GetContext().WithBlockHeight(100), s.chain.GetApp().CallbackKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + validCoin := sdk.NewInt64Coin("stake", 10) + + contractAddr := e2eTesting.GenContractAddresses(1)[0] + contractAdminAcc := s.chain.GetAccount(0) + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + + callbackHeight := int64(101) + + // Same contract requesting callback at same height with diff job id + callback := types.Callback{ + ContractAddress: contractAddr.String(), + JobId: 1, + CallbackHeight: callbackHeight, + ReservedBy: contractAddr.String(), + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + } + err := keeper.SaveCallback(ctx, callback) + s.Require().NoError(err) + + callback.JobId = 2 + err = keeper.SaveCallback(ctx, callback) + s.Require().NoError(err) + + callback.JobId = 3 + err = keeper.SaveCallback(ctx, callback) + s.Require().NoError(err) + + // Same contract requesting callback at diff height with diff job id + callback.JobId = 4 + callback.CallbackHeight = callbackHeight + 1 + err = keeper.SaveCallback(ctx, callback) + s.Require().NoError(err) + + s.Run("OK: Get all three existing callbacks at height 101", func() { + count := 0 + keeper.IterateCallbacksByHeight(ctx, callbackHeight, func(callback types.Callback) bool { + count++ + return false + }) + s.Assert().Equal(3, count) + }) + + s.Run("OK: Get one existing callbacks at height 102", func() { + count := 0 + keeper.IterateCallbacksByHeight(ctx, callbackHeight+1, func(callback types.Callback) bool { + count++ + return false + }) + s.Assert().Equal(1, count) + }) +} diff --git a/x/callback/keeper/fees.go b/x/callback/keeper/fees.go new file mode 100644 index 0000000..bc70d1a --- /dev/null +++ b/x/callback/keeper/fees.go @@ -0,0 +1,56 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// EstimateCallbackFees returns the fees that will be charged for registering a callback at the given block height +// The returned value is in the order of: +// 1. Future reservation fees +// 2. Block reservation fees +// 3. Transaction fees +// 4. Errors, if any +func (k Keeper) EstimateCallbackFees(ctx sdk.Context, blockHeight int64) (sdk.Coin, sdk.Coin, sdk.Coin, error) { + if blockHeight <= ctx.BlockHeight() { + return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, status.Errorf(codes.InvalidArgument, "block height %d is not in the future", blockHeight) + } + + params, err := k.GetParams(ctx) + if err != nil { + return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, status.Errorf(codes.NotFound, "could not fetch the module params: %s", err.Error()) + } + + // Calculates the fees based on how far in the future the callback is registered + futureReservationThreshold := ctx.BlockHeight() + int64(params.MaxFutureReservationLimit) + if blockHeight > futureReservationThreshold { + return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, status.Errorf(codes.OutOfRange, "block height %d is too far in the future. max block height callback can be registered at %d", blockHeight, futureReservationThreshold) + } + // futureReservationFeeMultiplies * (requestBlockHeight - currentBlockHeight) + futureReservationFeesAmount := params.FutureReservationFeeMultiplier.MulInt64((blockHeight - ctx.BlockHeight())) + + // Calculates the fees based on how many callbacks are registered at the given block height + callbacksForHeight, err := k.GetCallbacksByHeight(ctx, blockHeight) + if err != nil { + return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, status.Errorf(codes.NotFound, "could not fetch callbacks for given height: %s", err.Error()) + } + totalCallbacks := len(callbacksForHeight) + if totalCallbacks >= int(params.MaxBlockReservationLimit) { + return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, status.Errorf(codes.OutOfRange, "block height %d has reached max reservation limit", blockHeight) + } + // blockReservationFeeMultiplier * totalCallbacksRegistered + blockReservationFeesAmount := params.BlockReservationFeeMultiplier.MulInt64(int64(totalCallbacks)) + + // Calculates the fees based on the max gas limit of the callback and current price of gas + transactionFee := k.CalculateTransactionFees(ctx, params.GetCallbackGasLimit(), params.GetMinPriceOfGas()) + futureReservationFee := sdk.NewCoin(transactionFee.Denom, futureReservationFeesAmount.RoundInt()) + blockReservationFee := sdk.NewCoin(transactionFee.Denom, blockReservationFeesAmount.RoundInt()) + return futureReservationFee, blockReservationFee, transactionFee, nil +} + +func (k Keeper) CalculateTransactionFees(ctx sdk.Context, gasAmount uint64, minPriceOfGas sdk.Coin) sdk.Coin { + transactionFeeAmount := minPriceOfGas.Amount.MulRaw(int64(gasAmount)) + transactionFee := sdk.NewCoin(minPriceOfGas.Denom, transactionFeeAmount) + return transactionFee +} diff --git a/x/callback/keeper/grpc_query.go b/x/callback/keeper/grpc_query.go new file mode 100644 index 0000000..21f63e3 --- /dev/null +++ b/x/callback/keeper/grpc_query.go @@ -0,0 +1,79 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +var _ types.QueryServer = &QueryServer{} + +// QueryServer implements the module gRPC query service. +type QueryServer struct { + keeper Keeper +} + +// NewQueryServer creates a new gRPC query server. +func NewQueryServer(keeper Keeper) *QueryServer { + return &QueryServer{ + keeper: keeper, + } +} + +// Callbacks implements types.QueryServer. +func (qs *QueryServer) Callbacks(c context.Context, request *types.QueryCallbacksRequest) (*types.QueryCallbacksResponse, error) { + if request == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + callbacks, err := qs.keeper.GetCallbacksByHeight(sdk.UnwrapSDKContext(c), request.GetBlockHeight()) + if err != nil { + return nil, status.Errorf(codes.Internal, "could not fetch the callbacks at height %d: %s", request.GetBlockHeight(), err.Error()) + } + + return &types.QueryCallbacksResponse{ + Callbacks: callbacks, + }, nil +} + +// EstimateCallbackFees implements types.QueryServer. +func (qs *QueryServer) EstimateCallbackFees(c context.Context, request *types.QueryEstimateCallbackFeesRequest) (*types.QueryEstimateCallbackFeesResponse, error) { + if request == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + futureReservationFee, blockReservationFee, transactionFee, err := qs.keeper.EstimateCallbackFees(sdk.UnwrapSDKContext(c), request.GetBlockHeight()) + if err != nil { + return nil, err + } + totalFees := transactionFee.Add(blockReservationFee).Add(futureReservationFee) + + return &types.QueryEstimateCallbackFeesResponse{ + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &transactionFee, + BlockReservationFees: &blockReservationFee, + FutureReservationFees: &futureReservationFee, + }, + TotalFees: &totalFees, + }, nil +} + +// Params implements types.QueryServer. +func (qs *QueryServer) Params(c context.Context, request *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + if request == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + params, err := qs.keeper.GetParams(sdk.UnwrapSDKContext(c)) + if err != nil { + return nil, status.Errorf(codes.NotFound, "could not fetch the module params: %s", err.Error()) + } + + return &types.QueryParamsResponse{ + Params: params, + }, nil +} diff --git a/x/callback/keeper/grpc_query_test.go b/x/callback/keeper/grpc_query_test.go new file mode 100644 index 0000000..8585cc6 --- /dev/null +++ b/x/callback/keeper/grpc_query_test.go @@ -0,0 +1,204 @@ +package keeper_test + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + + e2eTesting "github.com/dymensionxyz/rollapp-wasm/e2e/testing" + "github.com/dymensionxyz/rollapp-wasm/pkg/testutils" + callbackKeeper "github.com/dymensionxyz/rollapp-wasm/x/callback/keeper" + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +func (s *KeeperTestSuite) TestCallbacks() { + // Setting up chain and contract in mock wasm keeper + ctx, keeper := s.chain.GetContext().WithBlockHeight(101), s.chain.GetApp().CallbackKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + validCoin := sdk.NewInt64Coin("stake", 10) + contractAddr := e2eTesting.GenContractAddresses(1)[0] + contractAdminAcc := s.chain.GetAccount(0) + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + callbackHeight := int64(102) + // Same contract requesting callback at same height with diff job id + callback := types.Callback{ + ContractAddress: contractAddr.String(), + JobId: 1, + CallbackHeight: callbackHeight, + ReservedBy: contractAddr.String(), + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + } + err := keeper.SaveCallback(ctx, callback) + s.Require().NoError(err) + callback.JobId = 2 + err = keeper.SaveCallback(ctx, callback) + s.Require().NoError(err) + callback.JobId = 3 + err = keeper.SaveCallback(ctx, callback) + s.Require().NoError(err) + // Same contract requesting callback at diff height with diff job id + callback.JobId = 4 + callback.CallbackHeight = callbackHeight + 1 + err = keeper.SaveCallback(ctx, callback) + s.Require().NoError(err) + + queryServer := callbackKeeper.NewQueryServer(keeper) + + testCases := []struct { + testCase string + input func() *types.QueryCallbacksRequest + expectError bool + noOfCallbackExpected int + }{ + { + testCase: "FAIL: empty request", + input: func() *types.QueryCallbacksRequest { + return nil + }, + expectError: true, + noOfCallbackExpected: 0, + }, + { + testCase: "OK: no callbacks at requested height", + input: func() *types.QueryCallbacksRequest { + return &types.QueryCallbacksRequest{ + BlockHeight: 100, + } + }, + expectError: false, + noOfCallbackExpected: 0, + }, + { + testCase: "OK: get callbacks at requested height. there are three callbacks", + input: func() *types.QueryCallbacksRequest { + return &types.QueryCallbacksRequest{ + BlockHeight: callbackHeight, + } + }, + expectError: false, + noOfCallbackExpected: 3, + }, + { + testCase: "OK: get callbacks at requested height. there is one callback", + input: func() *types.QueryCallbacksRequest { + return &types.QueryCallbacksRequest{ + BlockHeight: callbackHeight + 1, + } + }, + expectError: false, + noOfCallbackExpected: 1, + }, + } + for _, tc := range testCases { + s.Run(fmt.Sprintf("Case: %s", tc.testCase), func() { + req := tc.input() + res, err := queryServer.Callbacks(sdk.WrapSDKContext(ctx), req) + if tc.expectError { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Equal(tc.noOfCallbackExpected, len(res.Callbacks)) + } + }) + } +} + +func (s *KeeperTestSuite) TestEstimateCallbackFees() { + ctx, keeper := s.chain.GetContext().WithBlockHeight(101), s.chain.GetApp().CallbackKeeper + queryServer := callbackKeeper.NewQueryServer(keeper) + zeroCoin := sdk.NewInt64Coin("stake", 0) + + // Setting up custom params where the reservation multipliers are 0 + // and the max callback gas limit is 1, so tx fee is same as computational price of gas + params, err := keeper.GetParams(ctx) + s.Require().NoError(err) + err = keeper.SetParams(ctx, types.Params{ + CallbackGasLimit: 1, + MaxBlockReservationLimit: params.MaxBlockReservationLimit, + MaxFutureReservationLimit: params.MaxFutureReservationLimit, + FutureReservationFeeMultiplier: sdk.MustNewDecFromStr("0"), + BlockReservationFeeMultiplier: sdk.MustNewDecFromStr("0"), + MinPriceOfGas: params.MinPriceOfGas, + }) + s.Require().NoError(err) + expectedTxFeeAmount := params.GetMinPriceOfGas().Amount + expectedTxFeeCoin := sdk.NewInt64Coin("stake", expectedTxFeeAmount.Int64()) + + testCases := []struct { + testCase string + input func() *types.QueryEstimateCallbackFeesRequest + expectError bool + expectedOutput *types.QueryEstimateCallbackFeesResponse + }{ + { + testCase: "FAIL: empty request", + input: func() *types.QueryEstimateCallbackFeesRequest { + return nil + }, + expectError: true, + expectedOutput: nil, + }, + { + testCase: "FAIL: height is in the past", + input: func() *types.QueryEstimateCallbackFeesRequest { + return &types.QueryEstimateCallbackFeesRequest{ + BlockHeight: 100, + } + }, + expectError: true, + expectedOutput: nil, + }, + { + testCase: "FAIL: height is the current block height", + input: func() *types.QueryEstimateCallbackFeesRequest { + return &types.QueryEstimateCallbackFeesRequest{ + BlockHeight: 101, + } + }, + expectError: true, + expectedOutput: nil, + }, + { + testCase: "OK: fetch fees for next height", + input: func() *types.QueryEstimateCallbackFeesRequest { + return &types.QueryEstimateCallbackFeesRequest{ + BlockHeight: 102, + } + }, + expectError: false, + expectedOutput: &types.QueryEstimateCallbackFeesResponse{ + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &expectedTxFeeCoin, + BlockReservationFees: &zeroCoin, + FutureReservationFees: &zeroCoin, + }, + TotalFees: &expectedTxFeeCoin, + }, + }, + } + for _, tc := range testCases { + s.Run(fmt.Sprintf("Case: %s", tc.testCase), func() { + req := tc.input() + res, err := queryServer.EstimateCallbackFees(sdk.WrapSDKContext(ctx), req) + if tc.expectError { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Equal(tc.expectedOutput.TotalFees, res.TotalFees) + s.Require().Equal(tc.expectedOutput.FeeSplit.BlockReservationFees, res.FeeSplit.BlockReservationFees) + s.Require().Equal(tc.expectedOutput.FeeSplit.FutureReservationFees, res.FeeSplit.FutureReservationFees) + s.Require().Equal(tc.expectedOutput.FeeSplit.TransactionFees, res.FeeSplit.TransactionFees) + s.Require().Equal(tc.expectedOutput.FeeSplit.SurplusFees, res.FeeSplit.SurplusFees) + } + }) + } +} diff --git a/x/callback/keeper/keeper.go b/x/callback/keeper/keeper.go new file mode 100644 index 0000000..40b38fa --- /dev/null +++ b/x/callback/keeper/keeper.go @@ -0,0 +1,95 @@ +package keeper + +import ( + "cosmossdk.io/collections" + "github.com/tendermint/tendermint/libs/log" + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authTypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + "github.com/dymensionxyz/rollapp-wasm/internal/collcompat" + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +// Keeper provides module state operations. +type Keeper struct { + cdc codec.Codec + storeKey storetypes.StoreKey + wasmKeeper types.WasmKeeperExpected + bankKeeper types.BankKeeperExpected + + Schema collections.Schema + + // Params key: ParamsKeyPrefix | value: Params + Params collections.Item[types.Params] + // Callbacks key: CallbackKeyPrefix | value: []Callback + Callbacks collections.Map[collections.Triple[int64, []byte, uint64], types.Callback] +} + +// NewKeeper creates a new Keeper instance. +func NewKeeper(cdc codec.Codec, storeKey storetypes.StoreKey, wk types.WasmKeeperExpected, bk types.BankKeeperExpected) Keeper { + sb := collections.NewSchemaBuilder(collcompat.NewKVStoreService(storeKey)) + k := Keeper{ + cdc: cdc, + storeKey: storeKey, + wasmKeeper: wk, + bankKeeper: bk, + Params: collections.NewItem( + sb, + types.ParamsKeyPrefix, + "params", + collcompat.ProtoValue[types.Params](cdc), + ), + Callbacks: collections.NewMap( + sb, + types.CallbackKeyPrefix, + "callbacks", + collections.TripleKeyCodec(collections.Int64Key, collections.BytesKey, collections.Uint64Key), + collcompat.ProtoValue[types.Callback](cdc), + ), + } + schema, err := sb.Build() + if err != nil { + panic(err) + } + k.Schema = schema + return k +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "x/"+types.ModuleName) +} + +// SetWasmKeeper sets the given wasm keeper. +// Only for testing purposes +func (k *Keeper) SetWasmKeeper(wk types.WasmKeeperExpected) { + k.wasmKeeper = wk +} + +// SendToCallbackModule sends coins from the sender to the x/callback module account. +func (k Keeper) SendToCallbackModule(ctx sdk.Context, sender string, amount sdk.Coin) error { + senderAddr, err := sdk.AccAddressFromBech32(sender) + if err != nil { + return err + } + return k.bankKeeper.SendCoinsFromAccountToModule(ctx, senderAddr, types.ModuleName, sdk.NewCoins(amount)) +} + +// RefundFromCallbackModule sends coins from the x/callback module account to the recipient. +func (k Keeper) RefundFromCallbackModule(ctx sdk.Context, recipient string, amount sdk.Coin) error { + recipientAddr, err := sdk.AccAddressFromBech32(recipient) + if err != nil { + return err + } + if k.bankKeeper.BlockedAddr(recipientAddr) { // blocked accounts cant receive funds. so in that case we send to fee collector + return k.SendToFeeCollector(ctx, amount) + } + return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, recipientAddr, sdk.NewCoins(amount)) +} + +// SendToFeeCollector sends coins from the x/callback module account to the fee collector account. +func (k Keeper) SendToFeeCollector(ctx sdk.Context, amount sdk.Coin) error { + return k.bankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, authTypes.FeeCollectorName, sdk.NewCoins(amount)) +} diff --git a/x/callback/keeper/keeper_test.go b/x/callback/keeper/keeper_test.go new file mode 100644 index 0000000..8d5b922 --- /dev/null +++ b/x/callback/keeper/keeper_test.go @@ -0,0 +1,23 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + e2eTesting "github.com/dymensionxyz/rollapp-wasm/e2e/testing" +) + +type KeeperTestSuite struct { + suite.Suite + + chain *e2eTesting.TestChain +} + +func (s *KeeperTestSuite) SetupTest() { + s.chain = e2eTesting.NewTestChain(s.T(), 1) +} + +func TestCallbackKeeper(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} diff --git a/x/callback/keeper/msg_server.go b/x/callback/keeper/msg_server.go new file mode 100644 index 0000000..f4de59d --- /dev/null +++ b/x/callback/keeper/msg_server.go @@ -0,0 +1,130 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +var _ types.MsgServer = (*MsgServer)(nil) + +// MsgServer implements the module gRPC messaging service. +type MsgServer struct { + keeper Keeper +} + +// NewMsgServer creates a new gRPC messaging server. +func NewMsgServer(keeper Keeper) *MsgServer { + return &MsgServer{ + keeper: keeper, + } +} + +// CancelCallback implements types.MsgServer. +func (s MsgServer) CancelCallback(c context.Context, request *types.MsgCancelCallback) (*types.MsgCancelCallbackResponse, error) { + if request == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + ctx := sdk.UnwrapSDKContext(c) + + // If a callback with same job id does not exist, return error + callback, err := s.keeper.GetCallback(ctx, request.CallbackHeight, request.ContractAddress, request.JobId) + if err != nil { + return nil, errorsmod.Wrap(types.ErrCallbackNotFound, "callback with given job id does not exist for given height") + } + + // Deleting the callback from state + err = s.keeper.DeleteCallback(ctx, request.Sender, callback) + if err != nil { + return nil, err + } + + // Returning the transaction fees + surplus fees as the callback was never executed + refundFees := callback.FeeSplit.TransactionFees.Add(*callback.FeeSplit.SurplusFees) + err = s.keeper.RefundFromCallbackModule(ctx, request.Sender, refundFees) + if err != nil { + return nil, err + } + + // Sending the reservation fees to fee collector + reservationFees := callback.FeeSplit.BlockReservationFees.Add(*callback.FeeSplit.FutureReservationFees) + err = s.keeper.SendToFeeCollector(ctx, reservationFees) + if err != nil { + return nil, err + } + + // Emit event + types.EmitCallbackCancelledEvent( + ctx, + request.ContractAddress, + request.JobId, + request.CallbackHeight, + request.Sender, + refundFees, + ) + + return &types.MsgCancelCallbackResponse{ + Refund: refundFees, + }, nil +} + +// RequestCallback implements types.MsgServer. +func (s MsgServer) RequestCallback(c context.Context, request *types.MsgRequestCallback) (*types.MsgRequestCallbackResponse, error) { + if request == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + ctx := sdk.UnwrapSDKContext(c) + + // Get the expected fees which is to be paid + futureReservationFee, blockReservationFee, transactionFee, err := s.keeper.EstimateCallbackFees(ctx, request.CallbackHeight) + if err != nil { + return nil, err + } + expectedFees := transactionFee.Add(blockReservationFee).Add(futureReservationFee) + + // If the fees sent by the sender is less than the expected fees, return error + if request.Fees.IsLT(expectedFees) { + return nil, errorsmod.Wrapf(types.ErrInsufficientFees, "expected %s, got %s", expectedFees, request.Fees) + } + surplusFees := request.Fees.Sub(expectedFees) // Calculating any surplus user has sent + + // Save the callback in state + callback := types.NewCallback( + request.Sender, + request.ContractAddress, + request.CallbackHeight, + request.JobId, + transactionFee, + blockReservationFee, + futureReservationFee, + surplusFees, + ) + err = s.keeper.SaveCallback(ctx, callback) + if err != nil { + return nil, err + } + + // Send the fees into module account + err = s.keeper.SendToCallbackModule(ctx, request.Sender, request.Fees) + if err != nil { + return nil, err + } + + // Emit event + types.EmitCallbackRegisteredEvent( + ctx, + request.ContractAddress, + request.JobId, + request.CallbackHeight, + callback.FeeSplit, + request.Sender, + ) + + return &types.MsgRequestCallbackResponse{}, nil +} diff --git a/x/callback/keeper/msg_server_test.go b/x/callback/keeper/msg_server_test.go new file mode 100644 index 0000000..5a0bbe6 --- /dev/null +++ b/x/callback/keeper/msg_server_test.go @@ -0,0 +1,218 @@ +package keeper_test + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + e2eTesting "github.com/dymensionxyz/rollapp-wasm/e2e/testing" + "github.com/dymensionxyz/rollapp-wasm/pkg/testutils" + callbackKeeper "github.com/dymensionxyz/rollapp-wasm/x/callback/keeper" + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +func (s *KeeperTestSuite) TestRequestCallback() { + // Setting up chain and contract in mock wasm keeper + ctx, keeper := s.chain.GetContext().WithBlockHeight(101), s.chain.GetApp().CallbackKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + contractAddr := e2eTesting.GenContractAddresses(1)[0] + contractAdminAcc := s.chain.GetAccount(2) + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + contractAdminBalance := s.chain.GetBalance(contractAdminAcc.Address) + + msgServer := callbackKeeper.NewMsgServer(keeper) + + testCases := []struct { + testCase string + input func() *types.MsgRequestCallback + expectError bool + errorType error + }{ + { + testCase: "FAIL: empty request", + input: func() *types.MsgRequestCallback { + return nil + }, + expectError: true, + errorType: status.Error(codes.InvalidArgument, "empty request"), + }, + { + testCase: "OK: successfully register callback with 0 fees", + input: func() *types.MsgRequestCallback { + return &types.MsgRequestCallback{ + ContractAddress: contractAddr.String(), + JobId: 1, + CallbackHeight: 102, + Sender: contractAddr.String(), + Fees: sdk.NewInt64Coin("stake", 0), + } + }, + expectError: false, + errorType: nil, + }, + { + testCase: "FAIL: contract does not exist", + input: func() *types.MsgRequestCallback { + return &types.MsgRequestCallback{ + ContractAddress: contractAdminAcc.Address.String(), + JobId: 1, + CallbackHeight: 102, + Sender: contractAddr.String(), + Fees: sdk.NewInt64Coin("stake", 100000000), + } + }, + expectError: true, + errorType: types.ErrContractNotFound, + }, + { + testCase: "OK: successfully register callback", + input: func() *types.MsgRequestCallback { + return &types.MsgRequestCallback{ + ContractAddress: contractAddr.String(), + JobId: 1, + CallbackHeight: 120, + Sender: contractAdminAcc.Address.String(), + Fees: sdk.NewInt64Coin("stake", 100000000), + } + }, + expectError: false, + errorType: nil, + }, + } + for _, tc := range testCases { + s.Run(fmt.Sprintf("Case: %s", tc.testCase), func() { + req := tc.input() + res, err := msgServer.RequestCallback(sdk.WrapSDKContext(ctx), req) + if tc.expectError { + s.Require().Error(err) + s.Assert().ErrorIs(err, tc.errorType) + } else { + s.Require().NoError(err) + s.Require().Equal(&types.MsgRequestCallbackResponse{}, res) + // Ensuring the callback exists now + exists, err := keeper.ExistsCallback(ctx, req.CallbackHeight, req.ContractAddress, req.JobId) + s.Require().NoError(err) + s.Require().True(exists) + // Ensure account balance has been updated + contractAdminBalance = contractAdminBalance.Sub(req.Fees) + if req.Sender == contractAddr.String() { + s.Require().True(contractAdminBalance.IsEqual(s.chain.GetBalance(contractAdminAcc.Address))) + } else { + s.Require().True(contractAdminBalance.IsEqual(s.chain.GetBalance(sdk.MustAccAddressFromBech32(req.Sender)))) + } + } + }) + } +} + +func (s *KeeperTestSuite) TestCancelCallback() { + // Setting up chain and contract in mock wasm keeper + ctx, keeper := s.chain.GetContext().WithBlockHeight(102), s.chain.GetApp().CallbackKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + contractAddr := e2eTesting.GenContractAddresses(1)[0] + contractAdminAcc := s.chain.GetAccount(2) + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + + msgServer := callbackKeeper.NewMsgServer(keeper) + // Setting up an existing callback to delete + reqMsg := &types.MsgRequestCallback{ + ContractAddress: contractAddr.String(), + JobId: 1, + CallbackHeight: 130, + Sender: contractAdminAcc.Address.String(), + Fees: sdk.NewInt64Coin("stake", 100), + } + _, err := msgServer.RequestCallback(sdk.WrapSDKContext(ctx), reqMsg) + s.Require().NoError(err) + callback, err := keeper.GetCallback(ctx, reqMsg.CallbackHeight, reqMsg.ContractAddress, reqMsg.JobId) + s.Require().NoError(err) + senderBalance := s.chain.GetBalance(sdk.MustAccAddressFromBech32(callback.ReservedBy)) + + testCases := []struct { + testCase string + input func() *types.MsgCancelCallback + expectError bool + errorType error + }{ + { + testCase: "FAIL: empty request", + input: func() *types.MsgCancelCallback { + return nil + }, + expectError: true, + errorType: status.Error(codes.InvalidArgument, "empty request"), + }, + { + testCase: "FAIL: callback does not exist", + input: func() *types.MsgCancelCallback { + return &types.MsgCancelCallback{ + ContractAddress: contractAddr.String(), + JobId: 2, + CallbackHeight: 130, + Sender: contractAdminAcc.Address.String(), + } + }, + expectError: true, + errorType: types.ErrCallbackNotFound, + }, + { + testCase: "FAIL: sender is not authorized to cancel callback", + input: func() *types.MsgCancelCallback { + return &types.MsgCancelCallback{ + ContractAddress: contractAddr.String(), + JobId: 1, + CallbackHeight: 130, + Sender: s.chain.GetAccount(3).Address.String(), + } + }, + expectError: true, + errorType: types.ErrUnauthorized, + }, + { + testCase: "OK: successfully cancel callback", + input: func() *types.MsgCancelCallback { + return &types.MsgCancelCallback{ + ContractAddress: contractAddr.String(), + JobId: 1, + CallbackHeight: 130, + Sender: contractAdminAcc.Address.String(), + } + }, + expectError: false, + errorType: nil, + }, + } + + for _, tc := range testCases { + s.Run(fmt.Sprintf("Case: %s", tc.testCase), func() { + req := tc.input() + res, err := msgServer.CancelCallback(sdk.WrapSDKContext(ctx), req) + if tc.expectError { + s.Require().Error(err) + s.Assert().ErrorIs(err, tc.errorType) + } else { + s.Require().NoError(err) + // Ensuring the callback no longer exists + exists, err := keeper.ExistsCallback(ctx, req.CallbackHeight, req.ContractAddress, req.JobId) + s.Require().NoError(err) + s.Require().False(exists) + // Ensuring the refund amount matches expected amount + refundAmount := callback.FeeSplit.TransactionFees.Add(*callback.FeeSplit.SurplusFees) + s.Require().Equal(refundAmount, res.Refund) + // Ensuring the sender's balance has been updated + senderBalance = senderBalance.Add(refundAmount) + s.Require().Equal(senderBalance, s.chain.GetBalance(sdk.MustAccAddressFromBech32(req.Sender))) + } + }) + } +} diff --git a/x/callback/keeper/params.go b/x/callback/keeper/params.go new file mode 100644 index 0000000..dd50e2b --- /dev/null +++ b/x/callback/keeper/params.go @@ -0,0 +1,17 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +// GetParams return all module parameters. +func (k Keeper) GetParams(ctx sdk.Context) (params types.Params, err error) { + return k.Params.Get(ctx) +} + +// SetParams sets all module parameters. +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) error { + return k.Params.Set(ctx, params) +} diff --git a/x/callback/module.go b/x/callback/module.go new file mode 100644 index 0000000..d2f24c4 --- /dev/null +++ b/x/callback/module.go @@ -0,0 +1,150 @@ +package callback + +import ( + "context" + "encoding/json" + "fmt" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codecTypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + + "github.com/dymensionxyz/rollapp-wasm/x/callback/client/cli" + "github.com/dymensionxyz/rollapp-wasm/x/callback/keeper" + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +var ( + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModule = AppModule{} +) + +// AppModuleBasic defines the basic application module for this module. +type AppModuleBasic struct { + cdc codec.Codec +} + +// Name returns the module's name. +func (a AppModuleBasic) Name() string { + return types.ModuleName +} + +// QuerierRoute returns the capability module's query routing key. +func (AppModule) QuerierRoute() string { + return types.QuerierRoute +} + +// RegisterLegacyAminoCodec registers the module's types on the given LegacyAmino codec. +func (a AppModuleBasic) RegisterLegacyAminoCodec(amino *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(amino) +} + +// RegisterInterfaces registers the module's interface types. +func (a AppModuleBasic) RegisterInterfaces(registry codecTypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) +} + +// DefaultGenesis returns default genesis state as raw bytes for the module. +func (a AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesis()) +} + +// ValidateGenesis performs genesis state validation for the module. +func (a AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error { + var state types.GenesisState + if err := cdc.UnmarshalJSON(bz, &state); err != nil { + return fmt.Errorf("failed to unmarshal x/%s genesis state: %w", types.ModuleName, err) + } + + return state.Validate() +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module. +func (a AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, serveMux *runtime.ServeMux) { + if err := types.RegisterQueryHandlerClient(context.Background(), serveMux, types.NewQueryClient(clientCtx)); err != nil { + panic(fmt.Errorf("registering query handler for x/%s: %w", types.ModuleName, err)) + } +} + +// GetTxCmd returns the root tx command for the module. +func (a AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.GetTxCmd() +} + +// GetQueryCmd returns no root query command for the module. +func (a AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// AppModule implements an application module for this module. +type AppModule struct { + AppModuleBasic + + keeper keeper.Keeper + wasmKeeper types.WasmKeeperExpected + errorsKeeper types.ErrorsKeeperExpected +} + +// NewAppModule creates a new AppModule object. +func NewAppModule(cdc codec.Codec, keeper keeper.Keeper, wk types.WasmKeeperExpected, ek types.ErrorsKeeperExpected) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{cdc: cdc}, + keeper: keeper, + wasmKeeper: wk, + errorsKeeper: ek, + } +} + +// RegisterInvariants registers the module invariants. +func (a AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { +} + +// RegisterServices registers the module services. +func (a AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServer(a.keeper)) + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServer(a.keeper)) +} + +// InitGenesis performs genesis initialization for the module. It returns no validator updates. +func (a AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, bz json.RawMessage) []abci.ValidatorUpdate { + var genesisState types.GenesisState + cdc.MustUnmarshalJSON(bz, &genesisState) + + InitGenesis(ctx, a.keeper, genesisState) + + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the exported genesis state as raw bytes for the module. +func (a AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { + state := ExportGenesis(ctx, a.keeper) + return cdc.MustMarshalJSON(state) +} + +// ConsensusVersion implements AppModule/ConsensusVersion. +func (a AppModule) ConsensusVersion() uint64 { + return 1 +} + +// BeginBlock returns the begin blocker for the module. +func (a AppModule) BeginBlock(ctx sdk.Context, block abci.RequestBeginBlock) {} + +// EndBlock returns the end blocker for the module. It returns no validator updates. +func (a AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return EndBlocker(ctx, a.keeper, a.wasmKeeper, a.errorsKeeper) +} + +// LegacyQuerierHandler returns the capability module's Querier. +func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return nil +} + +// Route returns the capability module's message routing key. +func (am AppModule) Route() sdk.Route { + return sdk.Route{} +} \ No newline at end of file diff --git a/x/callback/spec/01_state.md b/x/callback/spec/01_state.md new file mode 100644 index 0000000..7fdf1a3 --- /dev/null +++ b/x/callback/spec/01_state.md @@ -0,0 +1,23 @@ +# State + +Section describes all stored by the module objects and their storage keys. + +Refer to the [callback.proto](../../../proto/rollapp/callback/v1/callback.proto) for objects fields description. + +## Params + +[Params](../../../proto/rollapp/callback/v1/callback.proto#L38) object is used to store the module params. + +The params value can only be updated by x/gov module via a governance upgrade proposal. + +Storage keys: +* Params: `ParamsKey -> ProtocolBuffer(Params)` + +## Callback + +[Callback](../../../proto/rollapp/callback/v1/callback.proto#L12) object is used to store the callbacks which are registered. + +The callbacks are pruned after they are executed. + +Storage keys: +* Callback: `CallbacksKey | BlockHeight | ContractAddress | JobID -> ProtocolBuffer(Callback)` \ No newline at end of file diff --git a/x/callback/spec/02_messages.md b/x/callback/spec/02_messages.md new file mode 100644 index 0000000..ebadf9c --- /dev/null +++ b/x/callback/spec/02_messages.md @@ -0,0 +1,38 @@ +# Messages + +Section describes the processing of the module messages + +## MsgRequestCallback + +A new callback can be registered by using the [MsgRequestCallback](../../../proto/rollapp/callback/v1/tx.proto#L39) message. + +On success: +* A callback is queued to be executed at the given height. +* The fee amount specified is transferred from the sender's account to the module account + +This message is expected to fail if: +* Insufficient fees are sent +* The account has insufficient balance +* The contract with given address does not exist +* A callback with at given height for specified height with given job id already exists +* The callback request height is in the past or in the current block +* The sender is not authorized to request a callback. The callback can only be request by the following + * The contract itself + * The contract admin as set in the x/wasmd module + * The contract owner as set in the x/rewards module + +## MsgCancelCallback + +An existing callback can be cancelled by using th [MsgCancelCallback](../../../proto/rollapp/callback/v1/tx.proto#L58) message, + +On success: +* The exisiting callback is removed from the execution queue. +* The txFee and surplusFee amount is refunded back to the sender. +* The rest of the fees are sent to fee_collector to be distributed to validators and stakers + +This message is expected to fail if: +* Callback with specified block height, contract address and job id does not exist +* The sender is not authorized to cancel the callback. The callback can only be cancelled by the following + * The contract itself + * The contract admin as set in the x/wasmd module + * The contract owner as set in the x/rewards module \ No newline at end of file diff --git a/x/callback/spec/03_end_block.md b/x/callback/spec/03_end_block.md new file mode 100644 index 0000000..0c23b92 --- /dev/null +++ b/x/callback/spec/03_end_block.md @@ -0,0 +1,33 @@ +# End Block + +Section describes the module state changes on the ABCI end block call + +## Callback Execution + +Every end block we iterate over all the callbacks registered at that height. For each of the registered callback we, + +1. Create a CallbackMsg + + It is a json encoded msg which includes the job id and is sent to the contract + +2. Execute the callback + + A new sdk context is used with a limited gas meter. The gas limit is set to the value of the module param [CallbackGasLimit](../../../proto/rollapp/callback/v1/callback.proto). Execute using the Sudo entrypoint and track the amount of gasUsed and errors, if any. + +3. Handle error + + If there was any error during the execution of the callback, whether from the contract returning an error, or an out of gas error, set the error with the [x/cwerrors](../../cwerrors/spec/README.md) module with the appropriate error code. + + If the callback was successfull, throw a success event. + +4. Calculate tx fees + + Based on the gas used, calculate the transaction fees for the executed callback. If the calculated fee is less than what was paid, refund the surplus to the address which registered the callback. + +5. Distribute fees + + The consumed tx fees and all the other fees are sent to the fee collector to be distributed to the validators and stakers. + +6. Cleanup + + Remove the callback entry from the state \ No newline at end of file diff --git a/x/callback/spec/04_events.md b/x/callback/spec/04_events.md new file mode 100644 index 0000000..3ff6b7f --- /dev/null +++ b/x/callback/spec/04_events.md @@ -0,0 +1,12 @@ +# Events + +Section describes the module events + +The module emits the following proto-events + +| Source type | Source name | Protobuf reference | +| ----------- | -------------------- |--------------------------------------------------------------------------------------| +| Message | `MsgRequestCallback` | [CallbackRegisteredEvent](../../../proto/rollapp/callback/v1/events.proto#L11) | +| Message | `MsgCancelCallback` | [CallbackCancelledEvent](../../../proto/rollapp/callback/v1/events.proto#L25) | +| Module | `EndBlocker` | [CallbackExecutedSuccessEvent](../../../proto/rollapp/callback/v1/events.proto#L39) | +| Module | `EndBlocker` | [CallbackExecutedFailedEvent](../../../proto/rollapp/callback/v1/events.proto#L53) | \ No newline at end of file diff --git a/x/callback/spec/05_client.md b/x/callback/spec/05_client.md new file mode 100644 index 0000000..7e356e5 --- /dev/null +++ b/x/callback/spec/05_client.md @@ -0,0 +1,132 @@ +# Client + +Section describes interaction with the module by the user + +## CLI + +### Query + +The `query` commands alllows a user to query the module state + +Use the `-h`/`--help` flag to get a help description of a command. + +`rollapp-wasm q callback -h` + +> You can add the `-o json` for the JSON output format + +#### params + +Get the current module parameters + +Usage: + +`rollapp-wasm q callback params [flags]` + +Example output: + +```yaml +block_reservation_fee_multiplier: "1.000000000000000000" +callback_gas_limit: "1000000" +future_reservation_fee_multiplier: "1.000000000000000000" +max_block_reservation_limit: "3" +max_future_reservation_limit: "10000" +``` + +#### callbacks + +List all the callbacks for the given height + +Usage: + +`rollapp-wasm q callback callbacks [block-height]` + +Example: + +`rollapp-wasm q callback callbacks 1234` + +Example output: + +```yaml +callbacks: +- callback_height: "400" + contract_address: cosmos1wug8sewp6cedgkmrmvhl3lf3tulagm9hnvy8p0rppz9yjw0g4wtqukxvuk + fee_split: + block_reservation_fees: + amount: "1" + denom: stake + future_reservation_fees: + amount: "80" + denom: stake + surplus_fees: + amount: "88" + denom: stake + transaction_fees: + amount: "0" + denom: stake + job_id: "5" + reserved_by: cosmos1x394ype3x8nt9wz0j78m8c8kcezpslrcnvs6ef +``` + +#### estimate-callback-fees + +Estimate the minimum fees to be paid to register a callback based on the requested height + +Usage: + +`rollapp-wasm q callback estimate-callback-fees [block-height]` + +Example: + +`rollapp-wasm q calback estimate-callback-fees 1234` + +Example output: + +```yaml +fee_split: + block_reservation_fees: + amount: "2000" + denom: stake + future_reservation_fees: + amount: "1000" + denom: stake + surplus_fees: null + transaction_fees: + amount: "5000" + denom: stake +total_fees: + amount: "7000" + denom: stake +``` + +### TX + +The `tx` commands allows a user to interact with the module. + +Use the `-h`/`--help` flag to get a help description of a command. + +`rollapp-wasm tx callback -h` + +#### request-callback + +Create a new callback for the given contract at specified height and given job id by paying the mentioned fees + +Usage: + +`rollapp-wasm tx callback request-callback [contract-address] [job-id] [callback-height] [fee-amount] [flags]` + +Example: + +`rollapp-wasm tx callback request-callback cosmos1wug8sewp6cedgkmrmvhl3 +lf3tulagm9hnvy8p0rppz9yjw0g4wtqukxvuk 1 1234 7000stake --from myAccountKey` + +#### cancel-callback + +Cancel an existing callback for the given contract at specified height and given job id + +Usage: + +`rollapp-wasm tx callback cancel-callback [contract-address] [job-id] [callback-height] [flags]` + +Example: + +`rollapp-wasm tx callback cancel-callback cosmos1wug8sewp6cedgkmrmvhl3 1 1234 --from myAccountKey` \ No newline at end of file diff --git a/x/callback/spec/06_wasm_bindings.md b/x/callback/spec/06_wasm_bindings.md new file mode 100644 index 0000000..c0c4c2b --- /dev/null +++ b/x/callback/spec/06_wasm_bindings.md @@ -0,0 +1,31 @@ +# Wasm Bindings + +The only custom binding the module has is in the callback message which is sent to the contract during the execution of the callback. + +```go +// SudoMsg callback message sent to a contract. +// This is encoded as JSON input to the contract when executing the callback +type SudoMsg struct { + // Callback is the endpoint name at the contract which is called + Callback *CallbackMsg `json:"callback,omitempty"` +} + +// CallbackMsg is the callback message sent to a contract. +type CallbackMsg struct { + // JobID is the user specified job id + JobID uint64 `json:"job_id"` +} +``` + +The above struct is converted into the json encoded string in the following way. +```json +{"callback":{"job_id":1}} +``` + +## Requesting Callback + +The contract can request a callback by using proto msg [MsgRequestCallback](./02_messages.md#msgrequestcallback) + +## Cancelling Callback + +The contract can cancel an existing callback by using proto msg [MsgCancelCallback](./02_messages.md#msgcancelcallback) \ No newline at end of file diff --git a/x/callback/spec/07_errors.md b/x/callback/spec/07_errors.md new file mode 100644 index 0000000..61f67e7 --- /dev/null +++ b/x/callback/spec/07_errors.md @@ -0,0 +1,14 @@ +# Errors + +The module exposes the following error codes which are used with the x/cwerrors module in case of callback failures. + +```proto +enum ModuleErrors { + // ERR_UNKNOWN is the default error code + ERR_UNKNOWN = 0; + // ERR_OUT_OF_GAS is the error code when the contract callback exceeds the gas limit allowed by the module + ERR_OUT_OF_GAS = 1; + // ERR_CONTRACT_EXECUTION_FAILED is the error code when the contract callback execution fails + ERR_CONTRACT_EXECUTION_FAILED = 2; +} +``` \ No newline at end of file diff --git a/x/callback/spec/README.md b/x/callback/spec/README.md new file mode 100644 index 0000000..e3f5867 --- /dev/null +++ b/x/callback/spec/README.md @@ -0,0 +1,104 @@ +# Callback + +This module enables CosmWasm based smart contracts to receive callbacks at the end of a desired block. This is useful for scheduling actions to happen at an expected time by reserving execution in advance. + +## Concepts + +Callbacks are an intent submitted by a smart contract or a contract admin or a contract owner(as set in x/rewards), which requests the protocol to execute an endpoint on the given contract for the desired height. The data structure of a callback can be found at [callback.proto](../../../proto/rollapp/callback/v1/callback.proto#L12). + +The authorized user can register a callback by providing the following: +1. Contract Address - The address of the contract which will receive the callback. +2. Job ID - User given number which can be used by the contract to handle different callbacks with custom logic. +3. Callback Height - The height at which the callback will be executed. +4. Fees - The total fees paid to successfully register a callback. [More](#fees) + +### Fees + +There are three types of fees that need to be paid to register a callback. + +$fees = txFee + blockFee + futureFee$ + +where, +* fees is the total amount of fees to be paid to register a callback +* txFee is the transaction fee. [More](#1-transaction-fees) +* blockFee is the block reservation fee. [More](#2-block-reservation-fee) +* futureFee is the future reservation fee. [More](#3-future-reservation-fee) + +#### 1. Transaction Fees +As the callbacks are executed by the protocol, the computation is subsidized by the validators. To ensure that the validators receive fair compensation, the transaction fees are paid upfront when registering a callback. As the gas consumption of the callback is not known at registration time, the user has to overpay for the callback. However, post completion of callback execution, any extra tx fee is refunded. + +$txFee = callbackGasLimit_{params} \times estimateFees(1)$ + +where, +* txFee is the total transaction fees which need to be paid +* callbackGasLimit is a module param. [More](./01_state.md) +* estimateFees is the x/rewards endpoint used to calculate the current block price of gas. [More](../../rewards/spec/07_client.md#estimate-fees) + +> **Note** +> +> The transaction fee to be paid at the time of registration is calculated based on the x/rewards [EstimateGasFees](../../../proto/rollapp/rewards/v1/query.proto#L39) endpoint for the current height. The refund is calculated based on the gas fees at the execution height. If the gas fee goes up enough in the duration that the tx fee provided during registration is not enough, the validators swallow the loss. + +#### 2. Block Reservation Fee +This part of the fee is calculated based on how many callbacks are registered at the current block. The more filled a block's callback queue is, the more expensive it is to request further callbacks in that block. + +$blockFee = count(callbacks_{currentHeight}) \times blockReservationFeeMultiplier_{params}$ + +where, +* blockFee is the block reservation fees which need to be paid +* count(callbacks) is the total number of callbacks already registered for the current block +* blockReservationFeeMultiplier is a module param. [More](./01_state.md) + +#### 3. Future Reservation Fee +This part of the fee is calculated based on how far in the future does the user want to register their callback. The further in the future it is, the more expensive it is to request a callback. + +$futureFee = (blockHeight_{callback} - blockHeight_{current}) \times futureReservationFeeMultiplier_{params}$ + +where, +* futureFee is the future reservation fees which need to be paid +* blockHeight is the respective height at the callback request and the current height +* futureReservationFeeMultiplier is a module param. [More](./01_state.md) + +> **Note** +> Any extra fees paid, is kept as surplus fee and is refunded in case the callback is cancelled. + +Post execution of a callback, all the fees are sent to the `fee_collector` account and are distributed to the validators and stakers + +## How to use in CW contract + +The callback is handled through the `sudo` entrypoint in the contract. The following Msg and endpoint needs to be implemented for the contract to be able to receive the callback. + +```rust +// msg.rs +#[cw_serde] +pub enum SudoMsg { + Callback { job_id: u64 }, +} +``` + +```rust +// contract.rs +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn sudo(_deps: DepsMut, _env: Env, msg: SudoMsg) -> Result { + match msg { + SudoMsg::Callback { job_id } => { + !unimplemented() + } + } +} +``` + +A sample contract which shows how the feature can be used can be found [here](../../../contracts/callback-test/). + +## Error Handling + +As the contracts are executed during the protocol end blocker, it is not possible to return any execution errors to the user. However, the contract can use [x/cwerrors](../../cwerrors/spec/README.md) to get the errors when they happen. + +## Contents + +1. [State](./01_state.md) +2. [Messages](./02_messages.md) +3. [End Block](./03_end_block.md) +4. [Events](./04_events.md) +5. [Client](./05_client.md) +6. [Wasm bindings](./06_wasm_bindings.md) +7. [Module Errors](./07_errors.md) diff --git a/x/callback/types/callback.go b/x/callback/types/callback.go new file mode 100644 index 0000000..f349800 --- /dev/null +++ b/x/callback/types/callback.go @@ -0,0 +1,45 @@ +package types + +import sdk "github.com/cosmos/cosmos-sdk/types" + +// NewCallback creates a new Callback instance. +func NewCallback(sender string, contractAddress string, height int64, jobID uint64, txFees sdk.Coin, blockReservationFees sdk.Coin, futureReservationFees sdk.Coin, surplusFees sdk.Coin) Callback { + return Callback{ + ContractAddress: contractAddress, + CallbackHeight: height, + JobId: jobID, + ReservedBy: sender, + FeeSplit: &CallbackFeesFeeSplit{ + TransactionFees: &txFees, + BlockReservationFees: &blockReservationFees, + FutureReservationFees: &futureReservationFees, + SurplusFees: &surplusFees, + }, + } +} + +// Validate perform object fields validation. +func (c Callback) Validate() error { + if _, err := sdk.AccAddressFromBech32(c.GetContractAddress()); err != nil { + return err + } + if _, err := sdk.AccAddressFromBech32(c.GetReservedBy()); err != nil { + return err + } + if c.GetCallbackHeight() <= 0 { + return ErrCallbackHeightNotInFuture + } + if err := c.GetFeeSplit().GetTransactionFees().Validate(); err != nil { + return err + } + if err := c.GetFeeSplit().GetBlockReservationFees().Validate(); err != nil { + return err + } + if err := c.GetFeeSplit().GetFutureReservationFees().Validate(); err != nil { + return err + } + if err := c.GetFeeSplit().GetSurplusFees().Validate(); err != nil { + return err + } + return nil +} diff --git a/x/callback/types/callback.pb.go b/x/callback/types/callback.pb.go new file mode 100644 index 0000000..054c39e --- /dev/null +++ b/x/callback/types/callback.pb.go @@ -0,0 +1,1318 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: rollapp/callback/v1/callback.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + _ "github.com/cosmos/cosmos-sdk/codec/types" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/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 + +// Callback defines the callback structure. +type Callback struct { + // contract_address is the address of the contract which is requesting the callback (bech32 encoded). + ContractAddress string `protobuf:"bytes,1,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` + // job_id is an identifier the callback requestor can pass in to identify the callback when it happens. + JobId uint64 `protobuf:"varint,2,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` + // callback_height is the height at which the callback is executed. + CallbackHeight int64 `protobuf:"varint,3,opt,name=callback_height,json=callbackHeight,proto3" json:"callback_height,omitempty"` + // fee_split is the breakdown of the fees paid by the contract to reserve the callback + FeeSplit *CallbackFeesFeeSplit `protobuf:"bytes,4,opt,name=fee_split,json=feeSplit,proto3" json:"fee_split,omitempty"` + // reserved_by is the address which reserved the callback (bech32 encoded). + ReservedBy string `protobuf:"bytes,5,opt,name=reserved_by,json=reservedBy,proto3" json:"reserved_by,omitempty"` + // callback_gas_limit is the maximum gas that can be consumed by this callback. + MaxGasLimit uint64 `protobuf:"varint,6,opt,name=max_gas_limit,json=maxGasLimit,proto3" json:"max_gas_limit,omitempty"` +} + +func (m *Callback) Reset() { *m = Callback{} } +func (m *Callback) String() string { return proto.CompactTextString(m) } +func (*Callback) ProtoMessage() {} +func (*Callback) Descriptor() ([]byte, []int) { + return fileDescriptor_13ee52054d042d69, []int{0} +} +func (m *Callback) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Callback) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Callback.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 *Callback) XXX_Merge(src proto.Message) { + xxx_messageInfo_Callback.Merge(m, src) +} +func (m *Callback) XXX_Size() int { + return m.Size() +} +func (m *Callback) XXX_DiscardUnknown() { + xxx_messageInfo_Callback.DiscardUnknown(m) +} + +var xxx_messageInfo_Callback proto.InternalMessageInfo + +func (m *Callback) GetContractAddress() string { + if m != nil { + return m.ContractAddress + } + return "" +} + +func (m *Callback) GetJobId() uint64 { + if m != nil { + return m.JobId + } + return 0 +} + +func (m *Callback) GetCallbackHeight() int64 { + if m != nil { + return m.CallbackHeight + } + return 0 +} + +func (m *Callback) GetFeeSplit() *CallbackFeesFeeSplit { + if m != nil { + return m.FeeSplit + } + return nil +} + +func (m *Callback) GetReservedBy() string { + if m != nil { + return m.ReservedBy + } + return "" +} + +func (m *Callback) GetMaxGasLimit() uint64 { + if m != nil { + return m.MaxGasLimit + } + return 0 +} + +// CallbackFeesFeeSplit is the breakdown of all the fees that need to be paid by the contract to reserve a callback +type CallbackFeesFeeSplit struct { + // transaction_fees is the transaction fees for the callback based on its gas consumption + TransactionFees *types.Coin `protobuf:"bytes,1,opt,name=transaction_fees,json=transactionFees,proto3" json:"transaction_fees,omitempty"` + // block_reservation_fees is the block reservation fees portion of the callback reservation fees + BlockReservationFees *types.Coin `protobuf:"bytes,2,opt,name=block_reservation_fees,json=blockReservationFees,proto3" json:"block_reservation_fees,omitempty"` + // future_reservation_fees is the future reservation fees portion of the callback reservation fees + FutureReservationFees *types.Coin `protobuf:"bytes,3,opt,name=future_reservation_fees,json=futureReservationFees,proto3" json:"future_reservation_fees,omitempty"` + // surplus_fees is any extra fees passed in for the registration of the callback + SurplusFees *types.Coin `protobuf:"bytes,4,opt,name=surplus_fees,json=surplusFees,proto3" json:"surplus_fees,omitempty"` +} + +func (m *CallbackFeesFeeSplit) Reset() { *m = CallbackFeesFeeSplit{} } +func (m *CallbackFeesFeeSplit) String() string { return proto.CompactTextString(m) } +func (*CallbackFeesFeeSplit) ProtoMessage() {} +func (*CallbackFeesFeeSplit) Descriptor() ([]byte, []int) { + return fileDescriptor_13ee52054d042d69, []int{1} +} +func (m *CallbackFeesFeeSplit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CallbackFeesFeeSplit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CallbackFeesFeeSplit.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 *CallbackFeesFeeSplit) XXX_Merge(src proto.Message) { + xxx_messageInfo_CallbackFeesFeeSplit.Merge(m, src) +} +func (m *CallbackFeesFeeSplit) XXX_Size() int { + return m.Size() +} +func (m *CallbackFeesFeeSplit) XXX_DiscardUnknown() { + xxx_messageInfo_CallbackFeesFeeSplit.DiscardUnknown(m) +} + +var xxx_messageInfo_CallbackFeesFeeSplit proto.InternalMessageInfo + +func (m *CallbackFeesFeeSplit) GetTransactionFees() *types.Coin { + if m != nil { + return m.TransactionFees + } + return nil +} + +func (m *CallbackFeesFeeSplit) GetBlockReservationFees() *types.Coin { + if m != nil { + return m.BlockReservationFees + } + return nil +} + +func (m *CallbackFeesFeeSplit) GetFutureReservationFees() *types.Coin { + if m != nil { + return m.FutureReservationFees + } + return nil +} + +func (m *CallbackFeesFeeSplit) GetSurplusFees() *types.Coin { + if m != nil { + return m.SurplusFees + } + return nil +} + +// Params defines the module parameters. +type Params struct { + // callback_gas_limit is the maximum gas that can be consumed by a callback. + CallbackGasLimit uint64 `protobuf:"varint,1,opt,name=callback_gas_limit,json=callbackGasLimit,proto3" json:"callback_gas_limit,omitempty"` + // max_block_reservation_limit is the maximum number of callbacks which can be registered in a given block. + MaxBlockReservationLimit uint64 `protobuf:"varint,2,opt,name=max_block_reservation_limit,json=maxBlockReservationLimit,proto3" json:"max_block_reservation_limit,omitempty"` + // max_future_reservation_limit is the maximum number of blocks in the future that a contract can request a callback in. + MaxFutureReservationLimit uint64 `protobuf:"varint,3,opt,name=max_future_reservation_limit,json=maxFutureReservationLimit,proto3" json:"max_future_reservation_limit,omitempty"` + // block_reservation_fee_multiplier is used to calculate a part of the reservation fees which will need to be paid when requesting the callback. + BlockReservationFeeMultiplier github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,4,opt,name=block_reservation_fee_multiplier,json=blockReservationFeeMultiplier,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"block_reservation_fee_multiplier"` + // future_reservation_fee_multiplier is used to calculate a part of the reservation fees which will need to be paid while requesting the callback. + FutureReservationFeeMultiplier github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,5,opt,name=future_reservation_fee_multiplier,json=futureReservationFeeMultiplier,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"future_reservation_fee_multiplier"` + // min_price_of_gas defines the minimum price for each single unit of gas in the network. + MinPriceOfGas types.Coin `protobuf:"bytes,6,opt,name=min_price_of_gas,json=minPriceOfGas,proto3" json:"min_price_of_gas"` +} + +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_13ee52054d042d69, []int{2} +} +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) GetCallbackGasLimit() uint64 { + if m != nil { + return m.CallbackGasLimit + } + return 0 +} + +func (m *Params) GetMaxBlockReservationLimit() uint64 { + if m != nil { + return m.MaxBlockReservationLimit + } + return 0 +} + +func (m *Params) GetMaxFutureReservationLimit() uint64 { + if m != nil { + return m.MaxFutureReservationLimit + } + return 0 +} + +func (m *Params) GetMinPriceOfGas() types.Coin { + if m != nil { + return m.MinPriceOfGas + } + return types.Coin{} +} + +func init() { + proto.RegisterType((*Callback)(nil), "rollapp.callback.v1.Callback") + proto.RegisterType((*CallbackFeesFeeSplit)(nil), "rollapp.callback.v1.CallbackFeesFeeSplit") + proto.RegisterType((*Params)(nil), "rollapp.callback.v1.Params") +} + +func init() { + proto.RegisterFile("rollapp/callback/v1/callback.proto", fileDescriptor_13ee52054d042d69) +} + +var fileDescriptor_13ee52054d042d69 = []byte{ + // 668 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0x41, 0x6f, 0xd3, 0x48, + 0x18, 0x8d, 0x93, 0x34, 0x6a, 0x27, 0xdb, 0x6d, 0xe4, 0x6d, 0x77, 0x9d, 0x2e, 0xb8, 0x21, 0x07, + 0x48, 0x25, 0x6a, 0x2b, 0x45, 0xdc, 0x8a, 0x10, 0x69, 0x95, 0x16, 0x09, 0xd4, 0x62, 0x6e, 0x5c, + 0xac, 0xb1, 0x3d, 0x76, 0xa7, 0xf5, 0x78, 0x2c, 0xcf, 0x38, 0x24, 0x1c, 0x91, 0x10, 0x57, 0xc4, + 0x6f, 0xe1, 0xcc, 0xb9, 0xc7, 0x8a, 0x13, 0xe2, 0x50, 0xa1, 0xf6, 0x8f, 0x20, 0xcf, 0xd8, 0x49, + 0xd4, 0x5a, 0xf4, 0xc2, 0x29, 0x93, 0xf7, 0x7d, 0xef, 0xcd, 0x9b, 0xf7, 0x8d, 0x07, 0x74, 0x13, + 0x1a, 0x86, 0x30, 0x8e, 0x4d, 0x17, 0x86, 0xa1, 0x03, 0xdd, 0x53, 0x73, 0xd4, 0x9f, 0xae, 0x8d, + 0x38, 0xa1, 0x9c, 0xaa, 0xff, 0xe4, 0x3d, 0xc6, 0x14, 0x1f, 0xf5, 0xd7, 0xdb, 0x01, 0xa5, 0x41, + 0x88, 0x4c, 0xd1, 0xe2, 0xa4, 0xbe, 0x09, 0xa3, 0x89, 0xec, 0x5f, 0x5f, 0x0d, 0x68, 0x40, 0xc5, + 0xd2, 0xcc, 0x56, 0x39, 0xaa, 0xbb, 0x94, 0x11, 0xca, 0x4c, 0x07, 0x32, 0x64, 0x8e, 0xfa, 0x0e, + 0xe2, 0xb0, 0x6f, 0xba, 0x14, 0x47, 0x79, 0xbd, 0x2d, 0xeb, 0xb6, 0x24, 0xca, 0x3f, 0xb2, 0xd4, + 0x7d, 0x5f, 0x05, 0x8b, 0xbb, 0xf9, 0xde, 0xea, 0x26, 0x68, 0xb9, 0x34, 0xe2, 0x09, 0x74, 0xb9, + 0x0d, 0x3d, 0x2f, 0x41, 0x8c, 0x69, 0x4a, 0x47, 0xe9, 0x2d, 0x59, 0x2b, 0x05, 0xfe, 0x4c, 0xc2, + 0xea, 0x1a, 0x68, 0x9c, 0x50, 0xc7, 0xc6, 0x9e, 0x56, 0xed, 0x28, 0xbd, 0xba, 0xb5, 0x70, 0x42, + 0x9d, 0xe7, 0x9e, 0xfa, 0x00, 0xac, 0x14, 0x27, 0xb1, 0x8f, 0x11, 0x0e, 0x8e, 0xb9, 0x56, 0xeb, + 0x28, 0xbd, 0x9a, 0xf5, 0x77, 0x01, 0x1f, 0x08, 0x54, 0x1d, 0x82, 0x25, 0x1f, 0x21, 0x9b, 0xc5, + 0x21, 0xe6, 0x5a, 0xbd, 0xa3, 0xf4, 0x9a, 0xdb, 0x9b, 0x46, 0x49, 0x18, 0x46, 0x61, 0x6e, 0x88, + 0x10, 0x1b, 0x22, 0xf4, 0x3a, 0x23, 0x58, 0x8b, 0x7e, 0xbe, 0x52, 0x37, 0x40, 0x33, 0x41, 0x0c, + 0x25, 0x23, 0xe4, 0xd9, 0xce, 0x44, 0x5b, 0x10, 0x6e, 0x41, 0x01, 0x0d, 0x26, 0x6a, 0x17, 0x2c, + 0x13, 0x38, 0xb6, 0x03, 0xc8, 0xec, 0x10, 0x13, 0xcc, 0xb5, 0x86, 0xf0, 0xdb, 0x24, 0x70, 0xbc, + 0x0f, 0xd9, 0x8b, 0x0c, 0xea, 0x7e, 0xad, 0x82, 0xd5, 0xb2, 0x7d, 0xd4, 0x3d, 0xd0, 0xe2, 0x09, + 0x8c, 0x18, 0x74, 0x39, 0xa6, 0x91, 0xed, 0x23, 0x24, 0x03, 0x69, 0x6e, 0xb7, 0x8d, 0x3c, 0xc6, + 0x2c, 0x73, 0x23, 0xcf, 0xdc, 0xd8, 0xa5, 0x38, 0xb2, 0x56, 0xe6, 0x28, 0x99, 0x9a, 0x7a, 0x08, + 0xfe, 0x75, 0x42, 0xea, 0x9e, 0xda, 0xd2, 0x16, 0x9c, 0x69, 0x55, 0x6f, 0xd3, 0x5a, 0x15, 0x44, + 0x6b, 0xc6, 0x13, 0x82, 0xaf, 0xc0, 0x7f, 0x7e, 0xca, 0xd3, 0x04, 0xdd, 0x54, 0xac, 0xdd, 0xa6, + 0xb8, 0x26, 0x99, 0xd7, 0x25, 0x77, 0xc0, 0x5f, 0x2c, 0x4d, 0xe2, 0x30, 0x65, 0x52, 0xa7, 0x7e, + 0x9b, 0x4e, 0x33, 0x6f, 0xcf, 0xd8, 0xdd, 0xcf, 0x75, 0xd0, 0x38, 0x82, 0x09, 0x24, 0x4c, 0x7d, + 0x08, 0xd4, 0xe9, 0x0d, 0x98, 0x85, 0xae, 0x88, 0xd0, 0x5b, 0x45, 0xa5, 0x48, 0x5e, 0x7d, 0x02, + 0xfe, 0xcf, 0xa6, 0x73, 0x33, 0x1e, 0x49, 0x93, 0x77, 0x4b, 0x23, 0x70, 0x3c, 0xb8, 0x96, 0x83, + 0xa4, 0x3f, 0x05, 0x77, 0x32, 0x7a, 0x49, 0x18, 0x92, 0x5f, 0x13, 0xfc, 0x36, 0x81, 0xe3, 0xe1, + 0xf5, 0x53, 0x4b, 0x81, 0x0f, 0x0a, 0xe8, 0x94, 0xce, 0xc6, 0x26, 0x69, 0xc8, 0x71, 0x1c, 0x62, + 0x94, 0x88, 0x2c, 0x96, 0x06, 0x3b, 0x67, 0x17, 0x1b, 0x95, 0x1f, 0x17, 0x1b, 0xf7, 0x03, 0xcc, + 0x8f, 0x53, 0xc7, 0x70, 0x29, 0xc9, 0x3f, 0xa5, 0xfc, 0x67, 0x8b, 0x79, 0xa7, 0x26, 0x9f, 0xc4, + 0x88, 0x19, 0x7b, 0xc8, 0xfd, 0xf6, 0x65, 0x0b, 0xe4, 0xe1, 0xed, 0x21, 0xd7, 0xba, 0x5b, 0x32, + 0xc8, 0x97, 0xd3, 0x2d, 0xd4, 0x8f, 0x0a, 0xb8, 0x57, 0x3e, 0xd2, 0x79, 0x23, 0x0b, 0x7f, 0xc0, + 0x88, 0x5e, 0x36, 0xff, 0x39, 0x27, 0x07, 0xa0, 0x45, 0x70, 0x64, 0xc7, 0x09, 0x76, 0x91, 0x4d, + 0xfd, 0x6c, 0x86, 0xe2, 0x93, 0xf9, 0xdd, 0x65, 0x18, 0xd4, 0x33, 0x4b, 0xd6, 0x32, 0xc1, 0xd1, + 0x51, 0xc6, 0x3b, 0xf4, 0xf7, 0x21, 0x1b, 0x1c, 0x9e, 0x5d, 0xea, 0xca, 0xf9, 0xa5, 0xae, 0xfc, + 0xbc, 0xd4, 0x95, 0x4f, 0x57, 0x7a, 0xe5, 0xfc, 0x4a, 0xaf, 0x7c, 0xbf, 0xd2, 0x2b, 0x6f, 0x1e, + 0xcf, 0x39, 0xf7, 0x26, 0x04, 0x45, 0x0c, 0xd3, 0x68, 0x3c, 0x79, 0x67, 0xe6, 0x0f, 0xc0, 0xd6, + 0x5b, 0xc8, 0x88, 0x39, 0x9e, 0x3d, 0x9c, 0xe2, 0x30, 0x4e, 0x43, 0x3c, 0x59, 0x8f, 0x7e, 0x05, + 0x00, 0x00, 0xff, 0xff, 0xa6, 0x95, 0x31, 0xfb, 0x59, 0x05, 0x00, 0x00, +} + +func (m *Callback) 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 *Callback) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Callback) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.MaxGasLimit != 0 { + i = encodeVarintCallback(dAtA, i, uint64(m.MaxGasLimit)) + i-- + dAtA[i] = 0x30 + } + if len(m.ReservedBy) > 0 { + i -= len(m.ReservedBy) + copy(dAtA[i:], m.ReservedBy) + i = encodeVarintCallback(dAtA, i, uint64(len(m.ReservedBy))) + i-- + dAtA[i] = 0x2a + } + if m.FeeSplit != nil { + { + size, err := m.FeeSplit.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCallback(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.CallbackHeight != 0 { + i = encodeVarintCallback(dAtA, i, uint64(m.CallbackHeight)) + i-- + dAtA[i] = 0x18 + } + if m.JobId != 0 { + i = encodeVarintCallback(dAtA, i, uint64(m.JobId)) + i-- + dAtA[i] = 0x10 + } + if len(m.ContractAddress) > 0 { + i -= len(m.ContractAddress) + copy(dAtA[i:], m.ContractAddress) + i = encodeVarintCallback(dAtA, i, uint64(len(m.ContractAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CallbackFeesFeeSplit) 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 *CallbackFeesFeeSplit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CallbackFeesFeeSplit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.SurplusFees != nil { + { + size, err := m.SurplusFees.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCallback(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.FutureReservationFees != nil { + { + size, err := m.FutureReservationFees.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCallback(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.BlockReservationFees != nil { + { + size, err := m.BlockReservationFees.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCallback(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.TransactionFees != nil { + { + size, err := m.TransactionFees.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCallback(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +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 + { + size, err := m.MinPriceOfGas.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCallback(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + { + size := m.FutureReservationFeeMultiplier.Size() + i -= size + if _, err := m.FutureReservationFeeMultiplier.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintCallback(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + { + size := m.BlockReservationFeeMultiplier.Size() + i -= size + if _, err := m.BlockReservationFeeMultiplier.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintCallback(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if m.MaxFutureReservationLimit != 0 { + i = encodeVarintCallback(dAtA, i, uint64(m.MaxFutureReservationLimit)) + i-- + dAtA[i] = 0x18 + } + if m.MaxBlockReservationLimit != 0 { + i = encodeVarintCallback(dAtA, i, uint64(m.MaxBlockReservationLimit)) + i-- + dAtA[i] = 0x10 + } + if m.CallbackGasLimit != 0 { + i = encodeVarintCallback(dAtA, i, uint64(m.CallbackGasLimit)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintCallback(dAtA []byte, offset int, v uint64) int { + offset -= sovCallback(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Callback) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ContractAddress) + if l > 0 { + n += 1 + l + sovCallback(uint64(l)) + } + if m.JobId != 0 { + n += 1 + sovCallback(uint64(m.JobId)) + } + if m.CallbackHeight != 0 { + n += 1 + sovCallback(uint64(m.CallbackHeight)) + } + if m.FeeSplit != nil { + l = m.FeeSplit.Size() + n += 1 + l + sovCallback(uint64(l)) + } + l = len(m.ReservedBy) + if l > 0 { + n += 1 + l + sovCallback(uint64(l)) + } + if m.MaxGasLimit != 0 { + n += 1 + sovCallback(uint64(m.MaxGasLimit)) + } + return n +} + +func (m *CallbackFeesFeeSplit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.TransactionFees != nil { + l = m.TransactionFees.Size() + n += 1 + l + sovCallback(uint64(l)) + } + if m.BlockReservationFees != nil { + l = m.BlockReservationFees.Size() + n += 1 + l + sovCallback(uint64(l)) + } + if m.FutureReservationFees != nil { + l = m.FutureReservationFees.Size() + n += 1 + l + sovCallback(uint64(l)) + } + if m.SurplusFees != nil { + l = m.SurplusFees.Size() + n += 1 + l + sovCallback(uint64(l)) + } + return n +} + +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.CallbackGasLimit != 0 { + n += 1 + sovCallback(uint64(m.CallbackGasLimit)) + } + if m.MaxBlockReservationLimit != 0 { + n += 1 + sovCallback(uint64(m.MaxBlockReservationLimit)) + } + if m.MaxFutureReservationLimit != 0 { + n += 1 + sovCallback(uint64(m.MaxFutureReservationLimit)) + } + l = m.BlockReservationFeeMultiplier.Size() + n += 1 + l + sovCallback(uint64(l)) + l = m.FutureReservationFeeMultiplier.Size() + n += 1 + l + sovCallback(uint64(l)) + l = m.MinPriceOfGas.Size() + n += 1 + l + sovCallback(uint64(l)) + return n +} + +func sovCallback(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozCallback(x uint64) (n int) { + return sovCallback(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Callback) 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 ErrIntOverflowCallback + } + 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: Callback: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Callback: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCallback + } + 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 ErrInvalidLengthCallback + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCallback + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContractAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field JobId", wireType) + } + m.JobId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCallback + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.JobId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CallbackHeight", wireType) + } + m.CallbackHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCallback + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CallbackHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeeSplit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCallback + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCallback + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCallback + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.FeeSplit == nil { + m.FeeSplit = &CallbackFeesFeeSplit{} + } + if err := m.FeeSplit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ReservedBy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCallback + } + 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 ErrInvalidLengthCallback + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCallback + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ReservedBy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxGasLimit", wireType) + } + m.MaxGasLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCallback + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxGasLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipCallback(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCallback + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CallbackFeesFeeSplit) 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 ErrIntOverflowCallback + } + 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: CallbackFeesFeeSplit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CallbackFeesFeeSplit: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TransactionFees", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCallback + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCallback + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCallback + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TransactionFees == nil { + m.TransactionFees = &types.Coin{} + } + if err := m.TransactionFees.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockReservationFees", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCallback + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCallback + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCallback + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BlockReservationFees == nil { + m.BlockReservationFees = &types.Coin{} + } + if err := m.BlockReservationFees.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FutureReservationFees", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCallback + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCallback + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCallback + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.FutureReservationFees == nil { + m.FutureReservationFees = &types.Coin{} + } + if err := m.FutureReservationFees.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SurplusFees", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCallback + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCallback + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCallback + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SurplusFees == nil { + m.SurplusFees = &types.Coin{} + } + if err := m.SurplusFees.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCallback(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCallback + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +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 ErrIntOverflowCallback + } + 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 != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CallbackGasLimit", wireType) + } + m.CallbackGasLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCallback + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CallbackGasLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxBlockReservationLimit", wireType) + } + m.MaxBlockReservationLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCallback + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxBlockReservationLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxFutureReservationLimit", wireType) + } + m.MaxFutureReservationLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCallback + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxFutureReservationLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockReservationFeeMultiplier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCallback + } + 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 ErrInvalidLengthCallback + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCallback + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.BlockReservationFeeMultiplier.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FutureReservationFeeMultiplier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCallback + } + 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 ErrInvalidLengthCallback + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCallback + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.FutureReservationFeeMultiplier.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MinPriceOfGas", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCallback + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCallback + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCallback + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.MinPriceOfGas.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCallback(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCallback + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipCallback(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, ErrIntOverflowCallback + } + 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, ErrIntOverflowCallback + } + 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, ErrIntOverflowCallback + } + 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, ErrInvalidLengthCallback + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupCallback + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthCallback + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthCallback = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowCallback = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupCallback = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/callback/types/callback_test.go b/x/callback/types/callback_test.go new file mode 100644 index 0000000..25ba3d5 --- /dev/null +++ b/x/callback/types/callback_test.go @@ -0,0 +1,104 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + sdk "github.com/cosmos/cosmos-sdk/types" + + e2eTesting "github.com/dymensionxyz/rollapp-wasm/e2e/testing" + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +func TestCallbackValidate(t *testing.T) { + accAddrs, _ := e2eTesting.GenAccounts(1) + accAddr := accAddrs[0] + contractAddr := e2eTesting.GenContractAddresses(1)[0] + validCoin := sdk.NewInt64Coin("stake", 1) + + type testCase struct { + name string + callback types.Callback + errExpected bool + } + + testCases := []testCase{ + { + name: "Fail: Empty values", + callback: types.Callback{}, + errExpected: true, + }, + { + name: "Fail: Invalid contract address", + callback: types.Callback{ + ContractAddress: "👻", + ReservedBy: accAddr.String(), + CallbackHeight: 1, + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + errExpected: true, + }, + { + name: "Fail: Invalid reservedby address", + callback: types.Callback{ + ContractAddress: contractAddr.String(), + ReservedBy: "👻", + CallbackHeight: 1, + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + errExpected: true, + }, + { + name: "Fail: Invalid callback height", + callback: types.Callback{ + ContractAddress: contractAddr.String(), + ReservedBy: accAddr.String(), + CallbackHeight: -1, + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + errExpected: true, + }, + { + name: "OK: Valid callback", + callback: types.Callback{ + ContractAddress: contractAddr.String(), + ReservedBy: accAddr.String(), + CallbackHeight: 1, + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + errExpected: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tc.callback.Validate() + if tc.errExpected { + assert.Error(t, err) + return + } + assert.NoError(t, err) + }) + } +} diff --git a/x/callback/types/codec.go b/x/callback/types/codec.go new file mode 100644 index 0000000..d6eb553 --- /dev/null +++ b/x/callback/types/codec.go @@ -0,0 +1,38 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + cryptoCodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +// RegisterLegacyAminoCodec registers the necessary interfaces and concrete types on the provided LegacyAmino codec. +// These types are used for Amino JSON serialization. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgRequestCallback{}, "callback/MsgRequestCallback", nil) + cdc.RegisterConcrete(&MsgCancelCallback{}, "callback/MsgCancelCallback", nil) +} + +// RegisterInterfaces registers interfaces types with the interface registry. +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgRequestCallback{}, + &MsgCancelCallback{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + ModuleCdc = codec.NewAminoCodec(amino) + amino = codec.NewLegacyAmino() +) + +func init() { + RegisterLegacyAminoCodec(amino) + cryptoCodec.RegisterCrypto(amino) + sdk.RegisterLegacyAminoCodec(amino) + amino.Seal() +} diff --git a/x/callback/types/errors.go b/x/callback/types/errors.go new file mode 100644 index 0000000..0d24e96 --- /dev/null +++ b/x/callback/types/errors.go @@ -0,0 +1,31 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + + cwerrortypes "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +var ( + DefaultCodespace = ModuleName + ErrContractNotFound = errorsmod.Register(DefaultCodespace, 2, "contract with given address not found") + ErrCallbackJobIDExists = errorsmod.Register(DefaultCodespace, 3, "callback with given job id already exists for given height") + ErrCallbackHeightNotInFuture = errorsmod.Register(DefaultCodespace, 4, "callback request height is not in the future") + ErrUnauthorized = errorsmod.Register(DefaultCodespace, 5, "sender not authorized to register callback") + ErrCallbackNotFound = errorsmod.Register(DefaultCodespace, 6, "callback with given job id does not exist for given height") + ErrInsufficientFees = errorsmod.Register(DefaultCodespace, 7, "insufficient fees to register callback") + ErrCallbackExists = errorsmod.Register(DefaultCodespace, 8, "callback with given job id already exists for given height") + ErrCallbackHeightTooFarInFuture = errorsmod.Register(DefaultCodespace, 9, "callback request height is too far in the future") + ErrBlockFilled = errorsmod.Register(DefaultCodespace, 10, "block filled with max capacity of callbacks") +) + +// NewSudoError creates a new sudo error instance to pass on to the errors module +func NewSudoError(errorCode ModuleErrors, contractAddr string, inputPayload string, errMsg string) cwerrortypes.SudoError { + return cwerrortypes.SudoError{ + ModuleName: ModuleName, + ErrorCode: int32(errorCode), + ContractAddress: contractAddr, + InputPayload: inputPayload, + ErrorMessage: errMsg, + } +} diff --git a/x/callback/types/errors.pb.go b/x/callback/types/errors.pb.go new file mode 100644 index 0000000..f202092 --- /dev/null +++ b/x/callback/types/errors.pb.go @@ -0,0 +1,79 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: rollapp/callback/v1/errors.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/gogo/protobuf/proto" + math "math" +) + +// 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 + +// ModuleErrors defines the module level error codes +type ModuleErrors int32 + +const ( + // ERR_UNKNOWN is the default error code + ModuleErrors_ERR_UNKNOWN ModuleErrors = 0 + // ERR_OUT_OF_GAS is the error code when the contract callback exceeds the gas limit allowed by the module + ModuleErrors_ERR_OUT_OF_GAS ModuleErrors = 1 + // ERR_CONTRACT_EXECUTION_FAILED is the error code when the contract callback execution fails + ModuleErrors_ERR_CONTRACT_EXECUTION_FAILED ModuleErrors = 2 +) + +var ModuleErrors_name = map[int32]string{ + 0: "ERR_UNKNOWN", + 1: "ERR_OUT_OF_GAS", + 2: "ERR_CONTRACT_EXECUTION_FAILED", +} + +var ModuleErrors_value = map[string]int32{ + "ERR_UNKNOWN": 0, + "ERR_OUT_OF_GAS": 1, + "ERR_CONTRACT_EXECUTION_FAILED": 2, +} + +func (x ModuleErrors) String() string { + return proto.EnumName(ModuleErrors_name, int32(x)) +} + +func (ModuleErrors) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_4e3f2bb4f6468bc1, []int{0} +} + +func init() { + proto.RegisterEnum("rollapp.callback.v1.ModuleErrors", ModuleErrors_name, ModuleErrors_value) +} + +func init() { proto.RegisterFile("rollapp/callback/v1/errors.proto", fileDescriptor_4e3f2bb4f6468bc1) } + +var fileDescriptor_4e3f2bb4f6468bc1 = []byte{ + // 237 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x28, 0xca, 0xcf, 0xc9, + 0x49, 0x2c, 0x28, 0xd0, 0x4f, 0x4e, 0xcc, 0xc9, 0x49, 0x4a, 0x4c, 0xce, 0xd6, 0x2f, 0x33, 0xd4, + 0x4f, 0x2d, 0x2a, 0xca, 0x2f, 0x2a, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x86, 0xaa, + 0xd0, 0x83, 0xa9, 0xd0, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xcb, 0xeb, 0x83, + 0x58, 0x10, 0xa5, 0x5a, 0x61, 0x5c, 0x3c, 0xbe, 0xf9, 0x29, 0xa5, 0x39, 0xa9, 0xae, 0x60, 0x03, + 0x84, 0xf8, 0xb9, 0xb8, 0x5d, 0x83, 0x82, 0xe2, 0x43, 0xfd, 0xbc, 0xfd, 0xfc, 0xc3, 0xfd, 0x04, + 0x18, 0x84, 0x84, 0xb8, 0xf8, 0x40, 0x02, 0xfe, 0xa1, 0x21, 0xf1, 0xfe, 0x6e, 0xf1, 0xee, 0x8e, + 0xc1, 0x02, 0x8c, 0x42, 0x8a, 0x5c, 0xb2, 0x20, 0x31, 0x67, 0x7f, 0xbf, 0x90, 0x20, 0x47, 0xe7, + 0x90, 0x78, 0xd7, 0x08, 0x57, 0xe7, 0xd0, 0x10, 0x4f, 0x7f, 0xbf, 0x78, 0x37, 0x47, 0x4f, 0x1f, + 0x57, 0x17, 0x01, 0x26, 0x27, 0xff, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, + 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, + 0x32, 0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xa9, 0xcc, 0x4d, + 0xcd, 0x2b, 0xce, 0xcc, 0xcf, 0xab, 0xa8, 0xac, 0xd2, 0x87, 0x3a, 0x5a, 0xb7, 0x3c, 0xb1, 0x38, + 0x57, 0xbf, 0x02, 0xe1, 0xbb, 0x92, 0xca, 0x82, 0xd4, 0xe2, 0x24, 0x36, 0xb0, 0x7b, 0x8d, 0x01, + 0x01, 0x00, 0x00, 0xff, 0xff, 0x8a, 0xee, 0x40, 0xde, 0xfe, 0x00, 0x00, 0x00, +} diff --git a/x/callback/types/events.go b/x/callback/types/events.go new file mode 100644 index 0000000..c7bc837 --- /dev/null +++ b/x/callback/types/events.go @@ -0,0 +1,85 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func EmitCallbackRegisteredEvent( + ctx sdk.Context, + contractAddress string, + jobId uint64, + callbackHeight int64, + feeSplit *CallbackFeesFeeSplit, + reservedBy string, +) { + err := ctx.EventManager().EmitTypedEvent(&CallbackRegisteredEvent{ + ContractAddress: contractAddress, + JobId: jobId, + CallbackHeight: callbackHeight, + FeeSplit: feeSplit, + ReservedBy: reservedBy, + }) + if err != nil { + panic(fmt.Errorf("sending CallbackRegisteredEvent event: %w", err)) + } +} + +func EmitCallbackCancelledEvent( + ctx sdk.Context, + contractAddress string, + jobId uint64, + callbackHeight int64, + cancelledBy string, + refundAmount sdk.Coin, +) { + err := ctx.EventManager().EmitTypedEvent(&CallbackCancelledEvent{ + ContractAddress: contractAddress, + JobId: jobId, + CallbackHeight: callbackHeight, + CancelledBy: cancelledBy, + RefundAmount: refundAmount, + }) + if err != nil { + panic(fmt.Errorf("sending CallbackCancelledEvent event: %w", err)) + } +} + +func EmitCallbackExecutedSuccessEvent( + ctx sdk.Context, + contractAddress string, + jobId uint64, + sudoMsg string, + gasUsed uint64, +) { + err := ctx.EventManager().EmitTypedEvent(&CallbackExecutedSuccessEvent{ + ContractAddress: contractAddress, + JobId: jobId, + SudoMsg: sudoMsg, + GasUsed: gasUsed, + }) + if err != nil { + panic(fmt.Errorf("sending CallbackExecutedSuccessEvent event: %w", err)) + } +} + +func EmitCallbackExecutedFailedEvent( + ctx sdk.Context, + contractAddress string, + jobId uint64, + sudoMsg string, + gasUsed uint64, + errMsg string, +) { + err := ctx.EventManager().EmitTypedEvent(&CallbackExecutedFailedEvent{ + Error: errMsg, + ContractAddress: contractAddress, + JobId: jobId, + SudoMsg: sudoMsg, + GasUsed: gasUsed, + }) + if err != nil { + panic(fmt.Errorf("sending CallbackExecutedFailedEvent event: %w", err)) + } +} diff --git a/x/callback/types/events.pb.go b/x/callback/types/events.pb.go new file mode 100644 index 0000000..0466428 --- /dev/null +++ b/x/callback/types/events.pb.go @@ -0,0 +1,1521 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: rollapp/callback/v1/events.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/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 + +// CallbackRegisteredEvent is emitted when a callback is registered. +type CallbackRegisteredEvent struct { + // contract_address is the address of the contract for which callback is being registered (bech32 encoded). + ContractAddress string `protobuf:"bytes,1,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` + // job_id is an identifier of the callback. + JobId uint64 `protobuf:"varint,2,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` + // callback_height is the height at which the callback is executed. + CallbackHeight int64 `protobuf:"varint,3,opt,name=callback_height,json=callbackHeight,proto3" json:"callback_height,omitempty"` + // fee_split is the breakdown of the fees paid by the contract to reserve the callback + FeeSplit *CallbackFeesFeeSplit `protobuf:"bytes,4,opt,name=fee_split,json=feeSplit,proto3" json:"fee_split,omitempty"` + // reserved_by is the address which reserved the callback (bech32 encoded). + ReservedBy string `protobuf:"bytes,5,opt,name=reserved_by,json=reservedBy,proto3" json:"reserved_by,omitempty"` +} + +func (m *CallbackRegisteredEvent) Reset() { *m = CallbackRegisteredEvent{} } +func (m *CallbackRegisteredEvent) String() string { return proto.CompactTextString(m) } +func (*CallbackRegisteredEvent) ProtoMessage() {} +func (*CallbackRegisteredEvent) Descriptor() ([]byte, []int) { + return fileDescriptor_0debbd46abcdff3e, []int{0} +} +func (m *CallbackRegisteredEvent) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CallbackRegisteredEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CallbackRegisteredEvent.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 *CallbackRegisteredEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_CallbackRegisteredEvent.Merge(m, src) +} +func (m *CallbackRegisteredEvent) XXX_Size() int { + return m.Size() +} +func (m *CallbackRegisteredEvent) XXX_DiscardUnknown() { + xxx_messageInfo_CallbackRegisteredEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_CallbackRegisteredEvent proto.InternalMessageInfo + +func (m *CallbackRegisteredEvent) GetContractAddress() string { + if m != nil { + return m.ContractAddress + } + return "" +} + +func (m *CallbackRegisteredEvent) GetJobId() uint64 { + if m != nil { + return m.JobId + } + return 0 +} + +func (m *CallbackRegisteredEvent) GetCallbackHeight() int64 { + if m != nil { + return m.CallbackHeight + } + return 0 +} + +func (m *CallbackRegisteredEvent) GetFeeSplit() *CallbackFeesFeeSplit { + if m != nil { + return m.FeeSplit + } + return nil +} + +func (m *CallbackRegisteredEvent) GetReservedBy() string { + if m != nil { + return m.ReservedBy + } + return "" +} + +// CallbackCancelledEvent is emitted when a callback is cancelled. +type CallbackCancelledEvent struct { + // cancelled_by is the address of the contract whose callback is being cancelled (bech32 encoded) + CancelledBy string `protobuf:"bytes,1,opt,name=cancelled_by,json=cancelledBy,proto3" json:"cancelled_by,omitempty"` + // contract_address is the address of the contract (bech32 encoded) + ContractAddress string `protobuf:"bytes,2,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` + // job_id is an identifier the callback requestor had passed during registration of the callback + JobId uint64 `protobuf:"varint,3,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` + // callback_height is the height at which the callback requestor had registered the callback + CallbackHeight int64 `protobuf:"varint,4,opt,name=callback_height,json=callbackHeight,proto3" json:"callback_height,omitempty"` + // refund_amount is the amount of fees which was refunded on cancellation + RefundAmount types.Coin `protobuf:"bytes,5,opt,name=refund_amount,json=refundAmount,proto3" json:"refund_amount"` +} + +func (m *CallbackCancelledEvent) Reset() { *m = CallbackCancelledEvent{} } +func (m *CallbackCancelledEvent) String() string { return proto.CompactTextString(m) } +func (*CallbackCancelledEvent) ProtoMessage() {} +func (*CallbackCancelledEvent) Descriptor() ([]byte, []int) { + return fileDescriptor_0debbd46abcdff3e, []int{1} +} +func (m *CallbackCancelledEvent) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CallbackCancelledEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CallbackCancelledEvent.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 *CallbackCancelledEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_CallbackCancelledEvent.Merge(m, src) +} +func (m *CallbackCancelledEvent) XXX_Size() int { + return m.Size() +} +func (m *CallbackCancelledEvent) XXX_DiscardUnknown() { + xxx_messageInfo_CallbackCancelledEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_CallbackCancelledEvent proto.InternalMessageInfo + +func (m *CallbackCancelledEvent) GetCancelledBy() string { + if m != nil { + return m.CancelledBy + } + return "" +} + +func (m *CallbackCancelledEvent) GetContractAddress() string { + if m != nil { + return m.ContractAddress + } + return "" +} + +func (m *CallbackCancelledEvent) GetJobId() uint64 { + if m != nil { + return m.JobId + } + return 0 +} + +func (m *CallbackCancelledEvent) GetCallbackHeight() int64 { + if m != nil { + return m.CallbackHeight + } + return 0 +} + +func (m *CallbackCancelledEvent) GetRefundAmount() types.Coin { + if m != nil { + return m.RefundAmount + } + return types.Coin{} +} + +// CallbackExecutedSuccessEvent is emitted when a callback is executed successfully. +type CallbackExecutedSuccessEvent struct { + // contract_address is the address of the contract for which callback is being executed (bech32 encoded). + ContractAddress string `protobuf:"bytes,1,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` + // job_id is an identifier of the callback. + JobId uint64 `protobuf:"varint,2,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` + // sudo_msg is the input passed by the module to the contract + SudoMsg string `protobuf:"bytes,3,opt,name=sudo_msg,json=sudoMsg,proto3" json:"sudo_msg,omitempty"` + // gas_used is the amount of gas consumed during the callback execution + GasUsed uint64 `protobuf:"varint,4,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"` +} + +func (m *CallbackExecutedSuccessEvent) Reset() { *m = CallbackExecutedSuccessEvent{} } +func (m *CallbackExecutedSuccessEvent) String() string { return proto.CompactTextString(m) } +func (*CallbackExecutedSuccessEvent) ProtoMessage() {} +func (*CallbackExecutedSuccessEvent) Descriptor() ([]byte, []int) { + return fileDescriptor_0debbd46abcdff3e, []int{2} +} +func (m *CallbackExecutedSuccessEvent) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CallbackExecutedSuccessEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CallbackExecutedSuccessEvent.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 *CallbackExecutedSuccessEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_CallbackExecutedSuccessEvent.Merge(m, src) +} +func (m *CallbackExecutedSuccessEvent) XXX_Size() int { + return m.Size() +} +func (m *CallbackExecutedSuccessEvent) XXX_DiscardUnknown() { + xxx_messageInfo_CallbackExecutedSuccessEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_CallbackExecutedSuccessEvent proto.InternalMessageInfo + +func (m *CallbackExecutedSuccessEvent) GetContractAddress() string { + if m != nil { + return m.ContractAddress + } + return "" +} + +func (m *CallbackExecutedSuccessEvent) GetJobId() uint64 { + if m != nil { + return m.JobId + } + return 0 +} + +func (m *CallbackExecutedSuccessEvent) GetSudoMsg() string { + if m != nil { + return m.SudoMsg + } + return "" +} + +func (m *CallbackExecutedSuccessEvent) GetGasUsed() uint64 { + if m != nil { + return m.GasUsed + } + return 0 +} + +// CallbackExecutedFailedEvent is emitted when a callback execution fails. +type CallbackExecutedFailedEvent struct { + // contract_address is the address of the contract for which callback is being executed (bech32 encoded). + ContractAddress string `protobuf:"bytes,1,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` + // job_id is an identifier of the callback. + JobId uint64 `protobuf:"varint,2,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` + // sudo_msg is the input passed by the module to the contract + SudoMsg string `protobuf:"bytes,3,opt,name=sudo_msg,json=sudoMsg,proto3" json:"sudo_msg,omitempty"` + // gas_used is the amount of gas consumed during the callback execution + GasUsed uint64 `protobuf:"varint,4,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"` + // error is the error returned during the callback execution + Error string `protobuf:"bytes,5,opt,name=error,proto3" json:"error,omitempty"` +} + +func (m *CallbackExecutedFailedEvent) Reset() { *m = CallbackExecutedFailedEvent{} } +func (m *CallbackExecutedFailedEvent) String() string { return proto.CompactTextString(m) } +func (*CallbackExecutedFailedEvent) ProtoMessage() {} +func (*CallbackExecutedFailedEvent) Descriptor() ([]byte, []int) { + return fileDescriptor_0debbd46abcdff3e, []int{3} +} +func (m *CallbackExecutedFailedEvent) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CallbackExecutedFailedEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CallbackExecutedFailedEvent.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 *CallbackExecutedFailedEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_CallbackExecutedFailedEvent.Merge(m, src) +} +func (m *CallbackExecutedFailedEvent) XXX_Size() int { + return m.Size() +} +func (m *CallbackExecutedFailedEvent) XXX_DiscardUnknown() { + xxx_messageInfo_CallbackExecutedFailedEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_CallbackExecutedFailedEvent proto.InternalMessageInfo + +func (m *CallbackExecutedFailedEvent) GetContractAddress() string { + if m != nil { + return m.ContractAddress + } + return "" +} + +func (m *CallbackExecutedFailedEvent) GetJobId() uint64 { + if m != nil { + return m.JobId + } + return 0 +} + +func (m *CallbackExecutedFailedEvent) GetSudoMsg() string { + if m != nil { + return m.SudoMsg + } + return "" +} + +func (m *CallbackExecutedFailedEvent) GetGasUsed() uint64 { + if m != nil { + return m.GasUsed + } + return 0 +} + +func (m *CallbackExecutedFailedEvent) GetError() string { + if m != nil { + return m.Error + } + return "" +} + +func init() { + proto.RegisterType((*CallbackRegisteredEvent)(nil), "rollapp.callback.v1.CallbackRegisteredEvent") + proto.RegisterType((*CallbackCancelledEvent)(nil), "rollapp.callback.v1.CallbackCancelledEvent") + proto.RegisterType((*CallbackExecutedSuccessEvent)(nil), "rollapp.callback.v1.CallbackExecutedSuccessEvent") + proto.RegisterType((*CallbackExecutedFailedEvent)(nil), "rollapp.callback.v1.CallbackExecutedFailedEvent") +} + +func init() { proto.RegisterFile("rollapp/callback/v1/events.proto", fileDescriptor_0debbd46abcdff3e) } + +var fileDescriptor_0debbd46abcdff3e = []byte{ + // 526 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x53, 0xcd, 0x6e, 0xd3, 0x40, + 0x10, 0xce, 0x36, 0x49, 0xdb, 0x6c, 0x0a, 0x45, 0xa6, 0x40, 0x52, 0x90, 0x1b, 0x72, 0x21, 0x3d, + 0x60, 0x2b, 0x45, 0x3c, 0x40, 0x13, 0x1a, 0xc1, 0x01, 0x21, 0xb9, 0xe2, 0xc2, 0xc5, 0x5a, 0xef, + 0x4e, 0x1c, 0x17, 0xdb, 0x1b, 0xed, 0xac, 0x43, 0xcc, 0x53, 0x70, 0xe2, 0x25, 0x78, 0x91, 0x1e, + 0x7b, 0xe4, 0x84, 0x50, 0x72, 0x85, 0x77, 0x40, 0xfe, 0x2b, 0x12, 0x8a, 0x54, 0x09, 0xf5, 0x36, + 0xf3, 0xcd, 0x7c, 0xf6, 0x7c, 0xdf, 0xcc, 0xd2, 0x9e, 0x92, 0x61, 0xc8, 0xe6, 0x73, 0x9b, 0xb3, + 0x30, 0xf4, 0x18, 0xff, 0x68, 0x2f, 0x86, 0x36, 0x2c, 0x20, 0xd6, 0x68, 0xcd, 0x95, 0xd4, 0xd2, + 0xb8, 0x5f, 0x76, 0x58, 0x55, 0x87, 0xb5, 0x18, 0x1e, 0xf6, 0x37, 0xd1, 0xae, 0x1b, 0x72, 0xe2, + 0xe1, 0x81, 0x2f, 0x7d, 0x99, 0x87, 0x76, 0x16, 0x95, 0xa8, 0xc9, 0x25, 0x46, 0x12, 0x6d, 0x8f, + 0x21, 0xd8, 0x8b, 0xa1, 0x07, 0x9a, 0x0d, 0x6d, 0x2e, 0x83, 0xb8, 0xac, 0x77, 0x8b, 0xba, 0x5b, + 0x10, 0x8b, 0xa4, 0x28, 0xf5, 0x7f, 0x13, 0xfa, 0x68, 0x5c, 0xfe, 0xc3, 0x01, 0x3f, 0x40, 0x0d, + 0x0a, 0xc4, 0x59, 0x36, 0xac, 0x71, 0x4c, 0xef, 0x71, 0x19, 0x6b, 0xc5, 0xb8, 0x76, 0x99, 0x10, + 0x0a, 0x10, 0x3b, 0xa4, 0x47, 0x06, 0x2d, 0x67, 0xbf, 0xc2, 0x4f, 0x0b, 0xd8, 0x78, 0x40, 0xb7, + 0x2f, 0xa4, 0xe7, 0x06, 0xa2, 0xb3, 0xd5, 0x23, 0x83, 0x86, 0xd3, 0xbc, 0x90, 0xde, 0x1b, 0x61, + 0x3c, 0xa3, 0xfb, 0x95, 0x00, 0x77, 0x06, 0x81, 0x3f, 0xd3, 0x9d, 0x7a, 0x8f, 0x0c, 0xea, 0xce, + 0xdd, 0x0a, 0x7e, 0x9d, 0xa3, 0xc6, 0x84, 0xb6, 0xa6, 0x00, 0x2e, 0xce, 0xc3, 0x40, 0x77, 0x1a, + 0x3d, 0x32, 0x68, 0x9f, 0x1c, 0x5b, 0x1b, 0x4c, 0xb2, 0xaa, 0x59, 0x27, 0x00, 0x38, 0x01, 0x38, + 0xcf, 0x08, 0xce, 0xee, 0xb4, 0x8c, 0x8c, 0x23, 0xda, 0x56, 0x80, 0xa0, 0x16, 0x20, 0x5c, 0x2f, + 0xed, 0x34, 0xf3, 0x69, 0x69, 0x05, 0x8d, 0xd2, 0xfe, 0x2f, 0x42, 0x1f, 0x56, 0xdf, 0x18, 0xb3, + 0x98, 0x43, 0x18, 0x56, 0x72, 0x9f, 0xd2, 0x3d, 0x5e, 0x21, 0x19, 0xb9, 0x90, 0xda, 0xbe, 0xc6, + 0x46, 0xe9, 0x46, 0x47, 0xb6, 0x6e, 0x72, 0xa4, 0x7e, 0x83, 0x23, 0x8d, 0x8d, 0x8e, 0xbc, 0xa2, + 0x77, 0x14, 0x4c, 0x93, 0x58, 0xb8, 0x2c, 0x92, 0x49, 0xac, 0x73, 0x2d, 0xed, 0x93, 0xae, 0x55, + 0xae, 0x2f, 0xdb, 0xb5, 0x55, 0xee, 0xda, 0x1a, 0xcb, 0x20, 0x1e, 0x35, 0x2e, 0x7f, 0x1c, 0xd5, + 0x9c, 0xbd, 0x82, 0x75, 0x9a, 0x93, 0xfa, 0x5f, 0x09, 0x7d, 0x52, 0xc9, 0x3d, 0x5b, 0x02, 0x4f, + 0x34, 0x88, 0xf3, 0x84, 0x73, 0x40, 0xbc, 0xad, 0x1d, 0x77, 0xe9, 0x2e, 0x26, 0x42, 0xba, 0x11, + 0xfa, 0xb9, 0xd4, 0x96, 0xb3, 0x93, 0xe5, 0x6f, 0xd1, 0xcf, 0x4a, 0x3e, 0x43, 0x37, 0x41, 0x10, + 0xb9, 0xca, 0x86, 0xb3, 0xe3, 0x33, 0x7c, 0x8f, 0x20, 0xfa, 0xdf, 0x08, 0x7d, 0xfc, 0xef, 0x60, + 0x13, 0x16, 0x84, 0xb7, 0x77, 0x7b, 0xff, 0x35, 0x97, 0x71, 0x40, 0x9b, 0xa0, 0x94, 0x54, 0xe5, + 0xe9, 0x14, 0xc9, 0xe8, 0xdd, 0xe5, 0xca, 0x24, 0x57, 0x2b, 0x93, 0xfc, 0x5c, 0x99, 0xe4, 0xcb, + 0xda, 0xac, 0x5d, 0xad, 0xcd, 0xda, 0xf7, 0xb5, 0x59, 0xfb, 0xf0, 0xd2, 0x0f, 0xf4, 0x2c, 0xf1, + 0x2c, 0x2e, 0x23, 0x5b, 0xa4, 0x11, 0xc4, 0x18, 0xc8, 0x78, 0x99, 0x7e, 0xb6, 0xcb, 0xe3, 0x7d, + 0xfe, 0x89, 0x61, 0x64, 0x2f, 0xff, 0xbe, 0x69, 0x9d, 0xce, 0x01, 0xbd, 0xed, 0xfc, 0xf5, 0xbd, + 0xf8, 0x13, 0x00, 0x00, 0xff, 0xff, 0x33, 0xa7, 0x19, 0x4f, 0x2b, 0x04, 0x00, 0x00, +} + +func (m *CallbackRegisteredEvent) 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 *CallbackRegisteredEvent) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CallbackRegisteredEvent) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ReservedBy) > 0 { + i -= len(m.ReservedBy) + copy(dAtA[i:], m.ReservedBy) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ReservedBy))) + i-- + dAtA[i] = 0x2a + } + if m.FeeSplit != nil { + { + size, err := m.FeeSplit.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.CallbackHeight != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.CallbackHeight)) + i-- + dAtA[i] = 0x18 + } + if m.JobId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.JobId)) + i-- + dAtA[i] = 0x10 + } + if len(m.ContractAddress) > 0 { + i -= len(m.ContractAddress) + copy(dAtA[i:], m.ContractAddress) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ContractAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CallbackCancelledEvent) 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 *CallbackCancelledEvent) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CallbackCancelledEvent) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.RefundAmount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if m.CallbackHeight != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.CallbackHeight)) + i-- + dAtA[i] = 0x20 + } + if m.JobId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.JobId)) + i-- + dAtA[i] = 0x18 + } + if len(m.ContractAddress) > 0 { + i -= len(m.ContractAddress) + copy(dAtA[i:], m.ContractAddress) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ContractAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.CancelledBy) > 0 { + i -= len(m.CancelledBy) + copy(dAtA[i:], m.CancelledBy) + i = encodeVarintEvents(dAtA, i, uint64(len(m.CancelledBy))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CallbackExecutedSuccessEvent) 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 *CallbackExecutedSuccessEvent) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CallbackExecutedSuccessEvent) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.GasUsed != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.GasUsed)) + i-- + dAtA[i] = 0x20 + } + if len(m.SudoMsg) > 0 { + i -= len(m.SudoMsg) + copy(dAtA[i:], m.SudoMsg) + i = encodeVarintEvents(dAtA, i, uint64(len(m.SudoMsg))) + i-- + dAtA[i] = 0x1a + } + if m.JobId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.JobId)) + i-- + dAtA[i] = 0x10 + } + if len(m.ContractAddress) > 0 { + i -= len(m.ContractAddress) + copy(dAtA[i:], m.ContractAddress) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ContractAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CallbackExecutedFailedEvent) 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 *CallbackExecutedFailedEvent) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CallbackExecutedFailedEvent) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Error) > 0 { + i -= len(m.Error) + copy(dAtA[i:], m.Error) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Error))) + i-- + dAtA[i] = 0x2a + } + if m.GasUsed != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.GasUsed)) + i-- + dAtA[i] = 0x20 + } + if len(m.SudoMsg) > 0 { + i -= len(m.SudoMsg) + copy(dAtA[i:], m.SudoMsg) + i = encodeVarintEvents(dAtA, i, uint64(len(m.SudoMsg))) + i-- + dAtA[i] = 0x1a + } + if m.JobId != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.JobId)) + i-- + dAtA[i] = 0x10 + } + if len(m.ContractAddress) > 0 { + i -= len(m.ContractAddress) + copy(dAtA[i:], m.ContractAddress) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ContractAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintEvents(dAtA []byte, offset int, v uint64) int { + offset -= sovEvents(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *CallbackRegisteredEvent) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ContractAddress) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.JobId != 0 { + n += 1 + sovEvents(uint64(m.JobId)) + } + if m.CallbackHeight != 0 { + n += 1 + sovEvents(uint64(m.CallbackHeight)) + } + if m.FeeSplit != nil { + l = m.FeeSplit.Size() + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.ReservedBy) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *CallbackCancelledEvent) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.CancelledBy) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.ContractAddress) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.JobId != 0 { + n += 1 + sovEvents(uint64(m.JobId)) + } + if m.CallbackHeight != 0 { + n += 1 + sovEvents(uint64(m.CallbackHeight)) + } + l = m.RefundAmount.Size() + n += 1 + l + sovEvents(uint64(l)) + return n +} + +func (m *CallbackExecutedSuccessEvent) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ContractAddress) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.JobId != 0 { + n += 1 + sovEvents(uint64(m.JobId)) + } + l = len(m.SudoMsg) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.GasUsed != 0 { + n += 1 + sovEvents(uint64(m.GasUsed)) + } + return n +} + +func (m *CallbackExecutedFailedEvent) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ContractAddress) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.JobId != 0 { + n += 1 + sovEvents(uint64(m.JobId)) + } + l = len(m.SudoMsg) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.GasUsed != 0 { + n += 1 + sovEvents(uint64(m.GasUsed)) + } + l = len(m.Error) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func sovEvents(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozEvents(x uint64) (n int) { + return sovEvents(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *CallbackRegisteredEvent) 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 ErrIntOverflowEvents + } + 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: CallbackRegisteredEvent: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CallbackRegisteredEvent: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContractAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field JobId", wireType) + } + m.JobId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.JobId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CallbackHeight", wireType) + } + m.CallbackHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CallbackHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeeSplit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.FeeSplit == nil { + m.FeeSplit = &CallbackFeesFeeSplit{} + } + if err := m.FeeSplit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ReservedBy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ReservedBy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CallbackCancelledEvent) 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 ErrIntOverflowEvents + } + 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: CallbackCancelledEvent: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CallbackCancelledEvent: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CancelledBy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CancelledBy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContractAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field JobId", wireType) + } + m.JobId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.JobId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CallbackHeight", wireType) + } + m.CallbackHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CallbackHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RefundAmount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.RefundAmount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CallbackExecutedSuccessEvent) 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 ErrIntOverflowEvents + } + 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: CallbackExecutedSuccessEvent: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CallbackExecutedSuccessEvent: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContractAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field JobId", wireType) + } + m.JobId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.JobId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SudoMsg", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SudoMsg = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GasUsed", wireType) + } + m.GasUsed = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GasUsed |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CallbackExecutedFailedEvent) 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 ErrIntOverflowEvents + } + 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: CallbackExecutedFailedEvent: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CallbackExecutedFailedEvent: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContractAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field JobId", wireType) + } + m.JobId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.JobId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SudoMsg", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SudoMsg = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GasUsed", wireType) + } + m.GasUsed = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GasUsed |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Error = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipEvents(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, ErrIntOverflowEvents + } + 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, ErrIntOverflowEvents + } + 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, ErrIntOverflowEvents + } + 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, ErrInvalidLengthEvents + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupEvents + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthEvents + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthEvents = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowEvents = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupEvents = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/callback/types/expected_keepers.go b/x/callback/types/expected_keepers.go new file mode 100644 index 0000000..54287b5 --- /dev/null +++ b/x/callback/types/expected_keepers.go @@ -0,0 +1,24 @@ +package types + +import ( + wasmdtypes "github.com/CosmWasm/wasmd/x/wasm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + cwerrortypes "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +type WasmKeeperExpected interface { + HasContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) bool + GetContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) *wasmdtypes.ContractInfo + Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error) +} + +type BankKeeperExpected interface { + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error + BlockedAddr(addr sdk.AccAddress) bool +} + +type ErrorsKeeperExpected interface { + SetError(ctx sdk.Context, sudoErr cwerrortypes.SudoError) error +} diff --git a/x/callback/types/genesis.go b/x/callback/types/genesis.go new file mode 100644 index 0000000..54c8f4c --- /dev/null +++ b/x/callback/types/genesis.go @@ -0,0 +1,35 @@ +package types + +// NewGenesisState creates a new GenesisState object. +func NewGenesisState( + params Params, + callbacks []Callback, +) *GenesisState { + var callbacksCopy []*Callback + for _, c := range callbacks { + callbacksCopy = append(callbacksCopy, &c) + } + return &GenesisState{ + Params: params, + Callbacks: callbacksCopy, + } +} + +// DefaultGenesisState returns a default genesis state. +func DefaultGenesis() *GenesisState { + defaultParams := DefaultParams() + return NewGenesisState(defaultParams, nil) +} + +// Validate perform object fields validation. +func (g GenesisState) Validate() error { + if err := g.Params.Validate(); err != nil { + return err + } + for _, callback := range g.GetCallbacks() { + if err := callback.Validate(); err != nil { + return err + } + } + return nil +} diff --git a/x/callback/types/genesis.pb.go b/x/callback/types/genesis.pb.go new file mode 100644 index 0000000..d7190c3 --- /dev/null +++ b/x/callback/types/genesis.pb.go @@ -0,0 +1,390 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: rollapp/callback/v1/genesis.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/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 + +// GenesisState defines the initial state of the callback module. +type GenesisState struct { + // params defines all the module parameters. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` + // callbacks defines all the callbacks which are yet to be executed + Callbacks []*Callback `protobuf:"bytes,2,rep,name=callbacks,proto3" json:"callbacks,omitempty"` +} + +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_40045791a5e582f8, []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 (m *GenesisState) GetCallbacks() []*Callback { + if m != nil { + return m.Callbacks + } + return nil +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "rollapp.callback.v1.GenesisState") +} + +func init() { proto.RegisterFile("rollapp/callback/v1/genesis.proto", fileDescriptor_40045791a5e582f8) } + +var fileDescriptor_40045791a5e582f8 = []byte{ + // 263 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2c, 0xca, 0xcf, 0xc9, + 0x49, 0x2c, 0x28, 0xd0, 0x4f, 0x4e, 0xcc, 0xc9, 0x49, 0x4a, 0x4c, 0xce, 0xd6, 0x2f, 0x33, 0xd4, + 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x86, + 0x2a, 0xd1, 0x83, 0x29, 0xd1, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xcb, 0xeb, + 0x83, 0x58, 0x10, 0xa5, 0x52, 0x72, 0xc9, 0xf9, 0xc5, 0xb9, 0xf9, 0xc5, 0xfa, 0x49, 0x89, 0xc5, + 0xa9, 0xfa, 0x65, 0x86, 0x49, 0xa9, 0x25, 0x89, 0x86, 0xfa, 0xc9, 0xf9, 0x99, 0x79, 0x50, 0x79, + 0x25, 0x6c, 0xb6, 0xc1, 0x8d, 0x05, 0xab, 0x51, 0x6a, 0x63, 0xe4, 0xe2, 0x71, 0x87, 0x38, 0x20, + 0xb8, 0x24, 0xb1, 0x24, 0x55, 0xc8, 0x92, 0x8b, 0xad, 0x20, 0xb1, 0x28, 0x31, 0xb7, 0x58, 0x82, + 0x51, 0x81, 0x51, 0x83, 0xdb, 0x48, 0x5a, 0x0f, 0x8b, 0x83, 0xf4, 0x02, 0xc0, 0x4a, 0x9c, 0x58, + 0x4e, 0xdc, 0x93, 0x67, 0x08, 0x82, 0x6a, 0x10, 0xb2, 0xe6, 0xe2, 0x84, 0xa9, 0x29, 0x96, 0x60, + 0x52, 0x60, 0xd6, 0xe0, 0x36, 0x92, 0xc5, 0xaa, 0xdb, 0x19, 0xca, 0x0e, 0x42, 0xa8, 0x77, 0xf2, + 0x3f, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, + 0x63, 0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39, 0x86, 0x28, 0xd3, 0xf4, 0xcc, 0x92, 0x8c, + 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0xfd, 0x94, 0xca, 0xdc, 0xd4, 0xbc, 0xe2, 0xcc, 0xfc, 0xbc, + 0x8a, 0xca, 0x2a, 0x7d, 0xa8, 0xd1, 0xba, 0xe5, 0x89, 0xc5, 0xb9, 0xfa, 0x15, 0x08, 0x5f, 0x96, + 0x54, 0x16, 0xa4, 0x16, 0x27, 0xb1, 0x81, 0x3d, 0x68, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xa2, + 0xc5, 0xf9, 0x83, 0x74, 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 + if len(m.Callbacks) > 0 { + for iNdEx := len(m.Callbacks) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Callbacks[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + 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)) + if len(m.Callbacks) > 0 { + for _, e := range m.Callbacks { + l = e.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 + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Callbacks", 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 + } + m.Callbacks = append(m.Callbacks, &Callback{}) + if err := m.Callbacks[len(m.Callbacks)-1].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/callback/types/genesis_test.go b/x/callback/types/genesis_test.go new file mode 100644 index 0000000..8ff0463 --- /dev/null +++ b/x/callback/types/genesis_test.go @@ -0,0 +1,110 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + sdk "github.com/cosmos/cosmos-sdk/types" + + e2eTesting "github.com/dymensionxyz/rollapp-wasm/e2e/testing" + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +func TestGenesisValidate(t *testing.T) { + accAddrs, _ := e2eTesting.GenAccounts(1) + accAddr := accAddrs[0] + contractAddr := e2eTesting.GenContractAddresses(1)[0] + validCoin := sdk.NewInt64Coin("stake", 1) + + type testCase struct { + name string + genesis types.GenesisState + errExpected bool + } + + testCases := []testCase{ + { + name: "Fail: Empty values", + genesis: types.GenesisState{}, + errExpected: true, + }, + { + name: "Fail: Invalid params", + genesis: types.GenesisState{ + Params: types.NewParams( + 0, + 100, + 100, + sdk.MustNewDecFromStr("1.0"), + sdk.MustNewDecFromStr("1.0"), + sdk.NewCoin(sdk.DefaultBondDenom, sdk.ZeroInt()), + ), + Callbacks: []*types.Callback{ + { + ContractAddress: contractAddr.String(), + ReservedBy: accAddr.String(), + CallbackHeight: 1, + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + }, + }, + errExpected: true, + }, { + name: "Fail: Invalid callback", + genesis: types.GenesisState{ + Params: types.DefaultParams(), + Callbacks: []*types.Callback{ + { + ContractAddress: "👻", + ReservedBy: accAddr.String(), + CallbackHeight: 1, + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + }, + }, + errExpected: true, + }, + { + name: "OK: Valid genesis state", + genesis: types.GenesisState{ + Params: types.DefaultParams(), + Callbacks: []*types.Callback{ + { + ContractAddress: contractAddr.String(), + ReservedBy: accAddr.String(), + CallbackHeight: 1, + FeeSplit: &types.CallbackFeesFeeSplit{ + TransactionFees: &validCoin, + BlockReservationFees: &validCoin, + FutureReservationFees: &validCoin, + SurplusFees: &validCoin, + }, + }, + }, + }, + errExpected: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tc.genesis.Validate() + if tc.errExpected { + assert.Error(t, err) + return + } + assert.NoError(t, err) + }) + } +} diff --git a/x/callback/types/keys.go b/x/callback/types/keys.go new file mode 100644 index 0000000..a082752 --- /dev/null +++ b/x/callback/types/keys.go @@ -0,0 +1,19 @@ +package types + +import ( + "cosmossdk.io/collections" +) + +const ( + // ModuleName is the module name. + ModuleName = "callback" + // StoreKey is the module KV storage prefix key. + StoreKey = ModuleName + // QuerierRoute is the querier route for the module. + QuerierRoute = ModuleName +) + +var ( + ParamsKeyPrefix = collections.NewPrefix(1) + CallbackKeyPrefix = collections.NewPrefix(2) +) diff --git a/x/callback/types/msg.go b/x/callback/types/msg.go new file mode 100644 index 0000000..de43656 --- /dev/null +++ b/x/callback/types/msg.go @@ -0,0 +1,82 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkErrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +var ( + _ sdk.Msg = &MsgRequestCallback{} + _ sdk.Msg = &MsgCancelCallback{} +) + +// NewMsgRequestCallback creates a new MsgRequestCallback instance. +func NewMsgRequestCallback( + senderAddr sdk.AccAddress, + contractAddr sdk.AccAddress, + jobId uint64, + callbackHeight int64, + fees sdk.Coin, +) *MsgRequestCallback { + msg := &MsgRequestCallback{ + Sender: senderAddr.String(), + ContractAddress: contractAddr.String(), + JobId: jobId, + CallbackHeight: callbackHeight, + Fees: fees, + } + + return msg +} + +// GetSigners implements the sdk.Msg interface. +func (m MsgRequestCallback) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{sdk.MustAccAddressFromBech32(m.Sender)} +} + +// ValidateBasic implements the sdk.Msg interface. +func (m MsgRequestCallback) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(m.Sender); err != nil { + return errorsmod.Wrapf(sdkErrors.ErrInvalidAddress, "invalid sender address: %v", err) + } + if _, err := sdk.AccAddressFromBech32(m.ContractAddress); err != nil { + return errorsmod.Wrapf(sdkErrors.ErrInvalidAddress, "invalid contract address: %v", err) + } + + return nil +} + +// NewMsgCancelCallback creates a new MsgCancelCallback instance. +func NewMsgCancelCallback( + senderAddr sdk.AccAddress, + contractAddr sdk.AccAddress, + jobId uint64, + callbackHeight int64, +) *MsgCancelCallback { + msg := &MsgCancelCallback{ + Sender: senderAddr.String(), + ContractAddress: contractAddr.String(), + JobId: jobId, + CallbackHeight: callbackHeight, + } + + return msg +} + +// GetSigners implements the sdk.Msg interface. +func (m MsgCancelCallback) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{sdk.MustAccAddressFromBech32(m.Sender)} +} + +// ValidateBasic implements the sdk.Msg interface. +func (m MsgCancelCallback) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(m.Sender); err != nil { + return errorsmod.Wrapf(sdkErrors.ErrInvalidAddress, "invalid sender address: %v", err) + } + if _, err := sdk.AccAddressFromBech32(m.ContractAddress); err != nil { + return errorsmod.Wrapf(sdkErrors.ErrInvalidAddress, "invalid contract address: %v", err) + } + + return nil +} diff --git a/x/callback/types/params.go b/x/callback/types/params.go new file mode 100644 index 0000000..10ddd2c --- /dev/null +++ b/x/callback/types/params.go @@ -0,0 +1,61 @@ +package types + +import ( + fmt "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + DefaultCallbackGasLimit = uint64(1000000) + DefaultMaxBlockReservationLimit = uint64(3) + DefaultMaxFutureReservationLimit = uint64(10000) + DefaultBlockReservationFeeMultiplier = sdk.ZeroDec() + DefaultFutureReservationFeeMultiplier = sdk.ZeroDec() + DefaultMinPriceOfGas = sdk.NewCoin(sdk.DefaultBondDenom, sdk.ZeroInt()) +) + +// NewParams creates a new Params instance. +func NewParams( + callbackGasLimit uint64, + maxBlockReservationLimit uint64, + maxFutureReservationLimit uint64, + blockReservationFeeMultiplier sdk.Dec, + futureReservationFeeMultiplier sdk.Dec, + minPriceOfGas sdk.Coin, +) Params { + return Params{ + CallbackGasLimit: callbackGasLimit, + MaxBlockReservationLimit: maxBlockReservationLimit, + MaxFutureReservationLimit: maxFutureReservationLimit, + BlockReservationFeeMultiplier: blockReservationFeeMultiplier, + FutureReservationFeeMultiplier: futureReservationFeeMultiplier, + MinPriceOfGas: minPriceOfGas, + } +} + +// DefaultParams returns a default set of parameters. +func DefaultParams() Params { + return NewParams( + DefaultCallbackGasLimit, + DefaultMaxBlockReservationLimit, + DefaultMaxFutureReservationLimit, + DefaultBlockReservationFeeMultiplier, + DefaultFutureReservationFeeMultiplier, + DefaultMinPriceOfGas, + ) +} + +// Validate perform object fields validation. +func (p Params) Validate() error { + if p.CallbackGasLimit == 0 { + return fmt.Errorf("CallbackGasLimit must be greater than 0") + } + if p.BlockReservationFeeMultiplier.IsNegative() { + return fmt.Errorf("BlockReservationFeeMultiplier must be greater than 0") + } + if p.FutureReservationFeeMultiplier.IsNegative() { + return fmt.Errorf("FutureReservationFeeMultiplier must be greater than 0") + } + return nil +} diff --git a/x/callback/types/params_test.go b/x/callback/types/params_test.go new file mode 100644 index 0000000..1a3a474 --- /dev/null +++ b/x/callback/types/params_test.go @@ -0,0 +1,85 @@ +package types_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/assert" + + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +func TestParamsValidate(t *testing.T) { + type testCase struct { + name string + params types.Params + errExpected bool + } + + testCases := []testCase{ + { + name: "OK: Default values", + params: types.DefaultParams(), + errExpected: false, + }, + { + name: "OK: All valid values", + params: types.NewParams( + 100, + 100, + 100, + sdk.MustNewDecFromStr("1.0"), + sdk.MustNewDecFromStr("1.0"), + sdk.NewCoin(sdk.DefaultBondDenom, sdk.ZeroInt()), + ), + errExpected: false, + }, + { + name: "Fail: CallbackGasLimit: zero", + params: types.NewParams( + 0, + 100, + 100, + sdk.MustNewDecFromStr("1.0"), + sdk.MustNewDecFromStr("1.0"), + sdk.NewCoin(sdk.DefaultBondDenom, sdk.ZeroInt()), + ), + errExpected: true, + }, + { + name: "Fail: BlockReservationFeeMultiplier: negative", + params: types.NewParams( + 100, + 100, + 100, + sdk.MustNewDecFromStr("-1.0"), + sdk.MustNewDecFromStr("1.0"), + sdk.NewCoin(sdk.DefaultBondDenom, sdk.ZeroInt()), + ), + errExpected: true, + }, + { + name: "Fail: FutureReservationFeeMultiplier: negative", + params: types.NewParams( + 100, + 100, + 100, + sdk.MustNewDecFromStr("1.0"), + sdk.MustNewDecFromStr("-1.0"), + sdk.NewCoin(sdk.DefaultBondDenom, sdk.ZeroInt()), + ), + errExpected: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tc.params.Validate() + if tc.errExpected { + assert.Error(t, err) + return + } + assert.NoError(t, err) + }) + } +} diff --git a/x/callback/types/query.pb.go b/x/callback/types/query.pb.go new file mode 100644 index 0000000..0625957 --- /dev/null +++ b/x/callback/types/query.pb.go @@ -0,0 +1,1365 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: rollapp/callback/v1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + 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 + +// QueryParamsRequest is the request for Query.Params. +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_26ece69f2c80e3de, []int{0} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.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 *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryParamsResponse is the response for Query.Params. +type QueryParamsResponse struct { + // params defines all the module parameters. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_26ece69f2c80e3de, []int{1} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.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 *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// QueryEstimateCallbackFeesRequest is the request for Query.EstimateCallbackFees. +type QueryEstimateCallbackFeesRequest struct { + // block_height is the height at which to estimate the callback fees + BlockHeight int64 `protobuf:"varint,1,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` +} + +func (m *QueryEstimateCallbackFeesRequest) Reset() { *m = QueryEstimateCallbackFeesRequest{} } +func (m *QueryEstimateCallbackFeesRequest) String() string { return proto.CompactTextString(m) } +func (*QueryEstimateCallbackFeesRequest) ProtoMessage() {} +func (*QueryEstimateCallbackFeesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_26ece69f2c80e3de, []int{2} +} +func (m *QueryEstimateCallbackFeesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryEstimateCallbackFeesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryEstimateCallbackFeesRequest.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 *QueryEstimateCallbackFeesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryEstimateCallbackFeesRequest.Merge(m, src) +} +func (m *QueryEstimateCallbackFeesRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryEstimateCallbackFeesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryEstimateCallbackFeesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryEstimateCallbackFeesRequest proto.InternalMessageInfo + +func (m *QueryEstimateCallbackFeesRequest) GetBlockHeight() int64 { + if m != nil { + return m.BlockHeight + } + return 0 +} + +// QueryEstimateCallbackFeesResponse is the response for Query.EstimateCallbackFees. +type QueryEstimateCallbackFeesResponse struct { + // total_fees is the total fees that needs to be paid by the contract to reserve a callback + TotalFees *types.Coin `protobuf:"bytes,1,opt,name=total_fees,json=totalFees,proto3" json:"total_fees,omitempty"` + // fee_split is the breakdown of the total_fees + FeeSplit *CallbackFeesFeeSplit `protobuf:"bytes,2,opt,name=fee_split,json=feeSplit,proto3" json:"fee_split,omitempty"` +} + +func (m *QueryEstimateCallbackFeesResponse) Reset() { *m = QueryEstimateCallbackFeesResponse{} } +func (m *QueryEstimateCallbackFeesResponse) String() string { return proto.CompactTextString(m) } +func (*QueryEstimateCallbackFeesResponse) ProtoMessage() {} +func (*QueryEstimateCallbackFeesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_26ece69f2c80e3de, []int{3} +} +func (m *QueryEstimateCallbackFeesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryEstimateCallbackFeesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryEstimateCallbackFeesResponse.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 *QueryEstimateCallbackFeesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryEstimateCallbackFeesResponse.Merge(m, src) +} +func (m *QueryEstimateCallbackFeesResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryEstimateCallbackFeesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryEstimateCallbackFeesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryEstimateCallbackFeesResponse proto.InternalMessageInfo + +func (m *QueryEstimateCallbackFeesResponse) GetTotalFees() *types.Coin { + if m != nil { + return m.TotalFees + } + return nil +} + +func (m *QueryEstimateCallbackFeesResponse) GetFeeSplit() *CallbackFeesFeeSplit { + if m != nil { + return m.FeeSplit + } + return nil +} + +// QueryCallbacksRequest is the request for Query.Callbacks. +type QueryCallbacksRequest struct { + // block_height is the height at which to query the callbacks + BlockHeight int64 `protobuf:"varint,1,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` +} + +func (m *QueryCallbacksRequest) Reset() { *m = QueryCallbacksRequest{} } +func (m *QueryCallbacksRequest) String() string { return proto.CompactTextString(m) } +func (*QueryCallbacksRequest) ProtoMessage() {} +func (*QueryCallbacksRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_26ece69f2c80e3de, []int{4} +} +func (m *QueryCallbacksRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryCallbacksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryCallbacksRequest.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 *QueryCallbacksRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryCallbacksRequest.Merge(m, src) +} +func (m *QueryCallbacksRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryCallbacksRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryCallbacksRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryCallbacksRequest proto.InternalMessageInfo + +func (m *QueryCallbacksRequest) GetBlockHeight() int64 { + if m != nil { + return m.BlockHeight + } + return 0 +} + +// QueryCallbacksResponse is the response for Query.Callbacks. +type QueryCallbacksResponse struct { + // callbacks is the list of callbacks registered at the given height + Callbacks []*Callback `protobuf:"bytes,1,rep,name=callbacks,proto3" json:"callbacks,omitempty"` +} + +func (m *QueryCallbacksResponse) Reset() { *m = QueryCallbacksResponse{} } +func (m *QueryCallbacksResponse) String() string { return proto.CompactTextString(m) } +func (*QueryCallbacksResponse) ProtoMessage() {} +func (*QueryCallbacksResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_26ece69f2c80e3de, []int{5} +} +func (m *QueryCallbacksResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryCallbacksResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryCallbacksResponse.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 *QueryCallbacksResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryCallbacksResponse.Merge(m, src) +} +func (m *QueryCallbacksResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryCallbacksResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryCallbacksResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryCallbacksResponse proto.InternalMessageInfo + +func (m *QueryCallbacksResponse) GetCallbacks() []*Callback { + if m != nil { + return m.Callbacks + } + return nil +} + +func init() { + proto.RegisterType((*QueryParamsRequest)(nil), "rollapp.callback.v1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "rollapp.callback.v1.QueryParamsResponse") + proto.RegisterType((*QueryEstimateCallbackFeesRequest)(nil), "rollapp.callback.v1.QueryEstimateCallbackFeesRequest") + proto.RegisterType((*QueryEstimateCallbackFeesResponse)(nil), "rollapp.callback.v1.QueryEstimateCallbackFeesResponse") + proto.RegisterType((*QueryCallbacksRequest)(nil), "rollapp.callback.v1.QueryCallbacksRequest") + proto.RegisterType((*QueryCallbacksResponse)(nil), "rollapp.callback.v1.QueryCallbacksResponse") +} + +func init() { proto.RegisterFile("rollapp/callback/v1/query.proto", fileDescriptor_26ece69f2c80e3de) } + +var fileDescriptor_26ece69f2c80e3de = []byte{ + // 537 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x94, 0xcf, 0x6e, 0xd3, 0x40, + 0x10, 0xc6, 0xe3, 0x06, 0x22, 0xb2, 0xe1, 0xb4, 0x0d, 0xa8, 0xa4, 0xd4, 0x4d, 0x8d, 0x04, 0x81, + 0xaa, 0xbb, 0x4a, 0xaa, 0x22, 0xfe, 0xdc, 0x5a, 0x35, 0xe2, 0x46, 0x31, 0xe2, 0xc2, 0x25, 0x5a, + 0x9b, 0x89, 0xb3, 0xaa, 0xed, 0x75, 0xb3, 0x9b, 0xd0, 0x70, 0x42, 0x9c, 0x39, 0x20, 0xf1, 0x0c, + 0xbc, 0x04, 0x4f, 0xd0, 0x63, 0x25, 0x24, 0xc4, 0x09, 0xa1, 0x84, 0x07, 0x41, 0x5e, 0xaf, 0x5b, + 0x28, 0x6e, 0xa0, 0xb7, 0xd5, 0xec, 0xf7, 0xcd, 0xfc, 0x66, 0x67, 0x6c, 0xb4, 0x3a, 0x14, 0x61, + 0xc8, 0x92, 0x84, 0xfa, 0x2c, 0x0c, 0x3d, 0xe6, 0xef, 0xd3, 0x71, 0x9b, 0x1e, 0x8c, 0x60, 0x38, + 0x21, 0xc9, 0x50, 0x28, 0x81, 0x17, 0x8d, 0x80, 0xe4, 0x02, 0x32, 0x6e, 0x37, 0xea, 0x81, 0x08, + 0x84, 0xbe, 0xa7, 0xe9, 0x29, 0x93, 0x36, 0x6e, 0x06, 0x42, 0x04, 0x21, 0x50, 0x96, 0x70, 0xca, + 0xe2, 0x58, 0x28, 0xa6, 0xb8, 0x88, 0xa5, 0xb9, 0xb5, 0x7d, 0x21, 0x23, 0x21, 0xa9, 0xc7, 0x24, + 0xd0, 0x71, 0xdb, 0x03, 0xc5, 0xda, 0xd4, 0x17, 0x3c, 0x36, 0xf7, 0x4e, 0x11, 0xc9, 0x49, 0x51, + 0xad, 0x71, 0xea, 0x08, 0x3f, 0x4b, 0xd9, 0xf6, 0xd8, 0x90, 0x45, 0xd2, 0x85, 0x83, 0x11, 0x48, + 0xe5, 0xec, 0xa1, 0xc5, 0x3f, 0xa2, 0x32, 0x11, 0xb1, 0x04, 0xfc, 0x10, 0x55, 0x12, 0x1d, 0x59, + 0xb2, 0x9a, 0x56, 0xab, 0xd6, 0x59, 0x26, 0x05, 0xad, 0x90, 0xcc, 0xb4, 0x7d, 0xe9, 0xe8, 0xfb, + 0x6a, 0xc9, 0x35, 0x06, 0x67, 0x17, 0x35, 0x75, 0xc6, 0x5d, 0xa9, 0x78, 0xc4, 0x14, 0xec, 0x18, + 0x43, 0x17, 0x20, 0xaf, 0x8a, 0xd7, 0xd0, 0x55, 0x2f, 0x14, 0xfe, 0x7e, 0x6f, 0x00, 0x3c, 0x18, + 0x28, 0x5d, 0xa4, 0xec, 0xd6, 0x74, 0xec, 0x89, 0x0e, 0x39, 0x9f, 0x2c, 0xb4, 0x36, 0x27, 0x8f, + 0xe1, 0x7c, 0x80, 0x90, 0x12, 0x8a, 0x85, 0xbd, 0x3e, 0x40, 0xce, 0x7a, 0x83, 0x64, 0xaf, 0x45, + 0xd2, 0xd7, 0x22, 0xe6, 0xb5, 0xc8, 0x8e, 0xe0, 0xb1, 0x5b, 0xd5, 0xe2, 0x34, 0x03, 0xee, 0xa2, + 0x6a, 0x1f, 0xa0, 0x27, 0x93, 0x90, 0xab, 0xa5, 0x05, 0x6d, 0xbc, 0x5b, 0xd8, 0xe4, 0xef, 0x75, + 0xbb, 0x00, 0xcf, 0x53, 0x83, 0x7b, 0xa5, 0x6f, 0x4e, 0xce, 0x23, 0x74, 0x4d, 0x63, 0xe6, 0xb2, + 0x8b, 0xf4, 0xf8, 0x02, 0x5d, 0x3f, 0xeb, 0x35, 0x7d, 0x3d, 0x46, 0xd5, 0x9c, 0x21, 0x6d, 0xab, + 0xdc, 0xaa, 0x75, 0x56, 0xe6, 0xd2, 0xb9, 0xa7, 0xfa, 0xce, 0xd7, 0x32, 0xba, 0xac, 0xf3, 0xe2, + 0xb7, 0x16, 0xaa, 0x64, 0x43, 0xc2, 0x77, 0x0a, 0xed, 0x7f, 0x6f, 0x44, 0xa3, 0xf5, 0x6f, 0x61, + 0x06, 0xe9, 0xdc, 0x7a, 0xf7, 0xe5, 0xe7, 0xc7, 0x85, 0x15, 0xbc, 0x4c, 0x8b, 0xd6, 0x2f, 0x5b, + 0x07, 0xfc, 0xd9, 0x42, 0xf5, 0xa2, 0x11, 0xe2, 0xad, 0xf3, 0xeb, 0xcc, 0x59, 0x9d, 0xc6, 0xfd, + 0x8b, 0xda, 0x0c, 0xec, 0xa6, 0x86, 0xdd, 0xc0, 0xeb, 0x85, 0xb0, 0x60, 0xac, 0xbd, 0x3c, 0xa8, + 0x17, 0x0a, 0xbf, 0xb7, 0x50, 0xf5, 0x64, 0x38, 0xf8, 0xde, 0xf9, 0xa5, 0xcf, 0x4e, 0xbf, 0xb1, + 0xfe, 0x5f, 0x5a, 0xc3, 0x76, 0x5b, 0xb3, 0x35, 0xb1, 0x4d, 0xe7, 0x7d, 0xc7, 0x72, 0xfb, 0xe9, + 0xd1, 0xd4, 0xb6, 0x8e, 0xa7, 0xb6, 0xf5, 0x63, 0x6a, 0x5b, 0x1f, 0x66, 0x76, 0xe9, 0x78, 0x66, + 0x97, 0xbe, 0xcd, 0xec, 0xd2, 0xcb, 0xad, 0x80, 0xab, 0xc1, 0xc8, 0x23, 0xbe, 0x88, 0xe8, 0xab, + 0x49, 0x04, 0xb1, 0xe4, 0x22, 0x3e, 0x9c, 0xbc, 0xc9, 0x13, 0x6e, 0xbc, 0x66, 0x32, 0xa2, 0x87, + 0xa7, 0x79, 0xd5, 0x24, 0x01, 0xe9, 0x55, 0xf4, 0xaf, 0x61, 0xf3, 0x57, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xb6, 0xe1, 0x91, 0xd9, 0xca, 0x04, 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 + +// QueryClient is the client API for Query service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type QueryClient interface { + // Params returns module parameters + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // EstimateCallbackFees returns the total amount of callback fees a contract needs to pay to register the callback + EstimateCallbackFees(ctx context.Context, in *QueryEstimateCallbackFeesRequest, opts ...grpc.CallOption) (*QueryEstimateCallbackFeesResponse, error) + // Callbacks returns all the callbacks registered at a given height + Callbacks(ctx context.Context, in *QueryCallbacksRequest, opts ...grpc.CallOption) (*QueryCallbacksResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/rollapp.callback.v1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) EstimateCallbackFees(ctx context.Context, in *QueryEstimateCallbackFeesRequest, opts ...grpc.CallOption) (*QueryEstimateCallbackFeesResponse, error) { + out := new(QueryEstimateCallbackFeesResponse) + err := c.cc.Invoke(ctx, "/rollapp.callback.v1.Query/EstimateCallbackFees", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Callbacks(ctx context.Context, in *QueryCallbacksRequest, opts ...grpc.CallOption) (*QueryCallbacksResponse, error) { + out := new(QueryCallbacksResponse) + err := c.cc.Invoke(ctx, "/rollapp.callback.v1.Query/Callbacks", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Params returns module parameters + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // EstimateCallbackFees returns the total amount of callback fees a contract needs to pay to register the callback + EstimateCallbackFees(context.Context, *QueryEstimateCallbackFeesRequest) (*QueryEstimateCallbackFeesResponse, error) + // Callbacks returns all the callbacks registered at a given height + Callbacks(context.Context, *QueryCallbacksRequest) (*QueryCallbacksResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} +func (*UnimplementedQueryServer) EstimateCallbackFees(ctx context.Context, req *QueryEstimateCallbackFeesRequest) (*QueryEstimateCallbackFeesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method EstimateCallbackFees not implemented") +} +func (*UnimplementedQueryServer) Callbacks(ctx context.Context, req *QueryCallbacksRequest) (*QueryCallbacksResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Callbacks not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/rollapp.callback.v1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_EstimateCallbackFees_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryEstimateCallbackFeesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).EstimateCallbackFees(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/rollapp.callback.v1.Query/EstimateCallbackFees", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).EstimateCallbackFees(ctx, req.(*QueryEstimateCallbackFeesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Callbacks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryCallbacksRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Callbacks(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/rollapp.callback.v1.Query/Callbacks", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Callbacks(ctx, req.(*QueryCallbacksRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "rollapp.callback.v1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + { + MethodName: "EstimateCallbackFees", + Handler: _Query_EstimateCallbackFees_Handler, + }, + { + MethodName: "Callbacks", + Handler: _Query_Callbacks_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "rollapp/callback/v1/query.proto", +} + +func (m *QueryParamsRequest) 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 *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) 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 *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) 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 = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *QueryEstimateCallbackFeesRequest) 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 *QueryEstimateCallbackFeesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryEstimateCallbackFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.BlockHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.BlockHeight)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryEstimateCallbackFeesResponse) 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 *QueryEstimateCallbackFeesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryEstimateCallbackFeesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.FeeSplit != nil { + { + size, err := m.FeeSplit.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.TotalFees != nil { + { + size, err := m.TotalFees.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryCallbacksRequest) 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 *QueryCallbacksRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryCallbacksRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.BlockHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.BlockHeight)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryCallbacksResponse) 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 *QueryCallbacksResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryCallbacksResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Callbacks) > 0 { + for iNdEx := len(m.Callbacks) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Callbacks[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryEstimateCallbackFeesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BlockHeight != 0 { + n += 1 + sovQuery(uint64(m.BlockHeight)) + } + return n +} + +func (m *QueryEstimateCallbackFeesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.TotalFees != nil { + l = m.TotalFees.Size() + n += 1 + l + sovQuery(uint64(l)) + } + if m.FeeSplit != nil { + l = m.FeeSplit.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryCallbacksRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BlockHeight != 0 { + n += 1 + sovQuery(uint64(m.BlockHeight)) + } + return n +} + +func (m *QueryCallbacksResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Callbacks) > 0 { + for _, e := range m.Callbacks { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsResponse: 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 ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryEstimateCallbackFeesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryEstimateCallbackFeesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryEstimateCallbackFeesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockHeight", wireType) + } + m.BlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryEstimateCallbackFeesResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryEstimateCallbackFeesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryEstimateCallbackFeesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalFees", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TotalFees == nil { + m.TotalFees = &types.Coin{} + } + if err := m.TotalFees.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeeSplit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.FeeSplit == nil { + m.FeeSplit = &CallbackFeesFeeSplit{} + } + if err := m.FeeSplit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryCallbacksRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryCallbacksRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryCallbacksRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockHeight", wireType) + } + m.BlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryCallbacksResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryCallbacksResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryCallbacksResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Callbacks", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Callbacks = append(m.Callbacks, &Callback{}) + if err := m.Callbacks[len(m.Callbacks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQuery(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, ErrIntOverflowQuery + } + 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, ErrIntOverflowQuery + } + 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, ErrIntOverflowQuery + } + 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, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/callback/types/query.pb.gw.go b/x/callback/types/query.pb.gw.go new file mode 100644 index 0000000..cebe3d1 --- /dev/null +++ b/x/callback/types/query.pb.gw.go @@ -0,0 +1,319 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: rollapp/callback/v1/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage +var _ = metadata.Join + +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_EstimateCallbackFees_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_EstimateCallbackFees_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryEstimateCallbackFeesRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_EstimateCallbackFees_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.EstimateCallbackFees(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_EstimateCallbackFees_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryEstimateCallbackFeesRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_EstimateCallbackFees_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.EstimateCallbackFees(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_Callbacks_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_Callbacks_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryCallbacksRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_Callbacks_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Callbacks(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Callbacks_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryCallbacksRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_Callbacks_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Callbacks(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_EstimateCallbackFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_EstimateCallbackFees_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_EstimateCallbackFees_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Callbacks_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Callbacks_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Callbacks_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "QueryClient" to call the correct interceptors. +func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Params_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_EstimateCallbackFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_EstimateCallbackFees_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_EstimateCallbackFees_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Callbacks_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Callbacks_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Callbacks_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"rollapp", "callback", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_EstimateCallbackFees_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"rollapp", "callback", "v1", "estimate_callback_fees"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_Callbacks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"rollapp", "callback", "v1", "callbacks"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Query_Params_0 = runtime.ForwardResponseMessage + + forward_Query_EstimateCallbackFees_0 = runtime.ForwardResponseMessage + + forward_Query_Callbacks_0 = runtime.ForwardResponseMessage +) diff --git a/x/callback/types/sudo_msg.go b/x/callback/types/sudo_msg.go new file mode 100644 index 0000000..c349cfd --- /dev/null +++ b/x/callback/types/sudo_msg.go @@ -0,0 +1,39 @@ +package types + +import "encoding/json" + +// SudoMsg callback message sent to a contract. +// This is encoded as JSON input to the contract when executing the callback +type SudoMsg struct { + // Callback is the endpoint name at the contract which is called + Callback *CallbackMsg `json:"callback,omitempty"` +} + +// CallbackMsg is the callback message sent to a contract. +type CallbackMsg struct { + // JobID is the user specified job id + JobID uint64 `json:"job_id"` +} + +// NewCallback creates a new Callback instance. +func NewCallbackMsg(jobID uint64) SudoMsg { + return SudoMsg{ + Callback: &CallbackMsg{ + JobID: jobID, + }, + } +} + +// Bytes returns the callback message as JSON bytes +func (s SudoMsg) Bytes() []byte { + msgBz, err := json.Marshal(s) + if err != nil { + panic(err) + } + return msgBz +} + +// String returns the callback message as JSON string +func (s SudoMsg) String() string { + return string(s.Bytes()) +} diff --git a/x/callback/types/sudo_msg_test.go b/x/callback/types/sudo_msg_test.go new file mode 100644 index 0000000..ad7967d --- /dev/null +++ b/x/callback/types/sudo_msg_test.go @@ -0,0 +1,31 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/dymensionxyz/rollapp-wasm/x/callback/types" +) + +func TestSudoMsgString(t *testing.T) { + testCases := []struct { + testCase string + msg types.SudoMsg + expectedMsg string + }{ + { + "ok: callback job_id is 1", + types.NewCallbackMsg(1), + `{"callback":{"job_id":1}}`, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.testCase, func(t *testing.T) { + res := tc.msg.String() + require.EqualValues(t, tc.expectedMsg, res) + }) + } +} diff --git a/x/callback/types/tx.pb.go b/x/callback/types/tx.pb.go new file mode 100644 index 0000000..a2cd4fd --- /dev/null +++ b/x/callback/types/tx.pb.go @@ -0,0 +1,1231 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: rollapp/callback/v1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/msgservice" + _ "github.com/cosmos/gogoproto/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 + +// MsgRequestCallback is the Msg/RequestCallback request type. +type MsgRequestCallback struct { + // sender is the address who is requesting the callback (bech32 encoded) + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + // contract_address is the address of the contract which is requesting the callback (bech32 encoded) + ContractAddress string `protobuf:"bytes,2,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` + // job_id is an identifier the callback requestor can pass in to identify the callback when it happens + JobId uint64 `protobuf:"varint,3,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` + // callback_height is the height at which the callback is executed. + CallbackHeight int64 `protobuf:"varint,4,opt,name=callback_height,json=callbackHeight,proto3" json:"callback_height,omitempty"` + // fees is the amount of fees being paid to register the contract + Fees types.Coin `protobuf:"bytes,5,opt,name=fees,proto3" json:"fees"` +} + +func (m *MsgRequestCallback) Reset() { *m = MsgRequestCallback{} } +func (m *MsgRequestCallback) String() string { return proto.CompactTextString(m) } +func (*MsgRequestCallback) ProtoMessage() {} +func (*MsgRequestCallback) Descriptor() ([]byte, []int) { + return fileDescriptor_2a0ba37147abd0c2, []int{0} +} +func (m *MsgRequestCallback) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRequestCallback) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRequestCallback.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 *MsgRequestCallback) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRequestCallback.Merge(m, src) +} +func (m *MsgRequestCallback) XXX_Size() int { + return m.Size() +} +func (m *MsgRequestCallback) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRequestCallback.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRequestCallback proto.InternalMessageInfo + +func (m *MsgRequestCallback) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgRequestCallback) GetContractAddress() string { + if m != nil { + return m.ContractAddress + } + return "" +} + +func (m *MsgRequestCallback) GetJobId() uint64 { + if m != nil { + return m.JobId + } + return 0 +} + +func (m *MsgRequestCallback) GetCallbackHeight() int64 { + if m != nil { + return m.CallbackHeight + } + return 0 +} + +func (m *MsgRequestCallback) GetFees() types.Coin { + if m != nil { + return m.Fees + } + return types.Coin{} +} + +// MsgRequestCallbackResponse defines the response structure for executing a MsgRequestCallback message. +type MsgRequestCallbackResponse struct { +} + +func (m *MsgRequestCallbackResponse) Reset() { *m = MsgRequestCallbackResponse{} } +func (m *MsgRequestCallbackResponse) String() string { return proto.CompactTextString(m) } +func (*MsgRequestCallbackResponse) ProtoMessage() {} +func (*MsgRequestCallbackResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_2a0ba37147abd0c2, []int{1} +} +func (m *MsgRequestCallbackResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRequestCallbackResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRequestCallbackResponse.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 *MsgRequestCallbackResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRequestCallbackResponse.Merge(m, src) +} +func (m *MsgRequestCallbackResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgRequestCallbackResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRequestCallbackResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRequestCallbackResponse proto.InternalMessageInfo + +// MsgCancelCallback is the Msg/CancelCallback request type. +type MsgCancelCallback struct { + // sender is the address of the contract which is cancelling the callback (bech32 encoded) + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + // contract_address is the address of the contract (bech32 encoded) + ContractAddress string `protobuf:"bytes,2,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` + // job_id is an identifier the callback requestor had passed during registration of the callback + JobId uint64 `protobuf:"varint,3,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` + // callback_height is the height at which the callback requestor had registered the callback + CallbackHeight int64 `protobuf:"varint,4,opt,name=callback_height,json=callbackHeight,proto3" json:"callback_height,omitempty"` +} + +func (m *MsgCancelCallback) Reset() { *m = MsgCancelCallback{} } +func (m *MsgCancelCallback) String() string { return proto.CompactTextString(m) } +func (*MsgCancelCallback) ProtoMessage() {} +func (*MsgCancelCallback) Descriptor() ([]byte, []int) { + return fileDescriptor_2a0ba37147abd0c2, []int{2} +} +func (m *MsgCancelCallback) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCancelCallback) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCancelCallback.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 *MsgCancelCallback) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCancelCallback.Merge(m, src) +} +func (m *MsgCancelCallback) XXX_Size() int { + return m.Size() +} +func (m *MsgCancelCallback) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCancelCallback.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCancelCallback proto.InternalMessageInfo + +func (m *MsgCancelCallback) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgCancelCallback) GetContractAddress() string { + if m != nil { + return m.ContractAddress + } + return "" +} + +func (m *MsgCancelCallback) GetJobId() uint64 { + if m != nil { + return m.JobId + } + return 0 +} + +func (m *MsgCancelCallback) GetCallbackHeight() int64 { + if m != nil { + return m.CallbackHeight + } + return 0 +} + +// MsgCancelCallbackResponse defines the response structure for executing a MsgCancelCallback message. +type MsgCancelCallbackResponse struct { + // refund is the amount of fees being refunded due to the cancellation of the callback + Refund types.Coin `protobuf:"bytes,1,opt,name=refund,proto3" json:"refund"` +} + +func (m *MsgCancelCallbackResponse) Reset() { *m = MsgCancelCallbackResponse{} } +func (m *MsgCancelCallbackResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCancelCallbackResponse) ProtoMessage() {} +func (*MsgCancelCallbackResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_2a0ba37147abd0c2, []int{3} +} +func (m *MsgCancelCallbackResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCancelCallbackResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCancelCallbackResponse.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 *MsgCancelCallbackResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCancelCallbackResponse.Merge(m, src) +} +func (m *MsgCancelCallbackResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCancelCallbackResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCancelCallbackResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCancelCallbackResponse proto.InternalMessageInfo + +func (m *MsgCancelCallbackResponse) GetRefund() types.Coin { + if m != nil { + return m.Refund + } + return types.Coin{} +} + +func init() { + proto.RegisterType((*MsgRequestCallback)(nil), "rollapp.callback.v1.MsgRequestCallback") + proto.RegisterType((*MsgRequestCallbackResponse)(nil), "rollapp.callback.v1.MsgRequestCallbackResponse") + proto.RegisterType((*MsgCancelCallback)(nil), "rollapp.callback.v1.MsgCancelCallback") + proto.RegisterType((*MsgCancelCallbackResponse)(nil), "rollapp.callback.v1.MsgCancelCallbackResponse") +} + +func init() { proto.RegisterFile("rollapp/callback/v1/tx.proto", fileDescriptor_2a0ba37147abd0c2) } + +var fileDescriptor_2a0ba37147abd0c2 = []byte{ + // 459 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x93, 0x4f, 0x6f, 0xd3, 0x30, + 0x18, 0xc6, 0x6b, 0xda, 0x55, 0xc2, 0x93, 0x56, 0x30, 0xff, 0xb2, 0x68, 0x0a, 0x55, 0x0f, 0xac, + 0x20, 0x61, 0xab, 0x9b, 0x10, 0x12, 0x37, 0xd6, 0x0b, 0x1c, 0x2a, 0xa4, 0x88, 0x13, 0x97, 0xca, + 0x71, 0x3c, 0x37, 0x5b, 0x62, 0x87, 0xbc, 0x6e, 0x69, 0x39, 0xf2, 0x09, 0xf8, 0x0e, 0x7c, 0x01, + 0x3e, 0xc6, 0x8e, 0x3b, 0x82, 0x84, 0x10, 0x6a, 0x0f, 0x7c, 0x0d, 0xd4, 0xd4, 0x19, 0xda, 0x3a, + 0xa4, 0x1e, 0xb9, 0x25, 0xef, 0xf3, 0x53, 0xde, 0xe7, 0x79, 0x62, 0xe3, 0xbd, 0xc2, 0xa4, 0x29, + 0xcf, 0x73, 0x26, 0x78, 0x9a, 0x46, 0x5c, 0x9c, 0xb2, 0x49, 0x8f, 0xd9, 0x29, 0xcd, 0x0b, 0x63, + 0x0d, 0xb9, 0xe3, 0x54, 0x5a, 0xa9, 0x74, 0xd2, 0xf3, 0xef, 0x2a, 0xa3, 0x4c, 0xa9, 0xb3, 0xe5, + 0xd3, 0x0a, 0xf5, 0x03, 0x61, 0x20, 0x33, 0xc0, 0x22, 0x0e, 0x92, 0x4d, 0x7a, 0x91, 0xb4, 0xbc, + 0xc7, 0x84, 0x49, 0xb4, 0xd3, 0x1f, 0x38, 0x3d, 0x03, 0xb5, 0x5c, 0x91, 0x81, 0x72, 0x42, 0xe7, + 0x3a, 0x07, 0x17, 0xfb, 0x4a, 0xa6, 0xf3, 0x1d, 0x61, 0x32, 0x00, 0x15, 0xca, 0xf7, 0x63, 0x09, + 0xb6, 0xef, 0x44, 0x72, 0x1f, 0x37, 0x41, 0xea, 0x58, 0x16, 0x1e, 0x6a, 0xa3, 0xee, 0xcd, 0xd0, + 0xbd, 0x91, 0xc7, 0xf8, 0x96, 0x30, 0xda, 0x16, 0x5c, 0xd8, 0x21, 0x8f, 0xe3, 0x42, 0x02, 0x78, + 0x37, 0x4a, 0xa2, 0x55, 0xcd, 0x5f, 0xae, 0xc6, 0xe4, 0x1e, 0x6e, 0x9e, 0x98, 0x68, 0x98, 0xc4, + 0x5e, 0xbd, 0x8d, 0xba, 0x8d, 0x70, 0xeb, 0xc4, 0x44, 0xaf, 0x63, 0xb2, 0x8f, 0x5b, 0x95, 0x85, + 0xe1, 0x48, 0x26, 0x6a, 0x64, 0xbd, 0x46, 0x1b, 0x75, 0xeb, 0xe1, 0x4e, 0x35, 0x7e, 0x55, 0x4e, + 0xc9, 0x21, 0x6e, 0x1c, 0x4b, 0x09, 0xde, 0x56, 0x1b, 0x75, 0xb7, 0x0f, 0x76, 0xe9, 0x2a, 0x25, + 0x5d, 0xb6, 0x40, 0x5d, 0x0b, 0xb4, 0x6f, 0x12, 0x7d, 0xd4, 0x38, 0xfb, 0xf9, 0xb0, 0x16, 0x96, + 0xf0, 0x8b, 0xed, 0x4f, 0xbf, 0xbf, 0x3e, 0x71, 0x66, 0x3b, 0x7b, 0xd8, 0x5f, 0x8f, 0x16, 0x4a, + 0xc8, 0x8d, 0x06, 0xd9, 0xf9, 0x82, 0xf0, 0xed, 0x01, 0xa8, 0x3e, 0xd7, 0x42, 0xa6, 0xff, 0x51, + 0xf0, 0xcb, 0x19, 0xde, 0xe2, 0xdd, 0x35, 0x93, 0x55, 0x04, 0xf2, 0x1c, 0x37, 0x0b, 0x79, 0x3c, + 0xd6, 0x71, 0x69, 0x76, 0x83, 0x92, 0x1c, 0x7e, 0xf0, 0x03, 0xe1, 0xfa, 0x00, 0x14, 0x39, 0xc5, + 0xad, 0xab, 0x7f, 0x7e, 0x9f, 0x5e, 0x73, 0x32, 0xe9, 0x7a, 0x8f, 0x3e, 0xdb, 0x10, 0xbc, 0x70, + 0x3b, 0xc2, 0x3b, 0x57, 0xca, 0x7e, 0xf4, 0xaf, 0x4f, 0x5c, 0xe6, 0x7c, 0xba, 0x19, 0x57, 0x6d, + 0x3a, 0x7a, 0x73, 0x36, 0x0f, 0xd0, 0xf9, 0x3c, 0x40, 0xbf, 0xe6, 0x01, 0xfa, 0xbc, 0x08, 0x6a, + 0xe7, 0x8b, 0xa0, 0xf6, 0x6d, 0x11, 0xd4, 0xde, 0x3d, 0x53, 0x89, 0x1d, 0x8d, 0x23, 0x2a, 0x4c, + 0xc6, 0xe2, 0x59, 0x26, 0x35, 0x24, 0x46, 0x4f, 0x67, 0x1f, 0x99, 0x5b, 0xf0, 0xf4, 0x03, 0x87, + 0x8c, 0x4d, 0xff, 0xde, 0x18, 0x3b, 0xcb, 0x25, 0x44, 0xcd, 0xf2, 0xb2, 0x1c, 0xfe, 0x09, 0x00, + 0x00, 0xff, 0xff, 0x52, 0x75, 0xff, 0x2f, 0xd4, 0x03, 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 { + // RequestCallback defines a message for registering a callback at a specific height by a given contract + RequestCallback(ctx context.Context, in *MsgRequestCallback, opts ...grpc.CallOption) (*MsgRequestCallbackResponse, error) + // CancelCallback defines a message for cancelling an existing callback + CancelCallback(ctx context.Context, in *MsgCancelCallback, opts ...grpc.CallOption) (*MsgCancelCallbackResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) RequestCallback(ctx context.Context, in *MsgRequestCallback, opts ...grpc.CallOption) (*MsgRequestCallbackResponse, error) { + out := new(MsgRequestCallbackResponse) + err := c.cc.Invoke(ctx, "/rollapp.callback.v1.Msg/RequestCallback", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) CancelCallback(ctx context.Context, in *MsgCancelCallback, opts ...grpc.CallOption) (*MsgCancelCallbackResponse, error) { + out := new(MsgCancelCallbackResponse) + err := c.cc.Invoke(ctx, "/rollapp.callback.v1.Msg/CancelCallback", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // RequestCallback defines a message for registering a callback at a specific height by a given contract + RequestCallback(context.Context, *MsgRequestCallback) (*MsgRequestCallbackResponse, error) + // CancelCallback defines a message for cancelling an existing callback + CancelCallback(context.Context, *MsgCancelCallback) (*MsgCancelCallbackResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) RequestCallback(ctx context.Context, req *MsgRequestCallback) (*MsgRequestCallbackResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RequestCallback not implemented") +} +func (*UnimplementedMsgServer) CancelCallback(ctx context.Context, req *MsgCancelCallback) (*MsgCancelCallbackResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CancelCallback not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_RequestCallback_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgRequestCallback) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).RequestCallback(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/rollapp.callback.v1.Msg/RequestCallback", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).RequestCallback(ctx, req.(*MsgRequestCallback)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_CancelCallback_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCancelCallback) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CancelCallback(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/rollapp.callback.v1.Msg/CancelCallback", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CancelCallback(ctx, req.(*MsgCancelCallback)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "rollapp.callback.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "RequestCallback", + Handler: _Msg_RequestCallback_Handler, + }, + { + MethodName: "CancelCallback", + Handler: _Msg_CancelCallback_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "rollapp/callback/v1/tx.proto", +} + +func (m *MsgRequestCallback) 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 *MsgRequestCallback) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRequestCallback) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Fees.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if m.CallbackHeight != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.CallbackHeight)) + i-- + dAtA[i] = 0x20 + } + if m.JobId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.JobId)) + i-- + dAtA[i] = 0x18 + } + if len(m.ContractAddress) > 0 { + i -= len(m.ContractAddress) + copy(dAtA[i:], m.ContractAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.ContractAddress))) + i-- + dAtA[i] = 0x12 + } + 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 *MsgRequestCallbackResponse) 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 *MsgRequestCallbackResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRequestCallbackResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgCancelCallback) 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 *MsgCancelCallback) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCancelCallback) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.CallbackHeight != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.CallbackHeight)) + i-- + dAtA[i] = 0x20 + } + if m.JobId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.JobId)) + i-- + dAtA[i] = 0x18 + } + if len(m.ContractAddress) > 0 { + i -= len(m.ContractAddress) + copy(dAtA[i:], m.ContractAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.ContractAddress))) + i-- + dAtA[i] = 0x12 + } + 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 *MsgCancelCallbackResponse) 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 *MsgCancelCallbackResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCancelCallbackResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Refund.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + 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 *MsgRequestCallback) 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)) + } + l = len(m.ContractAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.JobId != 0 { + n += 1 + sovTx(uint64(m.JobId)) + } + if m.CallbackHeight != 0 { + n += 1 + sovTx(uint64(m.CallbackHeight)) + } + l = m.Fees.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgRequestCallbackResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgCancelCallback) 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)) + } + l = len(m.ContractAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.JobId != 0 { + n += 1 + sovTx(uint64(m.JobId)) + } + if m.CallbackHeight != 0 { + n += 1 + sovTx(uint64(m.CallbackHeight)) + } + return n +} + +func (m *MsgCancelCallbackResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Refund.Size() + 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 *MsgRequestCallback) 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: MsgRequestCallback: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRequestCallback: 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 != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractAddress", 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.ContractAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field JobId", wireType) + } + m.JobId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.JobId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CallbackHeight", wireType) + } + m.CallbackHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CallbackHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Fees", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Fees.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + 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 *MsgRequestCallbackResponse) 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: MsgRequestCallbackResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRequestCallbackResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + 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 *MsgCancelCallback) 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: MsgCancelCallback: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCancelCallback: 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 != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractAddress", 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.ContractAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field JobId", wireType) + } + m.JobId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.JobId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CallbackHeight", wireType) + } + m.CallbackHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CallbackHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *MsgCancelCallbackResponse) 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: MsgCancelCallbackResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCancelCallbackResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Refund", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Refund.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + 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/callback/types/types.go b/x/callback/types/types.go new file mode 100644 index 0000000..ab1254f --- /dev/null +++ b/x/callback/types/types.go @@ -0,0 +1 @@ +package types diff --git a/x/cwerrors/abci.go b/x/cwerrors/abci.go new file mode 100644 index 0000000..e302d5f --- /dev/null +++ b/x/cwerrors/abci.go @@ -0,0 +1,60 @@ +package cwerrors + +import ( + abci "github.com/tendermint/tendermint/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/dymensionxyz/rollapp-wasm/pkg" + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/keeper" + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +const ErrorCallbackGasLimit = 150_000 + +// EndBlocker is called every block, and prunes errors that are older than the current block height. +func EndBlocker(ctx sdk.Context, k keeper.Keeper, wk types.WasmKeeperExpected) []abci.ValidatorUpdate { + // Iterate over all errors (with callback subscription) and execute the error callback for each error + k.IterateSudoErrorCallbacks(ctx, sudoErrorCallbackExec(ctx, k, wk)) + // Prune any error callback subscripitons that have expired in the current block height + if err := k.PruneSubscriptionsEndBlock(ctx); err != nil { + panic(err) + } + // Prune any errors(in state) that have expired in the current block height + if err := k.PruneErrorsCurrentBlock(ctx); err != nil { + panic(err) + } + + return nil +} + +func sudoErrorCallbackExec(ctx sdk.Context, k keeper.Keeper, wk types.WasmKeeperExpected) func(types.SudoError) bool { + return func(sudoError types.SudoError) bool { + contractAddr := sdk.MustAccAddressFromBech32(sudoError.ContractAddress) + + sudoMsg := types.NewSudoMsg(sudoError) + _, err := pkg.ExecuteWithGasLimit(ctx, ErrorCallbackGasLimit, func(ctx sdk.Context) error { + _, err := wk.Sudo(ctx, contractAddr, sudoMsg.Bytes()) + return err + }) + if err != nil { + // In case Sudo error, such as out of gas, emit an event and store the error in state (so that the error is not lost) + types.EmitSudoErrorCallbackFailedEvent( + ctx, + sudoError, + err.Error(), + ) + newSudoErr := types.SudoError{ + ModuleName: types.ModuleName, + ContractAddress: sudoError.ContractAddress, + ErrorCode: int32(types.ModuleErrors_ERR_CALLBACK_EXECUTION_FAILED), + InputPayload: string(sudoError.Bytes()), + ErrorMessage: err.Error(), + } + err = k.StoreErrorInState(ctx, contractAddr, newSudoErr) + if err != nil { + panic(err) + } + } + return false + } +} diff --git a/x/cwerrors/abci_test.go b/x/cwerrors/abci_test.go new file mode 100644 index 0000000..548f9fa --- /dev/null +++ b/x/cwerrors/abci_test.go @@ -0,0 +1,140 @@ +package cwerrors_test + +import ( + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + e2eTesting "github.com/dymensionxyz/rollapp-wasm/e2e/testing" + "github.com/dymensionxyz/rollapp-wasm/pkg/testutils" + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +func TestEndBlocker(t *testing.T) { + chain := e2eTesting.NewTestChain(t, 1) + ctx, keeper := chain.GetContext(), chain.GetApp().CWErrorsKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + contractAddresses := e2eTesting.GenContractAddresses(3) + contractAddr := contractAddresses[0] + contractAddr2 := contractAddresses[1] + contractAdminAcc := chain.GetAccount(0) + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + contractViewer.AddContractAdmin( + contractAddr2.String(), + contractAdminAcc.Address.String(), + ) + params := types.Params{ + ErrorStoredTime: 5, + SubscriptionFee: sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(0)), + SubscriptionPeriod: 5, + } + err := keeper.SetParams(ctx, params) + require.NoError(t, err) + + chain.NextBlock(1) + + // Set errors for block 1 + contract1Err := types.SudoError{ + ContractAddress: contractAddr.String(), + ModuleName: "test", + } + contract2Err := types.SudoError{ + ContractAddress: contractAddr2.String(), + ModuleName: "test", + } + err = keeper.SetError(chain.GetContext(), contract1Err) + require.NoError(t, err) + err = keeper.SetError(chain.GetContext(), contract1Err) + require.NoError(t, err) + err = keeper.SetError(chain.GetContext(), contract2Err) + require.NoError(t, err) + + pruneHeight := chain.GetContext().BlockHeight() + params.ErrorStoredTime + + // Increment block height + chain.NextBlock(1) + + // Set errors for block 2 + err = keeper.SetError(chain.GetContext(), contract1Err) + require.NoError(t, err) + err = keeper.SetError(chain.GetContext(), contract2Err) + require.NoError(t, err) + err = keeper.SetError(chain.GetContext(), contract2Err) + require.NoError(t, err) + + // Check number of errors match + sudoErrs, err := keeper.GetErrorsByContractAddress(chain.GetContext(), contractAddr.Bytes()) + require.NoError(t, err) + require.Len(t, sudoErrs, 3) + sudoErrs, err = keeper.GetErrorsByContractAddress(chain.GetContext(), contractAddr2.Bytes()) + require.NoError(t, err) + require.Len(t, sudoErrs, 3) + + // Go to prune height & execute being&endblockers + chain.GoToHeight(pruneHeight, time.Duration(pruneHeight)) + + // Check number of errors match + sudoErrs, err = keeper.GetErrorsByContractAddress(chain.GetContext(), contractAddr.Bytes()) + require.NoError(t, err) + require.Len(t, sudoErrs, 1) + sudoErrs, err = keeper.GetErrorsByContractAddress(chain.GetContext(), contractAddr2.Bytes()) + require.NoError(t, err) + require.Len(t, sudoErrs, 2) + + // Go to next block & execute being&endblockers + chain.NextBlock(1) + + // Check number of errors match + sudoErrs, err = keeper.GetErrorsByContractAddress(chain.GetContext(), contractAddr.Bytes()) + require.NoError(t, err) + require.Len(t, sudoErrs, 0) + sudoErrs, err = keeper.GetErrorsByContractAddress(chain.GetContext(), contractAddr2.Bytes()) + require.NoError(t, err) + require.Len(t, sudoErrs, 0) + + // Setup subscription + expiryTime, err := keeper.SetSubscription(chain.GetContext(), contractAdminAcc.Address, contractAddr, sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)) + require.NoError(t, err) + require.Equal(t, chain.GetContext().BlockHeight()+params.SubscriptionPeriod, expiryTime) + + // Go to next block + chain.NextBlock(1) + + // Set an error which should be called as callback + err = keeper.SetError(chain.GetContext(), contract1Err) + require.NoError(t, err) + // Set an error for a contract which has no subscription + err = keeper.SetError(chain.GetContext(), contract2Err) + require.NoError(t, err) + + // Should be empty as the is stored for error callback + sudoErrs, err = keeper.GetErrorsByContractAddress(chain.GetContext(), contractAddr.Bytes()) + require.NoError(t, err) + require.Len(t, sudoErrs, 0) + // Second error should still be stored in state + sudoErrs, err = keeper.GetErrorsByContractAddress(chain.GetContext(), contractAddr2.Bytes()) + require.NoError(t, err) + require.Len(t, sudoErrs, 1) + + // Should be queued for callback + sudoErrs = keeper.GetAllSudoErrorCallbacks(chain.GetContext()) + require.Len(t, sudoErrs, 1) + + // Execute endblocker & execute being&endblockers + chain.NextBlock(1) + + // Check number of errors match + sudoErrs = keeper.GetAllSudoErrorCallbacks(chain.GetContext()) + require.Len(t, sudoErrs, 0) + + // Ensure errors in state persist and are not purged + sudoErrs, err = keeper.GetErrorsByContractAddress(chain.GetContext(), contractAddr2.Bytes()) + require.NoError(t, err) + require.Len(t, sudoErrs, 1) +} diff --git a/x/cwerrors/client/cli/query.go b/x/cwerrors/client/cli/query.go new file mode 100644 index 0000000..85f11db --- /dev/null +++ b/x/cwerrors/client/cli/query.go @@ -0,0 +1,105 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/spf13/cobra" + + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +// GetQueryCmd builds query command group for the module. +func GetQueryCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the callback module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + cmd.AddCommand( + getQueryErrorsCmd(), + getQueryIsSubscribedCmd(), + getQueryParamsCmd(), + ) + return cmd +} + +// getQueryErrorsCmd returns the command to query errors for a contract address. +func getQueryErrorsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "errors [contract_address]", + Args: cobra.ExactArgs(1), + Short: "Query errors for a contract address", + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.Errors(cmd.Context(), &types.QueryErrorsRequest{ + ContractAddress: args[0], + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + flags.AddQueryFlagsToCmd(cmd) + return cmd +} + +// getQueryIsSubscribedCmd returns the command to query if a contract address is subscribed to errors. +func getQueryIsSubscribedCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "is-subscribed [contract_address]", + Args: cobra.ExactArgs(1), + Short: "Query if a contract address is subscribed to errors", + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.IsSubscribed(cmd.Context(), &types.QueryIsSubscribedRequest{ + ContractAddress: args[0], + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + flags.AddQueryFlagsToCmd(cmd) + return cmd +} + +// getQueryParamsCmd returns the command to query module parameters. +func getQueryParamsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Args: cobra.NoArgs, + Short: "Query module parameters", + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.Params(cmd.Context(), &types.QueryParamsRequest{}) + if err != nil { + return err + } + + return clientCtx.PrintProto(&res.Params) + }, + } + flags.AddQueryFlagsToCmd(cmd) + return cmd +} diff --git a/x/cwerrors/client/cli/tx.go b/x/cwerrors/client/cli/tx.go new file mode 100644 index 0000000..65f0f30 --- /dev/null +++ b/x/cwerrors/client/cli/tx.go @@ -0,0 +1,61 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/spf13/cobra" + + "github.com/dymensionxyz/rollapp-wasm/pkg" + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +// GetTxCmd builds tx command group for the module. +func GetTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Transaction commands for the cwerrors module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + cmd.AddCommand( + getTxSubscribeToErrorCmd(), + ) + + return cmd +} + +// getTxSubscribeToErrorCmd returns the command to subscribe to error callbacks for a contract address. +func getTxSubscribeToErrorCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "subscribe-to-error [contract-address] [fee-amount]", + Args: cobra.ExactArgs(2), + Short: "Subscribe to error callbacks for a contract address", + Aliases: []string{"subscribe"}, + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + senderAddr := clientCtx.GetFromAddress() + + fees, err := pkg.ParseCoinArg("fee-amount", args[1]) + if err != nil { + return err + } + + msg := types.MsgSubscribeToError{ + Sender: senderAddr.String(), + ContractAddress: args[0], + Fee: fees, + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/cwerrors/genesis.go b/x/cwerrors/genesis.go new file mode 100644 index 0000000..b4d7af8 --- /dev/null +++ b/x/cwerrors/genesis.go @@ -0,0 +1,31 @@ +package cwerrors + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/keeper" + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +// InitGenesis initializes the module genesis state. +func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { + params := genState.Params + if err := k.Params.Set(ctx, params); err != nil { + panic(err) + } +} + +// ExportGenesis exports the module genesis for the current block. +func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { + params, err := k.Params.Get(ctx) + if err != nil { + panic(err) + } + sudoErrs, err := k.ExportErrors(ctx) + if err != nil { + panic(err) + } + genesis := types.NewGenesisState(params) + genesis.Errors = sudoErrs + return genesis +} diff --git a/x/cwerrors/genesis_test.go b/x/cwerrors/genesis_test.go new file mode 100644 index 0000000..31957ce --- /dev/null +++ b/x/cwerrors/genesis_test.go @@ -0,0 +1,84 @@ +package cwerrors_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + + e2eTesting "github.com/dymensionxyz/rollapp-wasm/e2e/testing" + "github.com/dymensionxyz/rollapp-wasm/pkg/testutils" + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors" + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +func TestExportGenesis(t *testing.T) { + chain := e2eTesting.NewTestChain(t, 1) + ctx, keeper := chain.GetContext(), chain.GetApp().CWErrorsKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + contractAddresses := e2eTesting.GenContractAddresses(3) + contractAddr := contractAddresses[0] + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAddr.String(), + ) + err := keeper.SetError(ctx, types.SudoError{ContractAddress: contractAddr.String(), ModuleName: "test"}) + require.NoError(t, err) + err = keeper.SetError(ctx, types.SudoError{ContractAddress: contractAddr.String(), ModuleName: "test"}) + require.NoError(t, err) + + exportedState := cwerrors.ExportGenesis(ctx, keeper) + require.Equal(t, types.DefaultParams(), exportedState.Params) + require.Len(t, exportedState.Errors, 2) + + newParams := types.Params{ + ErrorStoredTime: 99999, + SubscriptionFee: sdk.NewCoin("stake", sdk.NewInt(100)), + SubscriptionPeriod: 1, + } + err = keeper.SetParams(ctx, newParams) + require.NoError(t, err) + + exportedState = cwerrors.ExportGenesis(ctx, keeper) + require.Equal(t, newParams.ErrorStoredTime, exportedState.Params.ErrorStoredTime) + require.Equal(t, newParams.SubscriptionFee, exportedState.Params.SubscriptionFee) + require.Equal(t, newParams.SubscriptionPeriod, exportedState.Params.SubscriptionPeriod) +} + +func TestInitGenesis(t *testing.T) { + chain := e2eTesting.NewTestChain(t, 1) + ctx, keeper := chain.GetContext(), chain.GetApp().CWErrorsKeeper + + genstate := types.GenesisState{ + Params: types.DefaultGenesis().Params, + } + cwerrors.InitGenesis(ctx, keeper, genstate) + + params, err := keeper.GetParams(ctx) + require.NoError(t, err) + require.Equal(t, types.DefaultParams(), params) + + genstate = types.GenesisState{ + Params: types.Params{ + ErrorStoredTime: 99999, + SubscriptionFee: sdk.NewCoin("stake", sdk.NewInt(100)), + SubscriptionPeriod: 1, + }, + Errors: []types.SudoError{ + {ContractAddress: "addr1", ModuleName: "test"}, + }, + } + cwerrors.InitGenesis(ctx, keeper, genstate) + + params, err = keeper.GetParams(ctx) + require.NoError(t, err) + require.Equal(t, genstate.Params.ErrorStoredTime, params.ErrorStoredTime) + require.Equal(t, genstate.Params.SubscriptionFee, params.SubscriptionFee) + require.Equal(t, genstate.Params.SubscriptionPeriod, params.SubscriptionPeriod) + + sudoErrs, err := keeper.ExportErrors(ctx) + require.NoError(t, err) + require.Len(t, sudoErrs, 0) // We only export errors and dont import them as they wont be relevant +} diff --git a/x/cwerrors/keeper/grpc_query.go b/x/cwerrors/keeper/grpc_query.go new file mode 100644 index 0000000..6119a6c --- /dev/null +++ b/x/cwerrors/keeper/grpc_query.go @@ -0,0 +1,80 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +var _ types.QueryServer = &QueryServer{} + +// QueryServer implements the module gRPC query service. +type QueryServer struct { + keeper Keeper +} + +// NewQueryServer creates a new gRPC query server. +func NewQueryServer(keeper Keeper) *QueryServer { + return &QueryServer{ + keeper: keeper, + } +} + +// Errors implements types.QueryServer. +func (qs *QueryServer) Errors(c context.Context, request *types.QueryErrorsRequest) (*types.QueryErrorsResponse, error) { + if request == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + contractAddr, err := sdk.AccAddressFromBech32(request.ContractAddress) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid contract address: %s", err.Error()) + } + + errors, err := qs.keeper.GetErrorsByContractAddress(sdk.UnwrapSDKContext(c), contractAddr) + if err != nil { + return nil, status.Errorf(codes.NotFound, "could not fetch the errors: %s", err.Error()) + } + + return &types.QueryErrorsResponse{ + Errors: errors, + }, nil +} + +// IsSubscribed implements types.QueryServer. +func (qs *QueryServer) IsSubscribed(c context.Context, request *types.QueryIsSubscribedRequest) (*types.QueryIsSubscribedResponse, error) { + if request == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + contractAddr, err := sdk.AccAddressFromBech32(request.ContractAddress) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid contract address: %s", err.Error()) + } + + hasSub, validtill := qs.keeper.GetSubscription(sdk.UnwrapSDKContext(c), contractAddr) + return &types.QueryIsSubscribedResponse{ + Subscribed: hasSub, + SubscriptionValidTill: validtill, + }, nil +} + +// Params implements types.QueryServer. +func (qs *QueryServer) Params(c context.Context, request *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + if request == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + params, err := qs.keeper.GetParams(sdk.UnwrapSDKContext(c)) + if err != nil { + return nil, status.Errorf(codes.NotFound, "could not fetch the module params: %s", err.Error()) + } + + return &types.QueryParamsResponse{ + Params: params, + }, nil +} diff --git a/x/cwerrors/keeper/grpc_query_test.go b/x/cwerrors/keeper/grpc_query_test.go new file mode 100644 index 0000000..64b4883 --- /dev/null +++ b/x/cwerrors/keeper/grpc_query_test.go @@ -0,0 +1,136 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + e2eTesting "github.com/dymensionxyz/rollapp-wasm/e2e/testing" + "github.com/dymensionxyz/rollapp-wasm/pkg/testutils" + cwerrorsKeeper "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/keeper" + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +func (s *KeeperTestSuite) TestErrors() { + ctx, keeper := s.chain.GetContext(), s.chain.GetApp().CWErrorsKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + contractAddresses := e2eTesting.GenContractAddresses(3) + contractAddr := contractAddresses[0] + contractAddr2 := contractAddresses[1] + contractAdminAcc := s.chain.GetAccount(0) + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + contractViewer.AddContractAdmin( + contractAddr2.String(), + contractAdminAcc.Address.String(), + ) + + queryServer := cwerrorsKeeper.NewQueryServer(keeper) + + // Sending nil query + _, err := queryServer.Errors(sdk.WrapSDKContext(ctx), nil) + s.Require().Error(err) + + // Set errors for block 1 + // 2 errors for contract1 + // 1 error for contract2 + contract1Err := types.SudoError{ + ContractAddress: contractAddr.String(), + ModuleName: "test", + } + contract2Err := types.SudoError{ + ContractAddress: contractAddr2.String(), + ModuleName: "test", + } + err = keeper.SetError(ctx, contract1Err) + s.Require().NoError(err) + err = keeper.SetError(ctx, contract1Err) + s.Require().NoError(err) + err = keeper.SetError(ctx, contract2Err) + s.Require().NoError(err) + + // Check number of errors match + res, err := queryServer.Errors(sdk.WrapSDKContext(ctx), &types.QueryErrorsRequest{ContractAddress: contractAddr.String()}) + s.Require().NoError(err) + s.Require().Len(res.Errors, 2) + res, err = queryServer.Errors(sdk.WrapSDKContext(ctx), &types.QueryErrorsRequest{ContractAddress: contractAddr2.String()}) + s.Require().NoError(err) + s.Require().Len(res.Errors, 1) + + // Increment block height + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // Set errors for block 2 + // 1 error for contract1 + // 1 error for contract2 + err = keeper.SetError(ctx, contract1Err) + s.Require().NoError(err) + err = keeper.SetError(ctx, contract2Err) + s.Require().NoError(err) + + // Check number of errors match + res, err = queryServer.Errors(sdk.WrapSDKContext(ctx), &types.QueryErrorsRequest{ContractAddress: contractAddr.String()}) + s.Require().NoError(err) + s.Require().Len(res.Errors, 3) + res, err = queryServer.Errors(sdk.WrapSDKContext(ctx), &types.QueryErrorsRequest{ContractAddress: contractAddr2.String()}) + s.Require().NoError(err) + s.Require().Len(res.Errors, 2) +} + +func (s *KeeperTestSuite) TestIsSubscribed() { + ctx, keeper := s.chain.GetContext(), s.chain.GetApp().CWErrorsKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + contractAddr := e2eTesting.GenContractAddresses(2)[0] + contractAdminAcc := s.chain.GetAccount(2) + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + queryServer := cwerrorsKeeper.NewQueryServer(keeper) + + // TEST CASE 1: empty request + _, err := queryServer.IsSubscribed(sdk.WrapSDKContext(ctx), nil) + s.Require().Error(err) + + // TEST CASE 2: invalid contract address + _, err = queryServer.IsSubscribed(sdk.WrapSDKContext(ctx), &types.QueryIsSubscribedRequest{ContractAddress: "👻"}) + s.Require().Error(err) + + // TEST CASE 3: subscription not found + res, err := queryServer.IsSubscribed(sdk.WrapSDKContext(ctx), &types.QueryIsSubscribedRequest{ContractAddress: contractAddr.String()}) + s.Require().NoError(err) + s.Require().False(res.Subscribed) + + // TEST CASE 4: subscription found + expectedEndHeight, err := keeper.SetSubscription(ctx, contractAdminAcc.Address, contractAddr, sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)) + s.Require().NoError(err) + res, err = queryServer.IsSubscribed(sdk.WrapSDKContext(ctx), &types.QueryIsSubscribedRequest{ContractAddress: contractAddr.String()}) + s.Require().NoError(err) + s.Require().True(res.Subscribed) + s.Require().Equal(expectedEndHeight, res.SubscriptionValidTill) +} + +func (s *KeeperTestSuite) TestParams() { + ctx, keeper := s.chain.GetContext(), s.chain.GetApp().CWErrorsKeeper + queryServer := cwerrorsKeeper.NewQueryServer(keeper) + + // Sending nil query + _, err := queryServer.Params(sdk.WrapSDKContext(ctx), nil) + s.Require().Error(err) + + // Set params + params := types.Params{ + ErrorStoredTime: 100, + SubscriptionFee: sdk.NewInt64Coin(sdk.DefaultBondDenom, 2), + SubscriptionPeriod: 100, + } + err = keeper.SetParams(ctx, params) + s.Require().NoError(err) + + // Query params + res, err := queryServer.Params(sdk.WrapSDKContext(ctx), &types.QueryParamsRequest{}) + s.Require().NoError(err) + s.Require().Equal(params, res.Params) +} diff --git a/x/cwerrors/keeper/keeper.go b/x/cwerrors/keeper/keeper.go new file mode 100644 index 0000000..9ffe50d --- /dev/null +++ b/x/cwerrors/keeper/keeper.go @@ -0,0 +1,116 @@ +package keeper + +import ( + "cosmossdk.io/collections" + "github.com/tendermint/tendermint/libs/log" + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/dymensionxyz/rollapp-wasm/internal/collcompat" + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +// Keeper provides module state operations. +type Keeper struct { + cdc codec.Codec + storeKey storetypes.StoreKey + tStoreKey storetypes.StoreKey + wasmKeeper types.WasmKeeperExpected + bankKeeper types.BankKeeperExpected + + Schema collections.Schema + + // Params key: ParamsKeyPrefix | value: Params + Params collections.Item[types.Params] + // ErrorID key: ErrorsCountKey | value: ErrorID + ErrorID collections.Sequence + // ContractErrors key: ContractErrorsKeyPrefix + contractAddress + ErrorId | value: nil + ContractErrors collections.Map[collections.Pair[[]byte, uint64], []byte] + // ContractErrors key: ErrorsKeyPrefix + ErrorId | value: SudoError + Errors collections.Map[uint64, types.SudoError] + // DeletionBlocks key: DeletionBlocksKeyPrefix + BlockHeight + ErrorId | value: nil + DeletionBlocks collections.Map[collections.Pair[int64, uint64], []byte] + // ContractSubscriptions key: ContractSubscriptionsKeyPrefix + contractAddress | value: deletionHeight + ContractSubscriptions collections.Map[[]byte, int64] + // SubscriptionEndBlock key: SubscriptionEndBlockKeyPrefix + BlockHeight + contractAddress | value: nil + SubscriptionEndBlock collections.Map[collections.Pair[int64, []byte], []byte] +} + +// NewKeeper creates a new Keeper instance. +func NewKeeper(cdc codec.Codec, storeKey storetypes.StoreKey, tStoreKey storetypes.StoreKey, + wk types.WasmKeeperExpected, bk types.BankKeeperExpected, +) Keeper { + sb := collections.NewSchemaBuilder(collcompat.NewKVStoreService(storeKey)) + k := Keeper{ + cdc: cdc, + storeKey: storeKey, + tStoreKey: tStoreKey, + wasmKeeper: wk, + bankKeeper: bk, + Params: collections.NewItem( + sb, + types.ParamsKeyPrefix, + "params", + collcompat.ProtoValue[types.Params](cdc), + ), + ErrorID: collections.NewSequence( + sb, + types.ErrorIDKey, + "errorID", + ), + ContractErrors: collections.NewMap( + sb, + types.ContractErrorsKeyPrefix, + "contractErrors", + collections.PairKeyCodec(collections.BytesKey, collections.Uint64Key), + collections.BytesValue, + ), + Errors: collections.NewMap( + sb, + types.ErrorsKeyPrefix, + "errors", + collections.Uint64Key, + collcompat.ProtoValue[types.SudoError](cdc), + ), + DeletionBlocks: collections.NewMap( + sb, + types.DeletionBlocksKeyPrefix, + "deletionBlocks", + collections.PairKeyCodec(collections.Int64Key, collections.Uint64Key), + collections.BytesValue, + ), + ContractSubscriptions: collections.NewMap( + sb, + types.ContractSubscriptionsKeyPrefix, + "contractSubscriptions", + collections.BytesKey, + collections.Int64Value, + ), + SubscriptionEndBlock: collections.NewMap( + sb, + types.SubscriptionEndBlockKeyPrefix, + "subscriptionEndBlock", + collections.PairKeyCodec(collections.Int64Key, collections.BytesKey), + collections.BytesValue, + ), + } + schema, err := sb.Build() + if err != nil { + panic(err) + } + k.Schema = schema + return k +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "x/"+types.ModuleName) +} + + +// SetWasmKeeper sets the given wasm keeper. +// Only for testing purposes +func (k *Keeper) SetWasmKeeper(wk types.WasmKeeperExpected) { + k.wasmKeeper = wk +} diff --git a/x/cwerrors/keeper/keeper_test.go b/x/cwerrors/keeper/keeper_test.go new file mode 100644 index 0000000..ec9900d --- /dev/null +++ b/x/cwerrors/keeper/keeper_test.go @@ -0,0 +1,23 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + e2eTesting "github.com/dymensionxyz/rollapp-wasm/e2e/testing" +) + +type KeeperTestSuite struct { + suite.Suite + + chain *e2eTesting.TestChain +} + +func (s *KeeperTestSuite) SetupTest() { + s.chain = e2eTesting.NewTestChain(s.T(), 1) +} + +func TestCWErrorsKeeper(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} diff --git a/x/cwerrors/keeper/msg_server.go b/x/cwerrors/keeper/msg_server.go new file mode 100644 index 0000000..3950ff1 --- /dev/null +++ b/x/cwerrors/keeper/msg_server.go @@ -0,0 +1,60 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +var _ types.MsgServer = (*MsgServer)(nil) + +// MsgServer implements the module gRPC messaging service. +type MsgServer struct { + keeper Keeper +} + +// NewMsgServer creates a new gRPC messaging server. +func NewMsgServer(keeper Keeper) *MsgServer { + return &MsgServer{ + keeper: keeper, + } +} + +// SubscribeToError implements types.MsgServer. +func (s *MsgServer) SubscribeToError(c context.Context, request *types.MsgSubscribeToError) (*types.MsgSubscribeToErrorResponse, error) { + if request == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + sender, err := sdk.AccAddressFromBech32(request.Sender) + if err != nil { + return nil, err + } + + contractAddr, err := sdk.AccAddressFromBech32(request.ContractAddress) + if err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(c) + subscriptionEndHeight, err := s.keeper.SetSubscription(ctx, sender, contractAddr, request.Fee) + if err != nil { + return nil, err + } + + types.EmitSubscribedToErrorsEvent( + ctx, + request.Sender, + request.ContractAddress, + request.Fee, + subscriptionEndHeight, + ) + return &types.MsgSubscribeToErrorResponse{ + SubscriptionValidTill: subscriptionEndHeight, + }, nil +} \ No newline at end of file diff --git a/x/cwerrors/keeper/msg_server_test.go b/x/cwerrors/keeper/msg_server_test.go new file mode 100644 index 0000000..5ee927f --- /dev/null +++ b/x/cwerrors/keeper/msg_server_test.go @@ -0,0 +1,128 @@ +package keeper_test + +import ( + "errors" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + e2eTesting "github.com/dymensionxyz/rollapp-wasm/e2e/testing" + "github.com/dymensionxyz/rollapp-wasm/pkg/testutils" + cwerrorsKeeper "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/keeper" + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +func (s *KeeperTestSuite) TestSubscribeToError() { + // Setting up chain and contract in mock wasm keeper + ctx, keeper := s.chain.GetContext(), s.chain.GetApp().CWErrorsKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + contractAddr := e2eTesting.GenContractAddresses(2)[0] + contractAddr2 := e2eTesting.GenContractAddresses(2)[1] + contractAdminAcc := s.chain.GetAccount(2) + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + params, err := keeper.GetParams(ctx) + s.Require().NoError(err) + params.SubscriptionFee = sdk.NewInt64Coin(sdk.DefaultBondDenom, 1) + err = keeper.SetParams(ctx, params) + s.Require().NoError(err) + + expectedEndHeight := ctx.BlockHeight() + params.SubscriptionPeriod + + msgServer := cwerrorsKeeper.NewMsgServer(keeper) + + testCases := []struct { + testCase string + input func() *types.MsgSubscribeToError + expectError bool + errorType error + }{ + { + testCase: "FAIL: empty request", + input: func() *types.MsgSubscribeToError { + return nil + }, + expectError: true, + errorType: status.Error(codes.InvalidArgument, "empty request"), + }, + { + testCase: "FAIL: invalid sender address", + input: func() *types.MsgSubscribeToError { + return &types.MsgSubscribeToError{ + Sender: "👻", + ContractAddress: contractAddr.String(), + Fee: sdk.NewInt64Coin(sdk.DefaultBondDenom, 100), + } + }, + expectError: true, + errorType: errors.New("invalid bech32 string length 4"), + }, + { + testCase: "FAIL: invalid contract address", + input: func() *types.MsgSubscribeToError { + return &types.MsgSubscribeToError{ + Sender: contractAdminAcc.Address.String(), + ContractAddress: "👻", + Fee: sdk.NewInt64Coin(sdk.DefaultBondDenom, 100), + } + }, + expectError: true, + errorType: errors.New("invalid bech32 string length 4"), + }, + { + testCase: "FAIL: contract not found", + input: func() *types.MsgSubscribeToError { + return &types.MsgSubscribeToError{ + Sender: contractAdminAcc.Address.String(), + ContractAddress: contractAddr2.String(), + Fee: sdk.NewInt64Coin(sdk.DefaultBondDenom, 100), + } + }, + expectError: true, + errorType: types.ErrContractNotFound, + }, + { + testCase: "FAIL: account doesnt have enough balance", + input: func() *types.MsgSubscribeToError { + return &types.MsgSubscribeToError{ + Sender: contractAddr.String(), + ContractAddress: contractAddr.String(), + Fee: params.SubscriptionFee, + } + }, + expectError: true, + errorType: sdkerrors.ErrInsufficientFunds, + }, + { + testCase: "OK: valid request", + input: func() *types.MsgSubscribeToError { + return &types.MsgSubscribeToError{ + Sender: contractAdminAcc.Address.String(), + ContractAddress: contractAddr.String(), + Fee: params.SubscriptionFee, + } + }, + expectError: false, + }, + } + for _, tc := range testCases { + s.Run(fmt.Sprintf("Case: %s", tc.testCase), func() { + req := tc.input() + res, err := msgServer.SubscribeToError(sdk.WrapSDKContext(ctx), req) + if tc.expectError { + s.Require().Error(err) + s.Assert().ErrorContains(err, tc.errorType.Error()) + } else { + s.Require().NoError(err) + s.Require().Equal(expectedEndHeight, res.SubscriptionValidTill) + } + }) + } +} + diff --git a/x/cwerrors/keeper/params.go b/x/cwerrors/keeper/params.go new file mode 100644 index 0000000..5bebdc7 --- /dev/null +++ b/x/cwerrors/keeper/params.go @@ -0,0 +1,17 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +// GetParams return all module parameters. +func (k Keeper) GetParams(ctx sdk.Context) (params types.Params, err error) { + return k.Params.Get(ctx) +} + +// SetParams sets all module parameters. +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) error { + return k.Params.Set(ctx, params) +} diff --git a/x/cwerrors/keeper/subscriptions.go b/x/cwerrors/keeper/subscriptions.go new file mode 100644 index 0000000..0a232cf --- /dev/null +++ b/x/cwerrors/keeper/subscriptions.go @@ -0,0 +1,94 @@ +package keeper + +import ( + "strings" + + "cosmossdk.io/collections" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +// SetSubscription sets a subscription for a contract so the contract can receive error callbacks +func (k Keeper) SetSubscription(ctx sdk.Context, sender, contractAddress sdk.AccAddress, fee sdk.Coin) (int64, error) { + if !k.wasmKeeper.HasContractInfo(ctx, contractAddress) { + return -1, types.ErrContractNotFound + } + if !isAuthorizedToSubscribe(ctx, k, contractAddress, sender.String()) { + return -1, types.ErrUnauthorized + } + + existingSubFound, existingEndHeight := k.GetSubscription(ctx, contractAddress) + if existingSubFound { + if err := k.SubscriptionEndBlock.Remove(ctx, collections.Join(existingEndHeight, contractAddress.Bytes())); err != nil { + return -1, err + } + } + params, err := k.GetParams(ctx) + if err != nil { + return -1, err + } + + if !fee.IsEqual(params.SubscriptionFee) { + return -1, types.ErrIncorrectSubscriptionFee + } + err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, authtypes.FeeCollectorName, sdk.NewCoins(fee)) + if err != nil { + return -1, err + } + + subscriptionEndHeight := max(ctx.BlockHeight(), existingEndHeight) + params.SubscriptionPeriod + if err = k.SubscriptionEndBlock.Set(ctx, collections.Join(subscriptionEndHeight, contractAddress.Bytes()), nil); err != nil { + return -1, err + } + return subscriptionEndHeight, k.ContractSubscriptions.Set(ctx, contractAddress, subscriptionEndHeight) +} + +// HasSubscription checks if a contract has a subscription +func (k Keeper) HasSubscription(ctx sdk.Context, contractAddress sdk.AccAddress) bool { + has, err := k.ContractSubscriptions.Has(ctx, contractAddress) + if err != nil { + return false + } + return has +} + +// GetSubscription returns the subscription end height for a contract +func (k Keeper) GetSubscription(ctx sdk.Context, contractAddress sdk.AccAddress) (bool, int64) { + has, err := k.ContractSubscriptions.Get(ctx, contractAddress) + if err != nil { + return false, 0 + } + return true, has +} + +// PruneSubscriptionsEndBlock prunes subscriptions that have ended at the given block. This is executed at the module endblocker +func (k Keeper) PruneSubscriptionsEndBlock(ctx sdk.Context) (err error) { + height := ctx.BlockHeight() + rng := collections.NewPrefixedPairRange[int64, []byte](height) + err = k.SubscriptionEndBlock.Walk(ctx, rng, func(key collections.Pair[int64, []byte], _ []byte) (bool, error) { + if err := k.ContractSubscriptions.Remove(ctx, key.K2()); err != nil { + return true, err + } + return false, nil + }) + if err != nil { + return err + } + return k.SubscriptionEndBlock.Clear(ctx, rng) +} + +// isAuthorizedToSubscribe checks if the sender is authorized to subscribe to the contract +func isAuthorizedToSubscribe(ctx sdk.Context, k Keeper, contractAddress sdk.AccAddress, sender string) bool { + if strings.EqualFold(sender, contractAddress.String()) { // A contract can set subscriptions for itself + return true + } + + contractInfo := k.wasmKeeper.GetContractInfo(ctx, contractAddress) + if strings.EqualFold(sender, contractInfo.Admin) { // Admin of the contract can set subscriptions + return true + } + + return false +} diff --git a/x/cwerrors/keeper/subscriptions_test.go b/x/cwerrors/keeper/subscriptions_test.go new file mode 100644 index 0000000..d3a4f1c --- /dev/null +++ b/x/cwerrors/keeper/subscriptions_test.go @@ -0,0 +1,184 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + e2eTesting "github.com/dymensionxyz/rollapp-wasm/e2e/testing" + "github.com/dymensionxyz/rollapp-wasm/pkg/testutils" + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +func (s *KeeperTestSuite) TestSetSubscription() { + // Setting up chain and contract in mock wasm keeper + ctx, keeper := s.chain.GetContext(), s.chain.GetApp().CWErrorsKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + contractAddresses := e2eTesting.GenContractAddresses(3) + contractAddr := contractAddresses[0] + contractAddr2 := contractAddresses[1] + contractAdminAcc := s.chain.GetAccount(0) + contractNotAdminAcc := s.chain.GetAccount(1) + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + fees := sdk.NewInt64Coin(sdk.DefaultBondDenom, 0) + + // TEST CASE 1: Contract does not exist + _, err := keeper.SetSubscription(ctx, contractAdminAcc.Address, contractAddr2, fees) + s.Require().ErrorIs(err, types.ErrContractNotFound) + + // TEST CASE 2: Sender unauthorized to set subscription + _, err = keeper.SetSubscription(ctx, contractNotAdminAcc.Address, contractAddr, fees) + s.Require().ErrorIs(err, types.ErrUnauthorized) + + // TEST CASE 3: Subscription fee is less than the minimum subscription fee + params, err := keeper.GetParams(ctx) + s.Require().NoError(err) + err = keeper.SetParams(ctx, types.Params{ + ErrorStoredTime: params.ErrorStoredTime, + SubscriptionFee: sdk.NewInt64Coin(sdk.DefaultBondDenom, 100), + SubscriptionPeriod: params.SubscriptionPeriod, + }) + s.Require().NoError(err) + _, err = keeper.SetSubscription(ctx, contractAdminAcc.Address, contractAddr, fees) + s.Require().ErrorIs(err, types.ErrIncorrectSubscriptionFee) + err = keeper.SetParams(ctx, types.DefaultParams()) + s.Require().NoError(err) + + // TEST CASE 4: Successful subscription + subscriptionEndHeight, err := keeper.SetSubscription(ctx, contractAdminAcc.Address, contractAddr, fees) + s.Require().NoError(err) + expectedEndDate := ctx.BlockHeight() + types.DefaultParams().SubscriptionPeriod + s.Require().Equal(subscriptionEndHeight, expectedEndDate) + + // TEST CASE 5: Subscription already exists - subscription end height gets updated to new height + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + subscriptionEndHeight, err = keeper.SetSubscription(ctx, contractAdminAcc.Address, contractAddr, fees) + s.Require().NoError(err) + expectedEndDate = expectedEndDate + params.SubscriptionPeriod // existing subscription gets extended + s.Require().Equal(subscriptionEndHeight, expectedEndDate) + + // TEST CASE 6: Subscription being updated by the contract itself (instead of admin) + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + subscriptionEndHeight, err = keeper.SetSubscription(ctx, contractAddr, contractAddr, fees) + s.Require().NoError(err) + expectedEndDate = expectedEndDate + params.SubscriptionPeriod // existing subscription gets extended + s.Require().Equal(subscriptionEndHeight, expectedEndDate) +} + +func (s *KeeperTestSuite) TestHasSubscription() { + ctx, keeper := s.chain.GetContext(), s.chain.GetApp().CWErrorsKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + contractAddresses := e2eTesting.GenContractAddresses(3) + contractAddr := contractAddresses[0] + contractAdminAcc := s.chain.GetAccount(0) + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + fees := sdk.NewInt64Coin(sdk.DefaultBondDenom, 0) + + // TEST CASE 1: Subscription does not exist + hasSub := keeper.HasSubscription(ctx, contractAddr) + s.Require().False(hasSub) + + // TEST CASE 2: Subscription exists + _, err := keeper.SetSubscription(ctx, contractAdminAcc.Address, contractAddr, fees) + s.Require().NoError(err) + hasSub = keeper.HasSubscription(ctx, contractAddr) + s.Require().True(hasSub) +} + +func (s *KeeperTestSuite) TestGetSubscription() { + ctx, keeper := s.chain.GetContext(), s.chain.GetApp().CWErrorsKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + contractAddresses := e2eTesting.GenContractAddresses(3) + contractAddr := contractAddresses[0] + contractAdminAcc := s.chain.GetAccount(0) + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + fees := sdk.NewInt64Coin(sdk.DefaultBondDenom, 0) + + // TEST CASE 1: Subscription does not exist + _, endHeight := keeper.GetSubscription(ctx, contractAddr) + s.Require().Equal(endHeight, int64(0)) + + // TEST CASE 2: Subscription exists + endHeight, err := keeper.SetSubscription(ctx, contractAdminAcc.Address, contractAddr, fees) + s.Require().NoError(err) + found, foundEndHeight := keeper.GetSubscription(ctx, contractAddr) + s.Require().True(found) + s.Require().Equal(endHeight, foundEndHeight) +} + +func (s *KeeperTestSuite) TestPruneSubscriptionsEndBlock() { + ctx, keeper := s.chain.GetContext(), s.chain.GetApp().CWErrorsKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + contractAddresses := e2eTesting.GenContractAddresses(3) + contractAddr := contractAddresses[0] + contractAddr2 := contractAddresses[1] + contractAddr3 := contractAddresses[2] + contractAdminAcc := s.chain.GetAccount(0) + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + contractViewer.AddContractAdmin( + contractAddr2.String(), + contractAdminAcc.Address.String(), + ) + contractViewer.AddContractAdmin( + contractAddr3.String(), + contractAdminAcc.Address.String(), + ) + + fees := sdk.NewInt64Coin(sdk.DefaultBondDenom, 0) + + // TEST CASE 1: No subscriptions to prune + err := keeper.PruneSubscriptionsEndBlock(ctx) + s.Require().NoError(err) + + // TEST CASE 2: Set subscription. Go to expire time. Prune subscriptions + endHeight, err := keeper.SetSubscription(ctx, contractAdminAcc.Address, contractAddr, fees) + s.Require().NoError(err) + ctx = ctx.WithBlockHeight(endHeight) + err = keeper.PruneSubscriptionsEndBlock(ctx) + s.Require().NoError(err) + hasSub := keeper.HasSubscription(ctx, contractAddr) + s.Require().False(hasSub) + + // TEST CASE 3: Prune subscriptions when many contracts have subscriptions + endHeight, err = keeper.SetSubscription(ctx, contractAdminAcc.Address, contractAddr, fees) + s.Require().NoError(err) + _, err = keeper.SetSubscription(ctx, contractAdminAcc.Address, contractAddr2, fees) + s.Require().NoError(err) + _, err = keeper.SetSubscription(ctx, contractAdminAcc.Address, contractAddr3, fees) + s.Require().NoError(err) + + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + // extend the subscription for contractAddr3 + newEndHeight, err := keeper.SetSubscription(ctx, contractAdminAcc.Address, contractAddr3, fees) + s.Require().NoError(err) + + ctx = ctx.WithBlockHeight(endHeight) + err = keeper.PruneSubscriptionsEndBlock(ctx) + s.Require().NoError(err) + hasSub = keeper.HasSubscription(ctx, contractAddr) + s.Require().False(hasSub) + hasSub = keeper.HasSubscription(ctx, contractAddr2) + s.Require().False(hasSub) + hasSub = keeper.HasSubscription(ctx, contractAddr3) + s.Require().True(hasSub) + + ctx = ctx.WithBlockHeight(newEndHeight) + err = keeper.PruneSubscriptionsEndBlock(ctx) + s.Require().NoError(err) + hasSub = keeper.HasSubscription(ctx, contractAddr3) + s.Require().False(hasSub) +} diff --git a/x/cwerrors/keeper/sudo_errors.go b/x/cwerrors/keeper/sudo_errors.go new file mode 100644 index 0000000..380d68e --- /dev/null +++ b/x/cwerrors/keeper/sudo_errors.go @@ -0,0 +1,174 @@ +package keeper + +import ( + "cosmossdk.io/collections" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +// SetError stores a sudo error and queues it for deletion after a certain block height +func (k Keeper) SetError(ctx sdk.Context, sudoErr types.SudoError) error { + // Ensure error is valid + if err := sudoErr.Validate(); err != nil { + return err + } + contractAddr := sdk.MustAccAddressFromBech32(sudoErr.ContractAddress) + // Ensure contract exists + if !k.wasmKeeper.HasContractInfo(ctx, contractAddr) { + return types.ErrContractNotFound + } + + if k.HasSubscription(ctx, contractAddr) { + // If contract has subscription, store the error in the transient store to be executed as error callback + return k.storeErrorCallback(ctx, sudoErr) + } else { + // for contracts which dont have an error subscription, store the error in state to be deleted after a set height + return k.StoreErrorInState(ctx, contractAddr, sudoErr) + } +} + +// StoreErrorInState stores the error in the state and queues it for deletion after a certain block height +func (k Keeper) StoreErrorInState(ctx sdk.Context, contractAddr sdk.AccAddress, sudoErr types.SudoError) error { + // just a unique identifier for the error + errorID, err := k.ErrorID.Next(ctx) + if err != nil { + return err + } + + // Associate the error with the contract + if err = k.ContractErrors.Set(ctx, collections.Join(contractAddr.Bytes(), errorID), nil); err != nil { + return err + } + + // Store when the error should be deleted + params, err := k.GetParams(ctx) + if err != nil { + return err + } + deletionHeight := ctx.BlockHeight() + params.ErrorStoredTime + if err = k.DeletionBlocks.Set(ctx, collections.Join(deletionHeight, errorID), nil); err != nil { + return err + } + + // Store the actual sudo error + err = k.Errors.Set(ctx, errorID, sudoErr) + if err != nil { + return err + } + + types.EmitStoringErrorEvent( + ctx, + sudoErr, + deletionHeight, + ) + return nil +} + +func (k Keeper) storeErrorCallback(ctx sdk.Context, sudoErr types.SudoError) error { + errorID, err := k.ErrorID.Next(ctx) + if err != nil { + return err + } + + k.SetSudoErrorCallback(ctx, errorID, sudoErr) + return nil +} + +// GetErrosByContractAddress returns all errors (in state) for a given contract address +func (k Keeper) GetErrorsByContractAddress(ctx sdk.Context, contractAddress []byte) (sudoErrs []types.SudoError, err error) { + rng := collections.NewPrefixedPairRange[[]byte, uint64](contractAddress) + err = k.ContractErrors.Walk(ctx, rng, func(key collections.Pair[[]byte, uint64], _ []byte) (bool, error) { + sudoErr, err := k.Errors.Get(ctx, key.K2()) + if err != nil { + return true, err + } + sudoErrs = append(sudoErrs, sudoErr) + return false, nil + }) + if err != nil { + return nil, err + } + return sudoErrs, nil +} + +// ExportErrors returns all errors in state. Used for genesis export +func (k Keeper) ExportErrors(ctx sdk.Context) (sudoErrs []types.SudoError, err error) { + iter, err := k.Errors.Iterate(ctx, nil) + if err != nil { + return nil, err + } + sudoErrs, err = iter.Values() + if err != nil { + return nil, err + } + return sudoErrs, nil +} + +// PruneErrorsCurrentBlock removes all errors that are queued to be deleted the given block height +func (k Keeper) PruneErrorsCurrentBlock(ctx sdk.Context) (err error) { + var errorIDs []uint64 + height := ctx.BlockHeight() + rng := collections.NewPrefixedPairRange[int64, uint64](height) + err = k.DeletionBlocks.Walk(ctx, rng, func(key collections.Pair[int64, uint64], _ []byte) (bool, error) { + errorIDs = append(errorIDs, key.K2()) + return false, nil + }) + if err != nil { + return err + } + for _, errorID := range errorIDs { + sudoErr, err := k.Errors.Get(ctx, errorID) + if err != nil { + return err + } + // Removing the error data + if err := k.Errors.Remove(ctx, errorID); err != nil { + return err + } + // Removing the contract errors + contractAddress := sdk.MustAccAddressFromBech32(sudoErr.ContractAddress) + if err := k.ContractErrors.Remove(ctx, collections.Join(contractAddress.Bytes(), errorID)); err != nil { + return err + } + // Removing the deletion block + if err := k.DeletionBlocks.Remove(ctx, collections.Join(height, errorID)); err != nil { + return err + } + } + return nil +} + +// SetSudoErrorCallback stores a sudo error callback in the transient store +func (k Keeper) SetSudoErrorCallback(ctx sdk.Context, errorId uint64, sudoErr types.SudoError) { + tStore := ctx.TransientStore(k.tStoreKey) + errToStore := k.cdc.MustMarshal(&sudoErr) + tStore.Set(types.GetErrorsForSudoCallStoreKey(errorId), errToStore) +} + +// GetAllSudoErrorCallbacks returns all sudo error callbacks from the transient store +func (k Keeper) GetAllSudoErrorCallbacks(ctx sdk.Context) (sudoErrs []types.SudoError) { + tStore := ctx.TransientStore(k.tStoreKey) + itr := sdk.KVStorePrefixIterator(tStore, types.ErrorsForSudoCallbackKey) + defer itr.Close() + for ; itr.Valid(); itr.Next() { + var sudoErr types.SudoError + k.cdc.MustUnmarshal(itr.Value(), &sudoErr) + sudoErrs = append(sudoErrs, sudoErr) + } + return sudoErrs +} + +// IterateSudoErrorCallbacks iterates over all sudo error callbacks from the transient store +func (k Keeper) IterateSudoErrorCallbacks(ctx sdk.Context, exec func(types.SudoError) bool) { + tStore := ctx.TransientStore(k.tStoreKey) + itr := sdk.KVStorePrefixIterator(tStore, types.ErrorsForSudoCallbackKey) + defer itr.Close() + for ; itr.Valid(); itr.Next() { + var sudoErr types.SudoError + k.cdc.MustUnmarshal(itr.Value(), &sudoErr) + if exec(sudoErr) { + break + } + } +} diff --git a/x/cwerrors/keeper/sudo_errors_test.go b/x/cwerrors/keeper/sudo_errors_test.go new file mode 100644 index 0000000..7885163 --- /dev/null +++ b/x/cwerrors/keeper/sudo_errors_test.go @@ -0,0 +1,272 @@ +package keeper_test + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + + e2eTesting "github.com/dymensionxyz/rollapp-wasm/e2e/testing" + "github.com/dymensionxyz/rollapp-wasm/pkg/testutils" + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +func (s *KeeperTestSuite) TestSetError() { + // Setting up chain and contract in mock wasm keeper + ctx, keeper := s.chain.GetContext(), s.chain.GetApp().CWErrorsKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + contractAddresses := e2eTesting.GenContractAddresses(3) + contractAddr := contractAddresses[0] + contractAddr2 := contractAddresses[1] + contractAdminAcc := s.chain.GetAccount(0) + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + testCases := []struct { + testCase string + sudoError types.SudoError + expectError bool + errorType error + }{ + { + testCase: "FAIL: contract address is invalid", + sudoError: types.SudoError{ + ContractAddress: "👻", + ModuleName: "test", + ErrorCode: 1, + InputPayload: "test", + ErrorMessage: "test", + }, + expectError: true, + errorType: fmt.Errorf("decoding bech32 failed: invalid bech32 string length 4"), + }, + { + testCase: "FAIL: module name is invalid", + sudoError: types.SudoError{ + ContractAddress: contractAddr.String(), + ModuleName: "", + ErrorCode: 1, + InputPayload: "test", + ErrorMessage: "test", + }, + expectError: true, + errorType: types.ErrModuleNameMissing, + }, + { + testCase: "FAIL: contract does not exist", + sudoError: types.SudoError{ + ContractAddress: contractAddr2.String(), + ModuleName: "test", + ErrorCode: 1, + InputPayload: "test", + ErrorMessage: "test", + }, + expectError: true, + errorType: types.ErrContractNotFound, + }, + { + testCase: "OK: successfully set error", + sudoError: types.SudoError{ + ContractAddress: contractAddr.String(), + ModuleName: "test", + ErrorCode: 1, + InputPayload: "test", + ErrorMessage: "test", + }, + expectError: false, + }, + } + for _, tc := range testCases { + s.Run(fmt.Sprintf("Case: %s", tc.testCase), func() { + err := keeper.SetError(ctx, tc.sudoError) + if tc.expectError { + s.Require().Error(err) + s.Assert().ErrorContains(err, tc.errorType.Error()) + } else { + s.Require().NoError(err) + + getErrors, err := keeper.GetErrorsByContractAddress(ctx, sdk.MustAccAddressFromBech32(tc.sudoError.ContractAddress)) + s.Require().NoError(err) + s.Require().Len(getErrors, 1) + s.Require().Equal(tc.sudoError, getErrors[0]) + } + }) + } +} + +func (s *KeeperTestSuite) TestGetErrorsByContractAddress() { + // Setting up chain and contract in mock wasm keeper + ctx, keeper := s.chain.GetContext(), s.chain.GetApp().CWErrorsKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + contractAddresses := e2eTesting.GenContractAddresses(3) + contractAddr := contractAddresses[0] + contractAddr2 := contractAddresses[1] + contractAdminAcc := s.chain.GetAccount(0) + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + contractViewer.AddContractAdmin( + contractAddr2.String(), + contractAdminAcc.Address.String(), + ) + + // Set errors for block 1 + // 2 errors for contract1 + // 1 error for contract2 + contract1Err := types.SudoError{ + ContractAddress: contractAddr.String(), + ModuleName: "test", + } + contract2Err := types.SudoError{ + ContractAddress: contractAddr2.String(), + ModuleName: "test", + } + err := keeper.SetError(ctx, contract1Err) + s.Require().NoError(err) + err = keeper.SetError(ctx, contract1Err) + s.Require().NoError(err) + err = keeper.SetError(ctx, contract2Err) + s.Require().NoError(err) + + // Check number of errors match + sudoErrs, err := keeper.GetErrorsByContractAddress(ctx, contractAddr.Bytes()) + s.Require().NoError(err) + s.Require().Len(sudoErrs, 2) + sudoErrs, err = keeper.GetErrorsByContractAddress(ctx, contractAddr2.Bytes()) + s.Require().NoError(err) + s.Require().Len(sudoErrs, 1) + + // Increment block height + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // Set errors for block 2 + // 1 error for contract1 + // 1 error for contract2 + err = keeper.SetError(ctx, contract1Err) + s.Require().NoError(err) + err = keeper.SetError(ctx, contract2Err) + s.Require().NoError(err) + + // Check number of errors match + sudoErrs, err = keeper.GetErrorsByContractAddress(ctx, contractAddr.Bytes()) + s.Require().NoError(err) + s.Require().Len(sudoErrs, 3) + sudoErrs, err = keeper.GetErrorsByContractAddress(ctx, contractAddr2.Bytes()) + s.Require().NoError(err) + s.Require().Len(sudoErrs, 2) +} + +func (s *KeeperTestSuite) TestPruneErrorsByBlockHeight() { + // Setting up chain and contract in mock wasm keeper + ctx, keeper := s.chain.GetContext(), s.chain.GetApp().CWErrorsKeeper + contractViewer := testutils.NewMockContractViewer() + keeper.SetWasmKeeper(contractViewer) + contractAddresses := e2eTesting.GenContractAddresses(3) + contractAddr := contractAddresses[0] + contractAddr2 := contractAddresses[1] + contractAdminAcc := s.chain.GetAccount(0) + contractViewer.AddContractAdmin( + contractAddr.String(), + contractAdminAcc.Address.String(), + ) + contractViewer.AddContractAdmin( + contractAddr2.String(), + contractAdminAcc.Address.String(), + ) + + // Set errors for block 1 + contract1Err := types.SudoError{ + ContractAddress: contractAddr.String(), + ModuleName: "test", + } + contract2Err := types.SudoError{ + ContractAddress: contractAddr2.String(), + ModuleName: "test", + } + + // Set errors for block 1 + // 1 errors for contract1 + // 1 error for contract2 + err := keeper.SetError(ctx, contract1Err) + s.Require().NoError(err) + err = keeper.SetError(ctx, contract2Err) + s.Require().NoError(err) + + // Calculate height at which these errors are pruned + params, err := keeper.GetParams(ctx) + s.Require().NoError(err) + pruneHeight := ctx.BlockHeight() + params.GetErrorStoredTime() + + // Increment block height + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + // Set errors for block 2 + // 1 error for contract1 + err = keeper.SetError(ctx, contract1Err) + s.Require().NoError(err) + + // Check number of errors match + getErrors, err := keeper.GetErrorsByContractAddress(ctx, contractAddr.Bytes()) + s.Require().NoError(err) + s.Require().Len(getErrors, 2) + getErrors, err = keeper.GetErrorsByContractAddress(ctx, contractAddr2.Bytes()) + s.Require().NoError(err) + s.Require().Len(getErrors, 1) + + // Go to prune height and prune errors + ctx = ctx.WithBlockHeight(pruneHeight) + err = keeper.PruneErrorsCurrentBlock(ctx) + s.Require().NoError(err) + + // Check number of errors match + getErrors, err = keeper.GetErrorsByContractAddress(ctx, contractAddr.Bytes()) + s.Require().NoError(err) + s.Require().Len(getErrors, 1) + getErrors, err = keeper.GetErrorsByContractAddress(ctx, contractAddr2.Bytes()) + s.Require().NoError(err) + s.Require().Len(getErrors, 0) + + // Increment block height + add error for contract 2 + prune + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + err = keeper.SetError(ctx, contract2Err) + s.Require().NoError(err) + err = keeper.PruneErrorsCurrentBlock(ctx) + s.Require().NoError(err) + + // Check number of errors match + getErrors, err = keeper.GetErrorsByContractAddress(ctx, contractAddr.Bytes()) + s.Require().NoError(err) + s.Require().Len(getErrors, 0) + getErrors, err = keeper.GetErrorsByContractAddress(ctx, contractAddr2.Bytes()) + s.Require().NoError(err) + s.Require().Len(getErrors, 1) +} + +func (s *KeeperTestSuite) TestSudoErrorCallback() { + // Setting up chain and contract in mock wasm keeper + ctx, keeper := s.chain.GetContext(), s.chain.GetApp().CWErrorsKeeper + + sudoErr := types.SudoError{ + ContractAddress: e2eTesting.GenContractAddresses(1)[0].String(), + ModuleName: "test", + ErrorCode: 1, + InputPayload: "test", + ErrorMessage: "test", + } + + // Set error + keeper.SetSudoErrorCallback(ctx, 1, sudoErr) + keeper.SetSudoErrorCallback(ctx, 2, sudoErr) + + // Get error + getErrs := keeper.GetAllSudoErrorCallbacks(ctx) + s.Require().Len(getErrs, 2) + + // Go to next block and check if errors are 0 as this is a transient store + s.chain.NextBlock(1) + + getErrs = keeper.GetAllSudoErrorCallbacks(s.chain.GetContext()) + s.Require().Len(getErrs, 0) +} diff --git a/x/cwerrors/module.go b/x/cwerrors/module.go new file mode 100644 index 0000000..e476399 --- /dev/null +++ b/x/cwerrors/module.go @@ -0,0 +1,148 @@ +package cwerrors + +import ( + "context" + "encoding/json" + "fmt" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codecTypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/client/cli" + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/keeper" + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +var ( + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModule = AppModule{} +) + +// AppModuleBasic defines the basic application module for this module. +type AppModuleBasic struct { + cdc codec.Codec +} + +// Name returns the module's name. +func (a AppModuleBasic) Name() string { + return types.ModuleName +} + +// QuerierRoute returns the capability module's query routing key. +func (AppModule) QuerierRoute() string { + return types.QuerierRoute +} + +// RegisterLegacyAminoCodec registers the module's types on the given LegacyAmino codec. +func (a AppModuleBasic) RegisterLegacyAminoCodec(amino *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(amino) +} + +// RegisterInterfaces registers the module's interface types. +func (a AppModuleBasic) RegisterInterfaces(registry codecTypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) +} + +// DefaultGenesis returns default genesis state as raw bytes for the module. +func (a AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesis()) +} + +// ValidateGenesis performs genesis state validation for the module. +func (a AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error { + var state types.GenesisState + if err := cdc.UnmarshalJSON(bz, &state); err != nil { + return fmt.Errorf("failed to unmarshal x/%s genesis state: %w", types.ModuleName, err) + } + + return state.Validate() +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module. +func (a AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, serveMux *runtime.ServeMux) { + if err := types.RegisterQueryHandlerClient(context.Background(), serveMux, types.NewQueryClient(clientCtx)); err != nil { + panic(fmt.Errorf("registering query handler for x/%s: %w", types.ModuleName, err)) + } +} + +// GetTxCmd returns the root tx command for the module. +func (a AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.GetTxCmd() +} + +// GetQueryCmd returns no root query command for the module. +func (a AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// AppModule implements an application module for this module. +type AppModule struct { + AppModuleBasic + + keeper keeper.Keeper + wasmKeeper types.WasmKeeperExpected +} + +// NewAppModule creates a new AppModule object. +func NewAppModule(cdc codec.Codec, keeper keeper.Keeper, wk types.WasmKeeperExpected) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{cdc: cdc}, + keeper: keeper, + wasmKeeper: wk, + } +} + +// RegisterInvariants registers the module invariants. +func (a AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { +} + +// RegisterServices registers the module services. +func (a AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServer(a.keeper)) + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServer(a.keeper)) +} + +// InitGenesis performs genesis initialization for the module. It returns no validator updates. +func (a AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, bz json.RawMessage) []abci.ValidatorUpdate { + var genesisState types.GenesisState + cdc.MustUnmarshalJSON(bz, &genesisState) + + InitGenesis(ctx, a.keeper, genesisState) + + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the exported genesis state as raw bytes for the module. +func (a AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { + state := ExportGenesis(ctx, a.keeper) + return cdc.MustMarshalJSON(state) +} + +// ConsensusVersion implements AppModule/ConsensusVersion. +func (a AppModule) ConsensusVersion() uint64 { + return 1 +} + +// BeginBlock returns the begin blocker for the module. +func (a AppModule) BeginBlock(ctx sdk.Context, block abci.RequestBeginBlock) {} + +// EndBlock returns the end blocker for the module. It returns no validator updates. +func (a AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return EndBlocker(ctx, a.keeper, a.wasmKeeper) +} + +// LegacyQuerierHandler returns the capability module's Querier. +func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return nil +} + +// Route returns the capability module's message routing key. +func (am AppModule) Route() sdk.Route { + return sdk.Route{} +} diff --git a/x/cwerrors/spec/01_state.md b/x/cwerrors/spec/01_state.md new file mode 100644 index 0000000..01fe72b --- /dev/null +++ b/x/cwerrors/spec/01_state.md @@ -0,0 +1,87 @@ +# State + +Section describes all stored by the module objects and their storage keys + +## Params + +[Params](../../../proto/rollapp/cwerrors/v1/params.proto) object is used to store the module params + +The params value can only be updated by x/gov module via a governance upgrade proposal. [More](./02_messages.md#msgupdateparams) + +Storage keys: +* Params: `ParamsKey -> ProtocolBuffer(Params)` + +```protobuf +message Params { + // error_stored_time is the relative block height until which error is stored + int64 error_stored_time = 1; + // subsciption_fee is the fee required to subscribe to error callbacks + cosmos.base.v1beta1.Coin subscription_fee = 2 [ (gogoproto.nullable) = false ]; + // subscription_period is the period for which the subscription is valid + int64 subscription_period = 3; +} +``` + +## ErrorID + +ErrorID is a sequence number used to increment error ID. + +Storage keys: +* ErrorID: `ErrorIDKey -> uint64` + +## Contract Errors + +Contract Errors is a collection of all the error ids associated with a given contract address. This is used to query contract errors. + +Storage keys: +* ContractErrors: `ContractErrorsKeyPrefix | contractAddress | errorID -> errorID` + +## Errors + +Errors is a collections of all the [SudoErrors](../../../proto/rollapp/cwerrors/v1/cwerrors.proto) currently stored by the module which can be queried. + +Storage keys: +* Errors: `ErrorsKeyPrefix | errorID -> protobuf(SudoError)` + +```protobuf +message SudoError { + // module_name is the name of the module throwing the error + string module_name = 1; + // error_code is the module level error code + uint32 error_code = 2; + // contract_address is the address of the contract which will receive the error callback + string contract_address = 3; + // input_payload is any input which caused the error + string input_payload = 4; + // error_message is the error message + string error_message = 5; +} +``` + +## Deletion Blocks + +Deletion Blocks is a collection of all the error ids which need to be pruned in a given block height + +Storage keys: +* DeletionBlocks: `DeletionBlocksKeyPrefix | blockHeight | errorID -> errorID` + +## Contract Subscriptions + +Contract Subscriptions is a map of the contract addresses which have subscriptions and the height when the subscription expires + +Storage keys: +* Contract Subscriptions: `ContractSubscriptionsKeyPrefix | contractAddress -> deletionHeight` + +## Subscription End Block + +Subscritption End Block is a collections of all the subscriptions which need to be cleared at the given block height + +Storage keys: +* Subscription End Block: `SubscriptionEndBlockKeyPrefix | blockHeight | contractAddress -> contractAddress` + +# Transient State + +The sudo errors which belong to the contracts with subscription are stored in the transient state of the block. + +Transient Storage keys: +* SudoErrors: `ErrorsForSudoCallbackKey | errorId -> SudoError` \ No newline at end of file diff --git a/x/cwerrors/spec/02_messages.md b/x/cwerrors/spec/02_messages.md new file mode 100644 index 0000000..f4f48b9 --- /dev/null +++ b/x/cwerrors/spec/02_messages.md @@ -0,0 +1,30 @@ +# Messages + +Section describes the processing of the module messages + +## MsgSubscribeToError + +A contract can be subscribed to errors by using the [MsgSubscribeToError](../../../proto/rollapp/cwerrors/v1/tx.proto) message. + +```protobuf +message MsgSubscribeToError { + option (cosmos.msg.v1.signer) = "sender"; + // sender is the address of who is registering the contracts for callback on error + string sender = 1; + // contract_address is the contarct subscribing to the error + string contract_address = 2; + // fee is the subscription fee for the feature + cosmos.base.v1beta1.Coin fee = 3 [ (gogoproto.nullable) = false ]; +} +``` + +On success +* A subscription is created valid for the duration as specified in the module params. +* The subscription fees are sent to the fee collector +* In case a subscription already exists, it is extended. + +This message is expected to fail if: +* The sender address and contract address are not valid addresses +* There is no contract with given address +* The sender is not authorized to subscribe - the sender is not the contract owner/admin or the contract itself +* The user does not send enough funds or doesnt have enough funds \ No newline at end of file diff --git a/x/cwerrors/spec/03_end_block.md b/x/cwerrors/spec/03_end_block.md new file mode 100644 index 0000000..8ff51ab --- /dev/null +++ b/x/cwerrors/spec/03_end_block.md @@ -0,0 +1,17 @@ +# End Block + +Section describes the module state changes on the ABCI end block call + +## SudoError invoke + +All the errors encountered in the current block are fetched from the transient store. For each error, a contract is hit at the sudo entrypoint. The execution happens with gas limit of `150_000` to prevent abusive operations and limit the usage to error handling. + +In case, the execution fails, the error is stored in state such that the contract can query it. + +## Prune expiring subscription + +All the contract subscriptions which end in the current block are pruned from state + +## Prune old errors + +All errors which are marked for deletion for the current block height are pruned from the state. diff --git a/x/cwerrors/spec/04_events.md b/x/cwerrors/spec/04_events.md new file mode 100644 index 0000000..0a48253 --- /dev/null +++ b/x/cwerrors/spec/04_events.md @@ -0,0 +1,12 @@ +# Events + +Section describes the module events + +The module emits the following proto-events + +| Source type | Source name | Protobuf reference | +| ----------- | --------------------- |--------------------------------------------------------------------------------------| +| Message | `MsgUpdateParams` | [ParamsUpdatedEvent](../../../proto/rollapp/cwerrors/v1/events.proto#L12) | +| Message | `MsgSubscribeToError` | [SubscribedToErrorsEvent](../../../proto/rollapp/cwerrors/v1/events.proto#L20) | +| Keeper | `SetErrorInState` | [StoringErrorEvent](../../../proto/rollapp/cwerrors/v1/events.proto#L32) | +| Module | `EndBlocker` | [SudoErrorCallbackFailedEvent](../../../proto/rollapp/cwerrors/v1/events.proto#L40) | \ No newline at end of file diff --git a/x/cwerrors/spec/05_client.md b/x/cwerrors/spec/05_client.md new file mode 100644 index 0000000..b0b712d --- /dev/null +++ b/x/cwerrors/spec/05_client.md @@ -0,0 +1,96 @@ +# Client + +Section describes interaction with the module by the user + +## CLI + +### Query + +The `query` commands alllows a user to query the module state + +Use the `-h`/`--help` flag to get a help description of a command. + +`rollapp-wasm q cwerrors -h` + +> You can add the `-o json` for the JSON output format + +#### params + +Get the current module parameters + +Usage: + +`rollapp-wasm q cwerrors params [flags]` + +Example output: + +```yaml +error_stored_time: "302400" +subscription_fee: + amount: "0" + denom: stake +subscription_period: "302400" +``` + +#### errors + +List all the errors for the given contract + +Usage: + +`rollapp-wasm q cwerrors errors [contract-address]` + +Example: + +`rollapp-wasm q cwerrors errors cosmos1wug8sewp6cedgkmrmvhl3lf3tulagm9hnvy8p0rppz9yjw0g4wtqukxvuk` + +Example output: + +```yaml +errors: +- module_name: "callback" + error_code: 2 + contract_address: cosmos1wug8sewp6cedgkmrmvhl3lf3tulagm9hnvy8p0rppz9yjw0g4wtqukxvuk + input_payload: "{'job_id':1}" + error_message: "Out of gas" +``` + +#### is-subscribed + +Lists if the given contract is subscribed to error callbacks and the block height it is valid till + +Usage: + +`rollapp-wasm q cwerrors is-subscribed [contract-address]` + +Example: + +`rollapp-wasm q cwerrors is-subscribed cosmos1wug8sewp6cedgkmrmvhl3lf3tulagm9hnvy8p0rppz9yjw0g4wtqukxvuk` + +Example output: + +```yaml +subscribed: true +subscription_valid_till: 1234 +``` + +### TX + +The `tx` commands allows a user to interact with the module. + +Use the `-h`/`--help` flag to get a help description of a command. + +`rollapp-wasm tx cwerrors -h` + + +#### subscribe-to-error + +Create a new subscription which will register a contract for a sudo callback on errors + +Usage: + +`rollapp-wasm tx cwerrors subscribe-to-error [contract-address] [fee-amount] [flags]` + +Example: + +`rollapp-wasm tx cwerrors subscribe-to-error cosmos1wug8sewp6cedgkmrmvhl3lf3tulagm9hnvy8p0rppz9yjw0g4wtqukxvuk 7000stake --from myAccountKey` \ No newline at end of file diff --git a/x/cwerrors/spec/06_errors.md b/x/cwerrors/spec/06_errors.md new file mode 100644 index 0000000..f6a4baf --- /dev/null +++ b/x/cwerrors/spec/06_errors.md @@ -0,0 +1,12 @@ +# Errors + +The module exposes the following error codes which are used with the x/cwerrors module in case of error callback failures. + +```proto +enum ModuleErrors { + // ERR_UNKNOWN is the default error code + ERR_UNKNOWN = 0; + // ERR_CALLBACK_EXECUTION_FAILED is the error code for when the error callback fails + ERR_CALLBACK_EXECUTION_FAILED = 1; +} +``` \ No newline at end of file diff --git a/x/cwerrors/spec/README.md b/x/cwerrors/spec/README.md new file mode 100644 index 0000000..a1cf59c --- /dev/null +++ b/x/cwerrors/spec/README.md @@ -0,0 +1,41 @@ +# CWErrors + +This module is the error handling mechanism for the protocol to let the contract know of errors encountered + +## Concepts + +[callback](../../callback/spec/README.md) module and [cwica](../../cwica/spec/README.md) module both rely on protocol invoking contract execution. In case, there are any errors generated by the protocol, there needs to be a way to let the contract know of this error. The module provides a standard way of error catching from the contract side. + +The module provides two diffferent ways that the error is communicated. + +### 1. Errors saved in state (default) + +Whenever a contract relevant error is encountered by the protocol, the module stores the error and associates with the contract address. This is stored in the chain state for `x` number of blocks (The `x` value is a module parameter. [See more](./01_state.md)) after which the error is permanently deleted. These stored errors are queryable by the contract using stargate queries. + +### 2. Errors sudo callback + +If a contract is subscribed to errors, then the contract will be invoked at the sudo entrypoint like so. + +```rust +#[cw_serde] +pub enum SudoMsg { + Error { + module_name: String, // The name of the module which generated the error + error_code: u32, // module specific error code + contract_address: String, // the contract address which is associated with the error; the contract receiving the callback + input_payload: String, // any relevant input payload which caused the error + error_message: String, // the relevant error message + } +} +``` + +The subscriptions are an opt-in feature where the contractadmin/owner has to subscribe to the feature by paying the relevant fees. [See more](01_state.md) The subscription is valid for `x` number of blocks where `x` is decided by the module param. The subscription cannot be cancelled but can be extended by attempting to subscribe again. + +## Contents + +1. [State](./01_state.md) +2. [Messages](./02_messages.md) +3. [End Block](./03_end_block.md) +4. [Events](./04_events.md) +5. [Client](./05_client.md) +6. [Module Errors](./06_errors.md) diff --git a/x/cwerrors/types/codec.go b/x/cwerrors/types/codec.go new file mode 100644 index 0000000..21ac411 --- /dev/null +++ b/x/cwerrors/types/codec.go @@ -0,0 +1,36 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + cryptoCodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +// RegisterLegacyAminoCodec registers the necessary interfaces and concrete types on the provided LegacyAmino codec. +// These types are used for Amino JSON serialization. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgSubscribeToError{}, "cwerrors/MsgSubscribeToError", nil) +} + +// RegisterInterfaces registers interfaces types with the interface registry. +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgSubscribeToError{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + ModuleCdc = codec.NewAminoCodec(amino) + amino = codec.NewLegacyAmino() +) + +func init() { + RegisterLegacyAminoCodec(amino) + cryptoCodec.RegisterCrypto(amino) + sdk.RegisterLegacyAminoCodec(amino) + amino.Seal() +} diff --git a/x/cwerrors/types/cwerrors.pb.go b/x/cwerrors/types/cwerrors.pb.go new file mode 100644 index 0000000..5849bf7 --- /dev/null +++ b/x/cwerrors/types/cwerrors.pb.go @@ -0,0 +1,553 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: rollapp/cwerrors/v1/cwerrors.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/gogoproto/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 + +// ModuleErrors defines the module level error codes +type ModuleErrors int32 + +const ( + // ERR_UNKNOWN is the default error code + ModuleErrors_ERR_UNKNOWN ModuleErrors = 0 + // ERR_CALLBACK_EXECUTION_FAILED is the error code for when the error callback fails + ModuleErrors_ERR_CALLBACK_EXECUTION_FAILED ModuleErrors = 1 +) + +var ModuleErrors_name = map[int32]string{ + 0: "ERR_UNKNOWN", + 1: "ERR_CALLBACK_EXECUTION_FAILED", +} + +var ModuleErrors_value = map[string]int32{ + "ERR_UNKNOWN": 0, + "ERR_CALLBACK_EXECUTION_FAILED": 1, +} + +func (x ModuleErrors) String() string { + return proto.EnumName(ModuleErrors_name, int32(x)) +} + +func (ModuleErrors) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_0678d95f425a4823, []int{0} +} + +// SudoError defines the sudo message for the error callback +type SudoError struct { + // module_name is the name of the module throwing the error + ModuleName string `protobuf:"bytes,1,opt,name=module_name,json=moduleName,proto3" json:"module_name,omitempty"` + // error_code is the module level error code + ErrorCode int32 `protobuf:"varint,2,opt,name=error_code,json=errorCode,proto3" json:"error_code,omitempty"` + // contract_address is the address of the contract which will receive the + // error callback + ContractAddress string `protobuf:"bytes,3,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` + // input_payload is any input which caused the error + InputPayload string `protobuf:"bytes,4,opt,name=input_payload,json=inputPayload,proto3" json:"input_payload,omitempty"` + // error_message is the error message + ErrorMessage string `protobuf:"bytes,5,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` +} + +func (m *SudoError) Reset() { *m = SudoError{} } +func (m *SudoError) String() string { return proto.CompactTextString(m) } +func (*SudoError) ProtoMessage() {} +func (*SudoError) Descriptor() ([]byte, []int) { + return fileDescriptor_0678d95f425a4823, []int{0} +} +func (m *SudoError) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SudoError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SudoError.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 *SudoError) XXX_Merge(src proto.Message) { + xxx_messageInfo_SudoError.Merge(m, src) +} +func (m *SudoError) XXX_Size() int { + return m.Size() +} +func (m *SudoError) XXX_DiscardUnknown() { + xxx_messageInfo_SudoError.DiscardUnknown(m) +} + +var xxx_messageInfo_SudoError proto.InternalMessageInfo + +func (m *SudoError) GetModuleName() string { + if m != nil { + return m.ModuleName + } + return "" +} + +func (m *SudoError) GetErrorCode() int32 { + if m != nil { + return m.ErrorCode + } + return 0 +} + +func (m *SudoError) GetContractAddress() string { + if m != nil { + return m.ContractAddress + } + return "" +} + +func (m *SudoError) GetInputPayload() string { + if m != nil { + return m.InputPayload + } + return "" +} + +func (m *SudoError) GetErrorMessage() string { + if m != nil { + return m.ErrorMessage + } + return "" +} + +func init() { + proto.RegisterEnum("rollapp.cwerrors.v1.ModuleErrors", ModuleErrors_name, ModuleErrors_value) + proto.RegisterType((*SudoError)(nil), "rollapp.cwerrors.v1.SudoError") +} + +func init() { + proto.RegisterFile("rollapp/cwerrors/v1/cwerrors.proto", fileDescriptor_0678d95f425a4823) +} + +var fileDescriptor_0678d95f425a4823 = []byte{ + // 343 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x44, 0xd1, 0x41, 0x4f, 0xc2, 0x30, + 0x14, 0x07, 0xf0, 0x55, 0xc5, 0x84, 0x82, 0x81, 0x4c, 0x0f, 0x8b, 0x09, 0x13, 0xf1, 0x82, 0x26, + 0xb2, 0x10, 0xe3, 0x07, 0x18, 0x73, 0x26, 0x04, 0x18, 0x66, 0x4a, 0x34, 0x5e, 0x96, 0xb2, 0x36, + 0x93, 0x64, 0xdb, 0x5b, 0xd6, 0x0d, 0x98, 0x9f, 0xc2, 0x8f, 0xe4, 0xd1, 0x23, 0x47, 0x8f, 0x06, + 0xbe, 0x88, 0x59, 0x87, 0x78, 0x7b, 0xf9, 0xf5, 0x9f, 0xd7, 0xf6, 0x3d, 0xdc, 0x8a, 0xc1, 0xf7, + 0x49, 0x14, 0x69, 0xee, 0x82, 0xc5, 0x31, 0xc4, 0x5c, 0x9b, 0x77, 0x77, 0x75, 0x27, 0x8a, 0x21, + 0x01, 0xf9, 0x78, 0x9b, 0xe9, 0xec, 0x7c, 0xde, 0x3d, 0x3d, 0xf1, 0xc0, 0x03, 0x71, 0xae, 0xe5, + 0x55, 0x11, 0x6d, 0x7d, 0x22, 0x5c, 0x7e, 0x4c, 0x29, 0x98, 0x79, 0x4e, 0x3e, 0xc3, 0x95, 0x00, + 0x68, 0xea, 0x33, 0x27, 0x24, 0x01, 0x53, 0x50, 0x13, 0xb5, 0xcb, 0x36, 0x2e, 0xc8, 0x22, 0x01, + 0x93, 0x1b, 0x18, 0x8b, 0x8e, 0x8e, 0x0b, 0x94, 0x29, 0x7b, 0x4d, 0xd4, 0x2e, 0xd9, 0x65, 0x21, + 0x06, 0x50, 0x26, 0x5f, 0xe2, 0xba, 0x0b, 0x61, 0x12, 0x13, 0x37, 0x71, 0x08, 0xa5, 0x31, 0xe3, + 0x5c, 0xd9, 0x17, 0x4d, 0x6a, 0x7f, 0xae, 0x17, 0x2c, 0x5f, 0xe0, 0xa3, 0x59, 0x18, 0xa5, 0x89, + 0x13, 0x91, 0xcc, 0x07, 0x42, 0x95, 0x03, 0x91, 0xab, 0x0a, 0x7c, 0x28, 0x2c, 0x0f, 0x15, 0xd7, + 0x05, 0x8c, 0x73, 0xe2, 0x31, 0xa5, 0x54, 0x84, 0x04, 0x8e, 0x0a, 0xbb, 0xea, 0xe1, 0xea, 0x48, + 0xbc, 0x50, 0xfc, 0x81, 0xcb, 0x35, 0x5c, 0x31, 0x6d, 0xdb, 0x99, 0x58, 0x03, 0x6b, 0xfc, 0x6c, + 0xd5, 0x25, 0xf9, 0x1c, 0x37, 0x72, 0x30, 0xf4, 0xe1, 0xb0, 0xa7, 0x1b, 0x03, 0xc7, 0x7c, 0x31, + 0x8d, 0xc9, 0x53, 0x7f, 0x6c, 0x39, 0xf7, 0x7a, 0x7f, 0x68, 0xde, 0xd5, 0x51, 0x6f, 0xfc, 0xb5, + 0x56, 0xd1, 0x6a, 0xad, 0xa2, 0x9f, 0xb5, 0x8a, 0x3e, 0x36, 0xaa, 0xb4, 0xda, 0xa8, 0xd2, 0xf7, + 0x46, 0x95, 0x5e, 0x6f, 0xbd, 0x59, 0xf2, 0x96, 0x4e, 0x3b, 0x2e, 0x04, 0x1a, 0xcd, 0x02, 0x16, + 0xf2, 0x19, 0x84, 0xcb, 0xec, 0x5d, 0xdb, 0xce, 0xf8, 0x7a, 0x41, 0x78, 0xa0, 0x2d, 0xff, 0xd7, + 0x91, 0x64, 0x11, 0xe3, 0xd3, 0x43, 0x31, 0xde, 0x9b, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x38, + 0x46, 0xfc, 0x4e, 0xaf, 0x01, 0x00, 0x00, +} + +func (m *SudoError) 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 *SudoError) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SudoError) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ErrorMessage) > 0 { + i -= len(m.ErrorMessage) + copy(dAtA[i:], m.ErrorMessage) + i = encodeVarintCwerrors(dAtA, i, uint64(len(m.ErrorMessage))) + i-- + dAtA[i] = 0x2a + } + if len(m.InputPayload) > 0 { + i -= len(m.InputPayload) + copy(dAtA[i:], m.InputPayload) + i = encodeVarintCwerrors(dAtA, i, uint64(len(m.InputPayload))) + i-- + dAtA[i] = 0x22 + } + if len(m.ContractAddress) > 0 { + i -= len(m.ContractAddress) + copy(dAtA[i:], m.ContractAddress) + i = encodeVarintCwerrors(dAtA, i, uint64(len(m.ContractAddress))) + i-- + dAtA[i] = 0x1a + } + if m.ErrorCode != 0 { + i = encodeVarintCwerrors(dAtA, i, uint64(m.ErrorCode)) + i-- + dAtA[i] = 0x10 + } + if len(m.ModuleName) > 0 { + i -= len(m.ModuleName) + copy(dAtA[i:], m.ModuleName) + i = encodeVarintCwerrors(dAtA, i, uint64(len(m.ModuleName))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintCwerrors(dAtA []byte, offset int, v uint64) int { + offset -= sovCwerrors(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *SudoError) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ModuleName) + if l > 0 { + n += 1 + l + sovCwerrors(uint64(l)) + } + if m.ErrorCode != 0 { + n += 1 + sovCwerrors(uint64(m.ErrorCode)) + } + l = len(m.ContractAddress) + if l > 0 { + n += 1 + l + sovCwerrors(uint64(l)) + } + l = len(m.InputPayload) + if l > 0 { + n += 1 + l + sovCwerrors(uint64(l)) + } + l = len(m.ErrorMessage) + if l > 0 { + n += 1 + l + sovCwerrors(uint64(l)) + } + return n +} + +func sovCwerrors(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozCwerrors(x uint64) (n int) { + return sovCwerrors(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *SudoError) 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 ErrIntOverflowCwerrors + } + 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: SudoError: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SudoError: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ModuleName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCwerrors + } + 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 ErrInvalidLengthCwerrors + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCwerrors + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ModuleName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ErrorCode", wireType) + } + m.ErrorCode = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCwerrors + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ErrorCode |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCwerrors + } + 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 ErrInvalidLengthCwerrors + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCwerrors + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContractAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InputPayload", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCwerrors + } + 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 ErrInvalidLengthCwerrors + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCwerrors + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InputPayload = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ErrorMessage", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCwerrors + } + 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 ErrInvalidLengthCwerrors + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCwerrors + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ErrorMessage = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCwerrors(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCwerrors + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipCwerrors(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, ErrIntOverflowCwerrors + } + 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, ErrIntOverflowCwerrors + } + 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, ErrIntOverflowCwerrors + } + 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, ErrInvalidLengthCwerrors + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupCwerrors + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthCwerrors + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthCwerrors = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowCwerrors = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupCwerrors = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/cwerrors/types/errors.go b/x/cwerrors/types/errors.go new file mode 100644 index 0000000..ab12208 --- /dev/null +++ b/x/cwerrors/types/errors.go @@ -0,0 +1,11 @@ +package types + +import errorsmod "cosmossdk.io/errors" + +var ( + DefaultCodespace = ModuleName + ErrContractNotFound = errorsmod.Register(DefaultCodespace, 2, "contract with given address not found") + ErrUnauthorized = errorsmod.Register(DefaultCodespace, 3, "sender unauthorized to perform the action") + ErrModuleNameMissing = errorsmod.Register(DefaultCodespace, 4, "module name missing from sudo error") + ErrIncorrectSubscriptionFee = errorsmod.Register(DefaultCodespace, 5, "incorrect subscription fee") +) diff --git a/x/cwerrors/types/events.go b/x/cwerrors/types/events.go new file mode 100644 index 0000000..fac41e2 --- /dev/null +++ b/x/cwerrors/types/events.go @@ -0,0 +1,52 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// EmitParamsUpdatedEvent emits an event when the params are updated +func EmitParamsUpdatedEvent(ctx sdk.Context, newParams Params) { + err := ctx.EventManager().EmitTypedEvent(&ParamsUpdatedEvent{ + NewParams: newParams, + }) + if err != nil { + panic(fmt.Errorf("sending ParamsUpdatedEvent event: %w", err)) + } +} + +// EmitSubscribedToErrorsEvent emits an event when a contract is subscribed to errors +func EmitSubscribedToErrorsEvent(ctx sdk.Context, sender, contractAddress string, fees sdk.Coin, subValidTill int64) { + err := ctx.EventManager().EmitTypedEvent(&SubscribedToErrorsEvent{ + Sender: sender, + ContractAddress: contractAddress, + FeesPaid: fees, + SubscriptionValidTill: subValidTill, + }) + if err != nil { + panic(fmt.Errorf("sending SubscribedToErrorsEvent event: %w", err)) + } +} + +// EmitStoringErrorEvent emits an event when an error is stored +func EmitStoringErrorEvent(ctx sdk.Context, sudoError SudoError, deletionBlockHeight int64) { + err := ctx.EventManager().EmitTypedEvent(&StoringErrorEvent{ + Error: sudoError, + DeletionBlockHeight: deletionBlockHeight, + }) + if err != nil { + panic(fmt.Errorf("sending StoringErrorEvent event: %w", err)) + } +} + +// EmitSudoErrorCallbackFailedEvent emits an event when a callback for a sudo error fails +func EmitSudoErrorCallbackFailedEvent(ctx sdk.Context, sudoError SudoError, callbackErr string) { + err := ctx.EventManager().EmitTypedEvent(&SudoErrorCallbackFailedEvent{ + Error: sudoError, + CallbackErrorMessage: callbackErr, + }) + if err != nil { + panic(fmt.Errorf("sending SudoErrorCallbackFailedEvent event: %w", err)) + } +} diff --git a/x/cwerrors/types/events.pb.go b/x/cwerrors/types/events.pb.go new file mode 100644 index 0000000..ce383d2 --- /dev/null +++ b/x/cwerrors/types/events.pb.go @@ -0,0 +1,1097 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: rollapp/cwerrors/v1/events.proto + +package types + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/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 + +// ParamsUpdatedEvent defines the event which is thrown when the module +// parameters are updated +type ParamsUpdatedEvent struct { + // new_params are the new parameters for the module + NewParams Params `protobuf:"bytes,1,opt,name=new_params,json=newParams,proto3" json:"new_params"` +} + +func (m *ParamsUpdatedEvent) Reset() { *m = ParamsUpdatedEvent{} } +func (m *ParamsUpdatedEvent) String() string { return proto.CompactTextString(m) } +func (*ParamsUpdatedEvent) ProtoMessage() {} +func (*ParamsUpdatedEvent) Descriptor() ([]byte, []int) { + return fileDescriptor_e4aab93379fd6199, []int{0} +} +func (m *ParamsUpdatedEvent) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ParamsUpdatedEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ParamsUpdatedEvent.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 *ParamsUpdatedEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_ParamsUpdatedEvent.Merge(m, src) +} +func (m *ParamsUpdatedEvent) XXX_Size() int { + return m.Size() +} +func (m *ParamsUpdatedEvent) XXX_DiscardUnknown() { + xxx_messageInfo_ParamsUpdatedEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_ParamsUpdatedEvent proto.InternalMessageInfo + +func (m *ParamsUpdatedEvent) GetNewParams() Params { + if m != nil { + return m.NewParams + } + return Params{} +} + +// SubscribedToErrorsEvent defines the event which is thrown when a contract +// subscribes to errors +type SubscribedToErrorsEvent struct { + // sender is the address which initiated the subscription + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + // contract_address is the address of the contract which is subscribed to + // errors + ContractAddress string `protobuf:"bytes,2,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` + // fees_paid is the fees paid for the subscription + FeesPaid types.Coin `protobuf:"bytes,3,opt,name=fees_paid,json=feesPaid,proto3" json:"fees_paid"` + // subscription_valid_till is the block height till which the subscription is + // valid + SubscriptionValidTill int64 `protobuf:"varint,4,opt,name=subscription_valid_till,json=subscriptionValidTill,proto3" json:"subscription_valid_till,omitempty"` +} + +func (m *SubscribedToErrorsEvent) Reset() { *m = SubscribedToErrorsEvent{} } +func (m *SubscribedToErrorsEvent) String() string { return proto.CompactTextString(m) } +func (*SubscribedToErrorsEvent) ProtoMessage() {} +func (*SubscribedToErrorsEvent) Descriptor() ([]byte, []int) { + return fileDescriptor_e4aab93379fd6199, []int{1} +} +func (m *SubscribedToErrorsEvent) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SubscribedToErrorsEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SubscribedToErrorsEvent.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 *SubscribedToErrorsEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_SubscribedToErrorsEvent.Merge(m, src) +} +func (m *SubscribedToErrorsEvent) XXX_Size() int { + return m.Size() +} +func (m *SubscribedToErrorsEvent) XXX_DiscardUnknown() { + xxx_messageInfo_SubscribedToErrorsEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_SubscribedToErrorsEvent proto.InternalMessageInfo + +func (m *SubscribedToErrorsEvent) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *SubscribedToErrorsEvent) GetContractAddress() string { + if m != nil { + return m.ContractAddress + } + return "" +} + +func (m *SubscribedToErrorsEvent) GetFeesPaid() types.Coin { + if m != nil { + return m.FeesPaid + } + return types.Coin{} +} + +func (m *SubscribedToErrorsEvent) GetSubscriptionValidTill() int64 { + if m != nil { + return m.SubscriptionValidTill + } + return 0 +} + +// StoringErrorEvent defines the event which is thrown when an error is stored +type StoringErrorEvent struct { + // error is the error which is stored + Error SudoError `protobuf:"bytes,1,opt,name=error,proto3" json:"error"` + // deletion_block_height is the block height at which the error will be pruned + // from the state + DeletionBlockHeight int64 `protobuf:"varint,2,opt,name=deletion_block_height,json=deletionBlockHeight,proto3" json:"deletion_block_height,omitempty"` +} + +func (m *StoringErrorEvent) Reset() { *m = StoringErrorEvent{} } +func (m *StoringErrorEvent) String() string { return proto.CompactTextString(m) } +func (*StoringErrorEvent) ProtoMessage() {} +func (*StoringErrorEvent) Descriptor() ([]byte, []int) { + return fileDescriptor_e4aab93379fd6199, []int{2} +} +func (m *StoringErrorEvent) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StoringErrorEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StoringErrorEvent.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 *StoringErrorEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_StoringErrorEvent.Merge(m, src) +} +func (m *StoringErrorEvent) XXX_Size() int { + return m.Size() +} +func (m *StoringErrorEvent) XXX_DiscardUnknown() { + xxx_messageInfo_StoringErrorEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_StoringErrorEvent proto.InternalMessageInfo + +func (m *StoringErrorEvent) GetError() SudoError { + if m != nil { + return m.Error + } + return SudoError{} +} + +func (m *StoringErrorEvent) GetDeletionBlockHeight() int64 { + if m != nil { + return m.DeletionBlockHeight + } + return 0 +} + +// SudoErrorCallbackFailedEvent defines the event which is thrown when a sudo +// error callback fails +type SudoErrorCallbackFailedEvent struct { + // error is the error for which the callback is executed + Error SudoError `protobuf:"bytes,1,opt,name=error,proto3" json:"error"` + // callback_error_message is the error message of why the callback failed + CallbackErrorMessage string `protobuf:"bytes,2,opt,name=callback_error_message,json=callbackErrorMessage,proto3" json:"callback_error_message,omitempty"` +} + +func (m *SudoErrorCallbackFailedEvent) Reset() { *m = SudoErrorCallbackFailedEvent{} } +func (m *SudoErrorCallbackFailedEvent) String() string { return proto.CompactTextString(m) } +func (*SudoErrorCallbackFailedEvent) ProtoMessage() {} +func (*SudoErrorCallbackFailedEvent) Descriptor() ([]byte, []int) { + return fileDescriptor_e4aab93379fd6199, []int{3} +} +func (m *SudoErrorCallbackFailedEvent) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SudoErrorCallbackFailedEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SudoErrorCallbackFailedEvent.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 *SudoErrorCallbackFailedEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_SudoErrorCallbackFailedEvent.Merge(m, src) +} +func (m *SudoErrorCallbackFailedEvent) XXX_Size() int { + return m.Size() +} +func (m *SudoErrorCallbackFailedEvent) XXX_DiscardUnknown() { + xxx_messageInfo_SudoErrorCallbackFailedEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_SudoErrorCallbackFailedEvent proto.InternalMessageInfo + +func (m *SudoErrorCallbackFailedEvent) GetError() SudoError { + if m != nil { + return m.Error + } + return SudoError{} +} + +func (m *SudoErrorCallbackFailedEvent) GetCallbackErrorMessage() string { + if m != nil { + return m.CallbackErrorMessage + } + return "" +} + +func init() { + proto.RegisterType((*ParamsUpdatedEvent)(nil), "rollapp.cwerrors.v1.ParamsUpdatedEvent") + proto.RegisterType((*SubscribedToErrorsEvent)(nil), "rollapp.cwerrors.v1.SubscribedToErrorsEvent") + proto.RegisterType((*StoringErrorEvent)(nil), "rollapp.cwerrors.v1.StoringErrorEvent") + proto.RegisterType((*SudoErrorCallbackFailedEvent)(nil), "rollapp.cwerrors.v1.SudoErrorCallbackFailedEvent") +} + +func init() { proto.RegisterFile("rollapp/cwerrors/v1/events.proto", fileDescriptor_e4aab93379fd6199) } + +var fileDescriptor_e4aab93379fd6199 = []byte{ + // 490 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x52, 0x3d, 0x6f, 0x13, 0x31, + 0x18, 0xce, 0xd1, 0x52, 0x11, 0x33, 0x00, 0xd7, 0xaf, 0x10, 0xd0, 0x11, 0x65, 0x2a, 0x03, 0x67, + 0xa5, 0x7c, 0x0c, 0x88, 0x01, 0x52, 0x15, 0xb1, 0x20, 0xaa, 0xa4, 0x74, 0x60, 0x39, 0xf9, 0xec, + 0x97, 0x8b, 0x55, 0x9f, 0x7d, 0xb2, 0x9d, 0xa4, 0x61, 0xe5, 0x0f, 0xf4, 0x67, 0x75, 0xac, 0x98, + 0x98, 0x10, 0x4a, 0xfe, 0x08, 0x3a, 0xdb, 0x57, 0x18, 0xb2, 0xb1, 0xdd, 0xbd, 0xcf, 0x87, 0x9f, + 0xe7, 0xb5, 0x51, 0x4f, 0x2b, 0x21, 0x48, 0x55, 0x61, 0x3a, 0x07, 0xad, 0x95, 0x36, 0x78, 0x36, + 0xc0, 0x30, 0x03, 0x69, 0x4d, 0x5a, 0x69, 0x65, 0x55, 0xbc, 0x1d, 0x18, 0x69, 0xc3, 0x48, 0x67, + 0x83, 0xee, 0x4e, 0xa1, 0x0a, 0xe5, 0x70, 0x5c, 0x7f, 0x79, 0x6a, 0x77, 0xad, 0x59, 0x45, 0x34, + 0x29, 0x83, 0x59, 0xb7, 0xbf, 0x8e, 0x71, 0x63, 0xec, 0x39, 0x09, 0x55, 0xa6, 0x54, 0x06, 0xe7, + 0xc4, 0x00, 0x9e, 0x0d, 0x72, 0xb0, 0x64, 0x80, 0xa9, 0xe2, 0xd2, 0xe3, 0xfd, 0x33, 0x14, 0x9f, + 0x38, 0xcf, 0xcf, 0x15, 0x23, 0x16, 0xd8, 0x71, 0x9d, 0x36, 0x7e, 0x8b, 0x90, 0x84, 0x79, 0xe6, + 0x4f, 0xeb, 0x44, 0xbd, 0xe8, 0xe0, 0xee, 0xe1, 0xa3, 0x74, 0x4d, 0xf6, 0xd4, 0x8b, 0x87, 0x9b, + 0x57, 0xbf, 0x9e, 0xb4, 0x46, 0x6d, 0x09, 0x73, 0x3f, 0xe8, 0xff, 0x88, 0xd0, 0xfe, 0x78, 0x9a, + 0x1b, 0xaa, 0x79, 0x0e, 0xec, 0x54, 0x1d, 0x3b, 0x85, 0x77, 0xdf, 0x43, 0x5b, 0x06, 0x24, 0x03, + 0xed, 0x9c, 0xdb, 0xa3, 0xf0, 0x17, 0x3f, 0x45, 0xf7, 0xa9, 0x92, 0x56, 0x13, 0x6a, 0x33, 0xc2, + 0x98, 0x06, 0x63, 0x3a, 0xb7, 0x1c, 0xe3, 0x5e, 0x33, 0x7f, 0xe7, 0xc7, 0xf1, 0x1b, 0xd4, 0xfe, + 0x0a, 0x60, 0xb2, 0x8a, 0x70, 0xd6, 0xd9, 0x70, 0xf9, 0x1e, 0xa6, 0xbe, 0x6a, 0x5a, 0x57, 0x4d, + 0x43, 0xd5, 0xf4, 0x48, 0x71, 0x19, 0xd2, 0xdd, 0xa9, 0x15, 0x27, 0x84, 0xb3, 0xf8, 0x15, 0xda, + 0x37, 0x3e, 0x5b, 0x65, 0xb9, 0x92, 0xd9, 0x8c, 0x08, 0xce, 0x32, 0xcb, 0x85, 0xe8, 0x6c, 0xf6, + 0xa2, 0x83, 0x8d, 0xd1, 0xee, 0xbf, 0xf0, 0x59, 0x8d, 0x9e, 0x72, 0x21, 0xfa, 0xdf, 0x23, 0xf4, + 0x60, 0x6c, 0x95, 0xe6, 0xb2, 0x70, 0x7d, 0x7c, 0x9d, 0xd7, 0xe8, 0xb6, 0xdb, 0x47, 0xd8, 0x53, + 0xb2, 0x76, 0x4f, 0xe3, 0x29, 0xf3, 0x3b, 0x08, 0x61, 0xbc, 0x24, 0x3e, 0x44, 0xbb, 0x0c, 0x04, + 0xb8, 0x14, 0xb9, 0x50, 0xf4, 0x3c, 0x9b, 0x00, 0x2f, 0x26, 0xd6, 0xf5, 0xde, 0x18, 0x6d, 0x37, + 0xe0, 0xb0, 0xc6, 0x3e, 0x38, 0xa8, 0x7f, 0x19, 0xa1, 0xc7, 0x37, 0x76, 0x47, 0x44, 0x88, 0x9c, + 0xd0, 0xf3, 0xf7, 0x84, 0x8b, 0xe6, 0xf6, 0xfe, 0x27, 0xd0, 0x0b, 0xb4, 0x47, 0x83, 0x65, 0xe6, + 0x26, 0x59, 0x09, 0xc6, 0x90, 0x02, 0xc2, 0x4d, 0xec, 0x34, 0xa8, 0xd3, 0x7e, 0xf4, 0xd8, 0xf0, + 0xd3, 0xd5, 0x32, 0x89, 0xae, 0x97, 0x49, 0xf4, 0x7b, 0x99, 0x44, 0x97, 0xab, 0xa4, 0x75, 0xbd, + 0x4a, 0x5a, 0x3f, 0x57, 0x49, 0xeb, 0xcb, 0xcb, 0x82, 0xdb, 0xc9, 0x34, 0x4f, 0xa9, 0x2a, 0x31, + 0x5b, 0x94, 0x20, 0x0d, 0x57, 0xf2, 0x62, 0xf1, 0x0d, 0x87, 0x4c, 0xcf, 0xe6, 0xc4, 0x94, 0xf8, + 0xe2, 0xef, 0x13, 0xb6, 0x8b, 0x0a, 0x4c, 0xbe, 0xe5, 0x5e, 0xe7, 0xf3, 0x3f, 0x01, 0x00, 0x00, + 0xff, 0xff, 0x73, 0xec, 0x3a, 0x81, 0x52, 0x03, 0x00, 0x00, +} + +func (m *ParamsUpdatedEvent) 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 *ParamsUpdatedEvent) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ParamsUpdatedEvent) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.NewParams.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *SubscribedToErrorsEvent) 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 *SubscribedToErrorsEvent) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SubscribedToErrorsEvent) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.SubscriptionValidTill != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.SubscriptionValidTill)) + i-- + dAtA[i] = 0x20 + } + { + size, err := m.FeesPaid.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.ContractAddress) > 0 { + i -= len(m.ContractAddress) + copy(dAtA[i:], m.ContractAddress) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ContractAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *StoringErrorEvent) 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 *StoringErrorEvent) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StoringErrorEvent) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.DeletionBlockHeight != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.DeletionBlockHeight)) + i-- + dAtA[i] = 0x10 + } + { + size, err := m.Error.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *SudoErrorCallbackFailedEvent) 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 *SudoErrorCallbackFailedEvent) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SudoErrorCallbackFailedEvent) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.CallbackErrorMessage) > 0 { + i -= len(m.CallbackErrorMessage) + copy(dAtA[i:], m.CallbackErrorMessage) + i = encodeVarintEvents(dAtA, i, uint64(len(m.CallbackErrorMessage))) + i-- + dAtA[i] = 0x12 + } + { + size, err := m.Error.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintEvents(dAtA []byte, offset int, v uint64) int { + offset -= sovEvents(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ParamsUpdatedEvent) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.NewParams.Size() + n += 1 + l + sovEvents(uint64(l)) + return n +} + +func (m *SubscribedToErrorsEvent) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.ContractAddress) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = m.FeesPaid.Size() + n += 1 + l + sovEvents(uint64(l)) + if m.SubscriptionValidTill != 0 { + n += 1 + sovEvents(uint64(m.SubscriptionValidTill)) + } + return n +} + +func (m *StoringErrorEvent) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Error.Size() + n += 1 + l + sovEvents(uint64(l)) + if m.DeletionBlockHeight != 0 { + n += 1 + sovEvents(uint64(m.DeletionBlockHeight)) + } + return n +} + +func (m *SudoErrorCallbackFailedEvent) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Error.Size() + n += 1 + l + sovEvents(uint64(l)) + l = len(m.CallbackErrorMessage) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func sovEvents(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozEvents(x uint64) (n int) { + return sovEvents(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ParamsUpdatedEvent) 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 ErrIntOverflowEvents + } + 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: ParamsUpdatedEvent: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ParamsUpdatedEvent: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewParams", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.NewParams.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SubscribedToErrorsEvent) 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 ErrIntOverflowEvents + } + 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: SubscribedToErrorsEvent: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SubscribedToErrorsEvent: 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 ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContractAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeesPaid", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.FeesPaid.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SubscriptionValidTill", wireType) + } + m.SubscriptionValidTill = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SubscriptionValidTill |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StoringErrorEvent) 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 ErrIntOverflowEvents + } + 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: StoringErrorEvent: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StoringErrorEvent: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Error.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DeletionBlockHeight", wireType) + } + m.DeletionBlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DeletionBlockHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SudoErrorCallbackFailedEvent) 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 ErrIntOverflowEvents + } + 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: SudoErrorCallbackFailedEvent: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SudoErrorCallbackFailedEvent: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Error.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CallbackErrorMessage", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CallbackErrorMessage = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipEvents(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, ErrIntOverflowEvents + } + 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, ErrIntOverflowEvents + } + 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, ErrIntOverflowEvents + } + 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, ErrInvalidLengthEvents + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupEvents + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthEvents + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthEvents = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowEvents = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupEvents = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/cwerrors/types/expected_keepers.go b/x/cwerrors/types/expected_keepers.go new file mode 100644 index 0000000..2f5e497 --- /dev/null +++ b/x/cwerrors/types/expected_keepers.go @@ -0,0 +1,23 @@ +package types + +import ( + wasmdtypes "github.com/CosmWasm/wasmd/x/wasm/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// WasmKeeperExpected is a subset of the expected wasm keeper +type WasmKeeperExpected interface { + // HasContractInfo returns true if the contract exists + HasContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) bool + // Sudo executes a contract message as a sudoer + Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error) + // GetContractInfo returns the contract info + GetContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) *wasmdtypes.ContractInfo +} + +// BankKeeperExpected is a subset of the expected bank keeper +type BankKeeperExpected interface { + // SendCoinsFromAccountToModule sends coins from an account to a module + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error +} + diff --git a/x/cwerrors/types/genesis.go b/x/cwerrors/types/genesis.go new file mode 100644 index 0000000..e9e3d26 --- /dev/null +++ b/x/cwerrors/types/genesis.go @@ -0,0 +1,22 @@ +package types + +// NewGenesisState creates a new GenesisState object. +func NewGenesisState( + params Params, +) *GenesisState { + return &GenesisState{ + Params: params, + Errors: nil, + } +} + +// DefaultGenesisState returns a default genesis state. +func DefaultGenesis() *GenesisState { + defaultParams := DefaultParams() + return NewGenesisState(defaultParams) +} + +// Validate perform object fields validation. +func (g GenesisState) Validate() error { + return g.Params.Validate() +} diff --git a/x/cwerrors/types/genesis.pb.go b/x/cwerrors/types/genesis.pb.go new file mode 100644 index 0000000..88e5683 --- /dev/null +++ b/x/cwerrors/types/genesis.pb.go @@ -0,0 +1,388 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: rollapp/cwerrors/v1/genesis.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/gogoproto/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 + +// GenesisState defines the cwerrors module's genesis state. +type GenesisState struct { + // params defines all the module parameters. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` + // errors defines all the sudo errors currently registered. + Errors []SudoError `protobuf:"bytes,2,rep,name=errors,proto3" json:"errors"` +} + +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_c591f9026ad26988, []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 (m *GenesisState) GetErrors() []SudoError { + if m != nil { + return m.Errors + } + return nil +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "rollapp.cwerrors.v1.GenesisState") +} + +func init() { proto.RegisterFile("rollapp/cwerrors/v1/genesis.proto", fileDescriptor_c591f9026ad26988) } + +var fileDescriptor_c591f9026ad26988 = []byte{ + // 248 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2c, 0xca, 0xcf, 0xc9, + 0x49, 0x2c, 0x28, 0xd0, 0x4f, 0x2e, 0x4f, 0x2d, 0x2a, 0xca, 0x2f, 0x2a, 0xd6, 0x2f, 0x33, 0xd4, + 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x86, + 0x2a, 0xd1, 0x83, 0x29, 0xd1, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xcb, 0xeb, + 0x83, 0x58, 0x10, 0xa5, 0x52, 0x0a, 0xd8, 0x4c, 0x2b, 0x48, 0x2c, 0x4a, 0xcc, 0x85, 0x1a, 0x26, + 0xa5, 0x84, 0x4d, 0x05, 0xdc, 0x60, 0xb0, 0x1a, 0xa5, 0x76, 0x46, 0x2e, 0x1e, 0x77, 0x88, 0x13, + 0x82, 0x4b, 0x12, 0x4b, 0x52, 0x85, 0x2c, 0xb9, 0xd8, 0x20, 0x86, 0x48, 0x30, 0x2a, 0x30, 0x6a, + 0x70, 0x1b, 0x49, 0xeb, 0x61, 0x71, 0x92, 0x5e, 0x00, 0x58, 0x89, 0x13, 0xcb, 0x89, 0x7b, 0xf2, + 0x0c, 0x41, 0x50, 0x0d, 0x42, 0x36, 0x5c, 0x6c, 0x10, 0x15, 0x12, 0x4c, 0x0a, 0xcc, 0x1a, 0xdc, + 0x46, 0x72, 0x58, 0xb5, 0x06, 0x97, 0xa6, 0xe4, 0xbb, 0x82, 0x78, 0x30, 0xdd, 0x10, 0x29, 0x27, + 0xff, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, + 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x32, 0x4d, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xa9, 0xcc, 0x4d, 0xcd, 0x2b, 0xce, 0xcc, 0xcf, + 0xab, 0xa8, 0xac, 0xd2, 0x87, 0x1a, 0xaf, 0x5b, 0x9e, 0x58, 0x9c, 0xab, 0x5f, 0x81, 0xf0, 0x66, + 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0xd8, 0x87, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x5a, 0xe1, 0x0d, 0x2a, 0x77, 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 + if len(m.Errors) > 0 { + for iNdEx := len(m.Errors) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Errors[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + 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)) + if len(m.Errors) > 0 { + for _, e := range m.Errors { + l = e.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 + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Errors", 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 + } + m.Errors = append(m.Errors, SudoError{}) + if err := m.Errors[len(m.Errors)-1].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/cwerrors/types/genesis_test.go b/x/cwerrors/types/genesis_test.go new file mode 100644 index 0000000..1bacbe3 --- /dev/null +++ b/x/cwerrors/types/genesis_test.go @@ -0,0 +1,55 @@ +package types_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/assert" + + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +func TestGenesisValidate(t *testing.T) { + type testCase struct { + name string + genesis types.GenesisState + errExpected bool + } + + testCases := []testCase{ + { + name: "Fail: Empty values", + genesis: types.GenesisState{}, + errExpected: true, + }, + { + name: "Fail: Invalid params", + genesis: types.GenesisState{ + Params: types.NewParams( + 0, + sdk.NewInt64Coin(sdk.DefaultBondDenom, 100), + 100, + ), + }, + errExpected: true, + }, + { + name: "OK: Valid genesis state", + genesis: types.GenesisState{ + Params: types.DefaultParams(), + }, + errExpected: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tc.genesis.Validate() + if tc.errExpected { + assert.Error(t, err) + return + } + assert.NoError(t, err) + }) + } +} diff --git a/x/cwerrors/types/keys.go b/x/cwerrors/types/keys.go new file mode 100644 index 0000000..8e70411 --- /dev/null +++ b/x/cwerrors/types/keys.go @@ -0,0 +1,44 @@ +package types + +import ( + "cosmossdk.io/collections" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + // ModuleName is the module name. + ModuleName = "cwerrors" + // StoreKey is the module KV storage prefix key. + StoreKey = ModuleName + // QuerierRoute is the querier route for the module. + QuerierRoute = ModuleName + // TStoreKey defines the transient store key + TStoreKey = "t_" + ModuleName +) + +// Collections +var ( + // ParamsKeyPrefix is the prefix for the module parameter store. + ParamsKeyPrefix = collections.NewPrefix(1) + // ErrorIDKey is the prefix for the count of errors + ErrorIDKey = collections.NewPrefix(2) + // ContractErrorsKeyPrefix is the prefix for the collection of all error ids for a given contractzs + ContractErrorsKeyPrefix = collections.NewPrefix(3) + // ErrorsKeyPrefix is the prefix for the collection of all errors + ErrorsKeyPrefix = collections.NewPrefix(4) + // DeletionBlocksKeyPrefix is the prefix for the collection of all error ids which need to be deleted in given block + DeletionBlocksKeyPrefix = collections.NewPrefix(5) + // ContractSubscriptionsKeyPrefix is the prefix for the collection of all contracts with subscriptions + ContractSubscriptionsKeyPrefix = collections.NewPrefix(6) + // SubscriptionEndBlockKeyPrefix is the prefix for the collection of all subscriptions which end at given block + SubscriptionEndBlockKeyPrefix = collections.NewPrefix(7) +) + +// Transient Store +var ( + ErrorsForSudoCallbackKey = []byte{0x00} +) + +func GetErrorsForSudoCallStoreKey(errorID uint64) []byte { + return append(ErrorsForSudoCallbackKey, sdk.Uint64ToBigEndian(errorID)...) +} diff --git a/x/cwerrors/types/msg.go b/x/cwerrors/types/msg.go new file mode 100644 index 0000000..52843fa --- /dev/null +++ b/x/cwerrors/types/msg.go @@ -0,0 +1,30 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkErrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +var ( + _ sdk.Msg = &MsgSubscribeToError{} +) + +// GetSigners implements the sdk.Msg interface. +func (m MsgSubscribeToError) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{sdk.MustAccAddressFromBech32(m.Sender)} +} + +// ValidateBasic implements the sdk.Msg interface. +func (m MsgSubscribeToError) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(m.Sender); err != nil { + return errorsmod.Wrapf(sdkErrors.ErrInvalidAddress, "invalid sender address: %v", err) + } + if _, err := sdk.AccAddressFromBech32(m.ContractAddress); err != nil { + return errorsmod.Wrapf(sdkErrors.ErrInvalidAddress, "invalid contract address: %v", err) + } + if err := m.Fee.Validate(); err != nil { + return errorsmod.Wrapf(sdkErrors.ErrInvalidCoins, "invalid fee: %v", err) + } + return nil +} diff --git a/x/cwerrors/types/params.go b/x/cwerrors/types/params.go new file mode 100644 index 0000000..5ec46e3 --- /dev/null +++ b/x/cwerrors/types/params.go @@ -0,0 +1,49 @@ +package types + +import ( + fmt "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + DefaultErrorStoredTime = int64(302400) // roughly 21 days + DefaultSubscriptionFee = sdk.NewInt64Coin(sdk.DefaultBondDenom, 0) // 1 ARCH (1e18 attoarch) + DefaultSubscriptionPeriod = int64(302400) // roughly 21 days +) + +// NewParams creates a new Params instance. +func NewParams( + errorStoredTime int64, + subscriptionFee sdk.Coin, + subscriptionPeriod int64, +) Params { + return Params{ + ErrorStoredTime: errorStoredTime, + SubscriptionFee: subscriptionFee, + SubscriptionPeriod: subscriptionPeriod, + } +} + +// DefaultParams returns a default set of parameters. +func DefaultParams() Params { + return NewParams( + DefaultErrorStoredTime, + DefaultSubscriptionFee, + DefaultSubscriptionPeriod, + ) +} + +// Validate perform object fields validation. +func (p Params) Validate() error { + if p.ErrorStoredTime <= 0 { + return fmt.Errorf("ErrorStoredTime must be greater than 0. Current value: %d", p.ErrorStoredTime) + } + if !p.SubscriptionFee.IsValid() { + return fmt.Errorf("SubscriptionFee is not valid. Current value: %s", p.SubscriptionFee) + } + if p.SubscriptionPeriod <= 0 { + return fmt.Errorf("SubscriptionPeriod must be greater than 0. Current value: %d", p.SubscriptionPeriod) + } + return nil +} diff --git a/x/cwerrors/types/params.pb.go b/x/cwerrors/types/params.pb.go new file mode 100644 index 0000000..ae3103d --- /dev/null +++ b/x/cwerrors/types/params.pb.go @@ -0,0 +1,401 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: rollapp/cwerrors/v1/params.proto + +package types + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/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 + +// Params defines the set of parameters for the cwerrors module. +type Params struct { + // error_stored_time is the block height until which error is stored + ErrorStoredTime int64 `protobuf:"varint,1,opt,name=error_stored_time,json=errorStoredTime,proto3" json:"error_stored_time,omitempty"` + // subsciption_fee is the fee required to subscribe to error callbacks + SubscriptionFee types.Coin `protobuf:"bytes,2,opt,name=subscription_fee,json=subscriptionFee,proto3" json:"subscription_fee"` + // subscription_period is the period for which the subscription is valid + SubscriptionPeriod int64 `protobuf:"varint,3,opt,name=subscription_period,json=subscriptionPeriod,proto3" json:"subscription_period,omitempty"` +} + +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_bae27602c27c7383, []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) GetErrorStoredTime() int64 { + if m != nil { + return m.ErrorStoredTime + } + return 0 +} + +func (m *Params) GetSubscriptionFee() types.Coin { + if m != nil { + return m.SubscriptionFee + } + return types.Coin{} +} + +func (m *Params) GetSubscriptionPeriod() int64 { + if m != nil { + return m.SubscriptionPeriod + } + return 0 +} + +func init() { + proto.RegisterType((*Params)(nil), "rollapp.cwerrors.v1.Params") +} + +func init() { proto.RegisterFile("rollapp/cwerrors/v1/params.proto", fileDescriptor_bae27602c27c7383) } + +var fileDescriptor_bae27602c27c7383 = []byte{ + // 297 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x90, 0xb1, 0x4e, 0xf3, 0x30, + 0x14, 0x85, 0xe3, 0xbf, 0xbf, 0x3a, 0x84, 0xa1, 0x90, 0x32, 0x94, 0x0e, 0xa6, 0x62, 0xaa, 0x90, + 0xb0, 0x15, 0x10, 0x2f, 0x50, 0x24, 0x06, 0x16, 0xaa, 0xc2, 0xc4, 0x52, 0x39, 0xe9, 0xa5, 0x58, + 0xaa, 0x73, 0x2d, 0xdb, 0x4d, 0x1b, 0x9e, 0x82, 0x57, 0xe1, 0x2d, 0x3a, 0x76, 0x64, 0x42, 0x28, + 0x79, 0x11, 0x54, 0x27, 0x88, 0xb2, 0x59, 0xfe, 0xce, 0x3d, 0x9f, 0x74, 0xc2, 0x81, 0xc1, 0xc5, + 0x42, 0x68, 0xcd, 0xd3, 0x15, 0x18, 0x83, 0xc6, 0xf2, 0x3c, 0xe6, 0x5a, 0x18, 0xa1, 0x2c, 0xd3, + 0x06, 0x1d, 0x46, 0xdd, 0x26, 0xc1, 0x7e, 0x12, 0x2c, 0x8f, 0xfb, 0xc7, 0x73, 0x9c, 0xa3, 0xe7, + 0x7c, 0xf7, 0xaa, 0xa3, 0x7d, 0x9a, 0xa2, 0x55, 0x68, 0x79, 0x22, 0x2c, 0xf0, 0x3c, 0x4e, 0xc0, + 0x89, 0x98, 0xa7, 0x28, 0xb3, 0x9a, 0x9f, 0xbd, 0x93, 0xb0, 0x3d, 0xf6, 0xdd, 0xd1, 0x79, 0x78, + 0xe4, 0xdb, 0xa6, 0xd6, 0xa1, 0x81, 0xd9, 0xd4, 0x49, 0x05, 0x3d, 0x32, 0x20, 0xc3, 0xd6, 0xa4, + 0xe3, 0xc1, 0x83, 0xff, 0x7f, 0x94, 0x0a, 0xa2, 0xbb, 0xf0, 0xd0, 0x2e, 0x13, 0x9b, 0x1a, 0xa9, + 0x9d, 0xc4, 0x6c, 0xfa, 0x0c, 0xd0, 0xfb, 0x37, 0x20, 0xc3, 0x83, 0xcb, 0x13, 0x56, 0x1b, 0xd9, + 0xce, 0xc8, 0x1a, 0x23, 0xbb, 0x41, 0x99, 0x8d, 0xfe, 0x6f, 0x3e, 0x4f, 0x83, 0x49, 0x67, 0xff, + 0xf0, 0x16, 0x20, 0xe2, 0x61, 0xf7, 0x4f, 0x97, 0x06, 0x23, 0x71, 0xd6, 0x6b, 0x79, 0x73, 0xb4, + 0x8f, 0xc6, 0x9e, 0x8c, 0xee, 0x37, 0x25, 0x25, 0xdb, 0x92, 0x92, 0xaf, 0x92, 0x92, 0xb7, 0x8a, + 0x06, 0xdb, 0x8a, 0x06, 0x1f, 0x15, 0x0d, 0x9e, 0xae, 0xe7, 0xd2, 0xbd, 0x2c, 0x13, 0x96, 0xa2, + 0xe2, 0xb3, 0x42, 0x41, 0x66, 0x25, 0x66, 0xeb, 0xe2, 0x95, 0x37, 0x83, 0x5d, 0xac, 0x84, 0x55, + 0x7c, 0xfd, 0xbb, 0xac, 0x2b, 0x34, 0xd8, 0xa4, 0xed, 0xb7, 0xb8, 0xfa, 0x0e, 0x00, 0x00, 0xff, + 0xff, 0x50, 0xcc, 0x01, 0x91, 0x7a, 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 m.SubscriptionPeriod != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.SubscriptionPeriod)) + i-- + dAtA[i] = 0x18 + } + { + size, err := m.SubscriptionFee.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if m.ErrorStoredTime != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.ErrorStoredTime)) + i-- + dAtA[i] = 0x8 + } + 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 m.ErrorStoredTime != 0 { + n += 1 + sovParams(uint64(m.ErrorStoredTime)) + } + l = m.SubscriptionFee.Size() + n += 1 + l + sovParams(uint64(l)) + if m.SubscriptionPeriod != 0 { + n += 1 + sovParams(uint64(m.SubscriptionPeriod)) + } + 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 != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ErrorStoredTime", wireType) + } + m.ErrorStoredTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ErrorStoredTime |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SubscriptionFee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.SubscriptionFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SubscriptionPeriod", wireType) + } + m.SubscriptionPeriod = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SubscriptionPeriod |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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/cwerrors/types/params_test.go b/x/cwerrors/types/params_test.go new file mode 100644 index 0000000..9cfad0b --- /dev/null +++ b/x/cwerrors/types/params_test.go @@ -0,0 +1,82 @@ +package types_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/assert" + + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +func TestParamsValidate(t *testing.T) { + type testCase struct { + name string + params types.Params + errExpected bool + } + + testCases := []testCase{ + { + name: "OK: Default values", + params: types.DefaultParams(), + errExpected: false, + }, + { + name: "OK: All valid values", + params: types.NewParams( + 100, + sdk.NewInt64Coin(sdk.DefaultBondDenom, 100), + 100, + ), + errExpected: false, + }, + { + name: "Fail: ErrorStoredTime: zero", + params: types.NewParams( + 0, + sdk.NewInt64Coin(sdk.DefaultBondDenom, 100), + 100, + ), + errExpected: true, + }, + { + name: "Fail: ErrorStoredTime: negative", + params: types.NewParams( + -2, + sdk.NewInt64Coin(sdk.DefaultBondDenom, 100), + 100, + ), + errExpected: true, + }, + { + name: "Fail: SubsciptionFee: invalid", + params: types.NewParams( + 100, + sdk.Coin{Denom: "", Amount: sdk.NewInt(100)}, + 100, + ), + errExpected: true, + }, + { + name: "Fail: SubscriptionPeriod: zero", + params: types.NewParams( + 100, + sdk.NewInt64Coin(sdk.DefaultBondDenom, 100), + -2, + ), + errExpected: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tc.params.Validate() + if tc.errExpected { + assert.Error(t, err) + return + } + assert.NoError(t, err) + }) + } +} diff --git a/x/cwerrors/types/query.pb.go b/x/cwerrors/types/query.pb.go new file mode 100644 index 0000000..b1a032a --- /dev/null +++ b/x/cwerrors/types/query.pb.go @@ -0,0 +1,1352 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: rollapp/cwerrors/v1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + 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 + +// QueryParamsRequest is the request for Query.Params. +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_4ee790b6306a4ea2, []int{0} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.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 *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryParamsResponse is the response for Query.Params. +type QueryParamsResponse struct { + // params defines all the module parameters. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_4ee790b6306a4ea2, []int{1} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.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 *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// QueryErrorsRequest is the request for Query.Errors. +type QueryErrorsRequest struct { + // contract_address is the address of the contract whose errors to query for + ContractAddress string `protobuf:"bytes,1,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` +} + +func (m *QueryErrorsRequest) Reset() { *m = QueryErrorsRequest{} } +func (m *QueryErrorsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryErrorsRequest) ProtoMessage() {} +func (*QueryErrorsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_4ee790b6306a4ea2, []int{2} +} +func (m *QueryErrorsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryErrorsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryErrorsRequest.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 *QueryErrorsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryErrorsRequest.Merge(m, src) +} +func (m *QueryErrorsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryErrorsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryErrorsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryErrorsRequest proto.InternalMessageInfo + +func (m *QueryErrorsRequest) GetContractAddress() string { + if m != nil { + return m.ContractAddress + } + return "" +} + +// QueryErrorsResponse is the response for Query.Errors. +type QueryErrorsResponse struct { + // errors defines all the contract errors which will be returned + Errors []SudoError `protobuf:"bytes,1,rep,name=errors,proto3" json:"errors"` +} + +func (m *QueryErrorsResponse) Reset() { *m = QueryErrorsResponse{} } +func (m *QueryErrorsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryErrorsResponse) ProtoMessage() {} +func (*QueryErrorsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_4ee790b6306a4ea2, []int{3} +} +func (m *QueryErrorsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryErrorsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryErrorsResponse.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 *QueryErrorsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryErrorsResponse.Merge(m, src) +} +func (m *QueryErrorsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryErrorsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryErrorsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryErrorsResponse proto.InternalMessageInfo + +func (m *QueryErrorsResponse) GetErrors() []SudoError { + if m != nil { + return m.Errors + } + return nil +} + +// QueryIsSubscribedRequest is the request for Query.IsSubscribed. +type QueryIsSubscribedRequest struct { + // contract_address is the address of the contract to query if subscribed + ContractAddress string `protobuf:"bytes,1,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` +} + +func (m *QueryIsSubscribedRequest) Reset() { *m = QueryIsSubscribedRequest{} } +func (m *QueryIsSubscribedRequest) String() string { return proto.CompactTextString(m) } +func (*QueryIsSubscribedRequest) ProtoMessage() {} +func (*QueryIsSubscribedRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_4ee790b6306a4ea2, []int{4} +} +func (m *QueryIsSubscribedRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryIsSubscribedRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryIsSubscribedRequest.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 *QueryIsSubscribedRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryIsSubscribedRequest.Merge(m, src) +} +func (m *QueryIsSubscribedRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryIsSubscribedRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryIsSubscribedRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryIsSubscribedRequest proto.InternalMessageInfo + +func (m *QueryIsSubscribedRequest) GetContractAddress() string { + if m != nil { + return m.ContractAddress + } + return "" +} + +// QueryIsSubscribedResponse is the response for Query.IsSubscribed. +type QueryIsSubscribedResponse struct { + // subscribed defines if the contract is subscribed to sudo error callbacks + Subscribed bool `protobuf:"varint,1,opt,name=subscribed,proto3" json:"subscribed,omitempty"` + // subscription_valid_till defines the block height till which the + // subscription is valid + SubscriptionValidTill int64 `protobuf:"varint,2,opt,name=subscription_valid_till,json=subscriptionValidTill,proto3" json:"subscription_valid_till,omitempty"` +} + +func (m *QueryIsSubscribedResponse) Reset() { *m = QueryIsSubscribedResponse{} } +func (m *QueryIsSubscribedResponse) String() string { return proto.CompactTextString(m) } +func (*QueryIsSubscribedResponse) ProtoMessage() {} +func (*QueryIsSubscribedResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_4ee790b6306a4ea2, []int{5} +} +func (m *QueryIsSubscribedResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryIsSubscribedResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryIsSubscribedResponse.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 *QueryIsSubscribedResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryIsSubscribedResponse.Merge(m, src) +} +func (m *QueryIsSubscribedResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryIsSubscribedResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryIsSubscribedResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryIsSubscribedResponse proto.InternalMessageInfo + +func (m *QueryIsSubscribedResponse) GetSubscribed() bool { + if m != nil { + return m.Subscribed + } + return false +} + +func (m *QueryIsSubscribedResponse) GetSubscriptionValidTill() int64 { + if m != nil { + return m.SubscriptionValidTill + } + return 0 +} + +func init() { + proto.RegisterType((*QueryParamsRequest)(nil), "rollapp.cwerrors.v1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "rollapp.cwerrors.v1.QueryParamsResponse") + proto.RegisterType((*QueryErrorsRequest)(nil), "rollapp.cwerrors.v1.QueryErrorsRequest") + proto.RegisterType((*QueryErrorsResponse)(nil), "rollapp.cwerrors.v1.QueryErrorsResponse") + proto.RegisterType((*QueryIsSubscribedRequest)(nil), "rollapp.cwerrors.v1.QueryIsSubscribedRequest") + proto.RegisterType((*QueryIsSubscribedResponse)(nil), "rollapp.cwerrors.v1.QueryIsSubscribedResponse") +} + +func init() { proto.RegisterFile("rollapp/cwerrors/v1/query.proto", fileDescriptor_4ee790b6306a4ea2) } + +var fileDescriptor_4ee790b6306a4ea2 = []byte{ + // 512 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x93, 0x41, 0x6b, 0xd4, 0x40, + 0x14, 0xc7, 0x37, 0x5d, 0x5d, 0x74, 0x2a, 0x28, 0xd3, 0x8a, 0xeb, 0x56, 0xd3, 0x25, 0x0a, 0xae, + 0x42, 0x33, 0xec, 0x8a, 0x82, 0x20, 0x88, 0x85, 0x1e, 0x3c, 0x59, 0xb3, 0xe2, 0xc1, 0xcb, 0x32, + 0x49, 0x86, 0x38, 0x90, 0xe4, 0xa5, 0x33, 0x93, 0x6d, 0xd7, 0x93, 0xf8, 0x09, 0x04, 0x4f, 0x7e, + 0x04, 0xbf, 0x49, 0x8f, 0x05, 0x2f, 0x9e, 0x44, 0x76, 0xfd, 0x20, 0xb2, 0x33, 0x93, 0x76, 0x17, + 0xd3, 0x56, 0x7a, 0x9b, 0xbc, 0xf7, 0x7f, 0xff, 0xf7, 0x9b, 0xfc, 0x13, 0xb4, 0x29, 0x20, 0x4d, + 0x69, 0x51, 0x90, 0x68, 0x9f, 0x09, 0x01, 0x42, 0x92, 0x71, 0x9f, 0xec, 0x95, 0x4c, 0x4c, 0xfc, + 0x42, 0x80, 0x02, 0xbc, 0x66, 0x05, 0x7e, 0x25, 0xf0, 0xc7, 0xfd, 0xce, 0x7a, 0x02, 0x09, 0xe8, + 0x3e, 0x99, 0x9f, 0x8c, 0xb4, 0x73, 0x27, 0x01, 0x48, 0x52, 0x46, 0x68, 0xc1, 0x09, 0xcd, 0x73, + 0x50, 0x54, 0x71, 0xc8, 0xa5, 0xed, 0xba, 0x11, 0xc8, 0x0c, 0x24, 0x09, 0xa9, 0x64, 0x64, 0xdc, + 0x0f, 0x99, 0xa2, 0x7d, 0x12, 0x01, 0xcf, 0x6d, 0xdf, 0xab, 0x23, 0x39, 0x5e, 0x6a, 0x34, 0xdd, + 0x3a, 0x4d, 0x41, 0x05, 0xcd, 0xac, 0xc2, 0x5b, 0x47, 0xf8, 0xcd, 0x9c, 0x7e, 0x57, 0x17, 0x03, + 0xb6, 0x57, 0x32, 0xa9, 0xbc, 0x5d, 0xb4, 0xb6, 0x54, 0x95, 0x05, 0xe4, 0x92, 0xe1, 0x67, 0xa8, + 0x65, 0x86, 0xdb, 0x4e, 0xd7, 0xe9, 0xad, 0x0e, 0x36, 0xfc, 0x9a, 0xcb, 0xfa, 0x66, 0x68, 0xfb, + 0xd2, 0xe1, 0xaf, 0xcd, 0x46, 0x60, 0x07, 0xbc, 0x17, 0x76, 0xcf, 0x8e, 0x96, 0xd9, 0x3d, 0xf8, + 0x21, 0xba, 0x11, 0x41, 0xae, 0x04, 0x8d, 0xd4, 0x88, 0xc6, 0xb1, 0x60, 0xd2, 0x58, 0x5f, 0x0d, + 0xae, 0x57, 0xf5, 0x97, 0xa6, 0xec, 0x0d, 0x2d, 0x52, 0x65, 0x60, 0x91, 0x9e, 0xa3, 0x96, 0xd9, + 0xdc, 0x76, 0xba, 0xcd, 0xde, 0xea, 0xc0, 0xad, 0x45, 0x1a, 0x96, 0x31, 0xe8, 0xc1, 0x8a, 0xca, + 0xb4, 0xbc, 0x1d, 0xd4, 0xd6, 0xa6, 0xaf, 0xe4, 0xb0, 0x0c, 0x65, 0x24, 0x78, 0xc8, 0xe2, 0x0b, + 0xb0, 0x49, 0x74, 0xbb, 0xc6, 0xc6, 0x12, 0xba, 0x08, 0xc9, 0xe3, 0xaa, 0x76, 0xb8, 0x12, 0x2c, + 0x54, 0xf0, 0x53, 0x74, 0xcb, 0x3e, 0x15, 0xf3, 0xf8, 0x47, 0x63, 0x9a, 0xf2, 0x78, 0xa4, 0x78, + 0x9a, 0xb6, 0x57, 0xba, 0x4e, 0xaf, 0x19, 0xdc, 0x5c, 0x6c, 0xbf, 0x9b, 0x77, 0xdf, 0xf2, 0x34, + 0x1d, 0x7c, 0x6f, 0xa2, 0xcb, 0x7a, 0x2b, 0xfe, 0xe4, 0xa0, 0x96, 0x79, 0xe9, 0xf8, 0x41, 0xed, + 0xf5, 0xff, 0x4d, 0xb8, 0xd3, 0x3b, 0x5f, 0x68, 0xf8, 0xbd, 0x7b, 0x9f, 0x7f, 0xfc, 0xf9, 0xba, + 0x72, 0x17, 0x6f, 0x90, 0xd3, 0x3f, 0x26, 0x8d, 0x60, 0x92, 0x39, 0x0b, 0x61, 0x29, 0xfc, 0xb3, + 0x10, 0x96, 0x43, 0x3e, 0x07, 0xc1, 0x9c, 0xf0, 0x37, 0x07, 0x5d, 0x5b, 0x0c, 0x00, 0x6f, 0x9d, + 0xee, 0x5f, 0x93, 0x77, 0xc7, 0xff, 0x5f, 0xb9, 0x85, 0x7a, 0xa4, 0xa1, 0xee, 0x63, 0xaf, 0x16, + 0x8a, 0xcb, 0xd1, 0x49, 0xc6, 0xdb, 0xaf, 0x0f, 0xa7, 0xae, 0x73, 0x34, 0x75, 0x9d, 0xdf, 0x53, + 0xd7, 0xf9, 0x32, 0x73, 0x1b, 0x47, 0x33, 0xb7, 0xf1, 0x73, 0xe6, 0x36, 0xde, 0x3f, 0x49, 0xb8, + 0xfa, 0x50, 0x86, 0x7e, 0x04, 0x19, 0x89, 0x27, 0x19, 0xcb, 0x25, 0x87, 0xfc, 0x60, 0xf2, 0xb1, + 0x32, 0xdd, 0xda, 0xa7, 0x32, 0x23, 0x07, 0x27, 0xde, 0x6a, 0x52, 0x30, 0x19, 0xb6, 0xf4, 0xdf, + 0xfb, 0xf8, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x59, 0x4f, 0x38, 0x86, 0x8f, 0x04, 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 + +// QueryClient is the client API for Query service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type QueryClient interface { + // Params queries all the module parameters. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // Errors queries all the errors for a given contract. + Errors(ctx context.Context, in *QueryErrorsRequest, opts ...grpc.CallOption) (*QueryErrorsResponse, error) + // IsSubscribed queries if a contract is subscribed to sudo error callbacks. + IsSubscribed(ctx context.Context, in *QueryIsSubscribedRequest, opts ...grpc.CallOption) (*QueryIsSubscribedResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/rollapp.cwerrors.v1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Errors(ctx context.Context, in *QueryErrorsRequest, opts ...grpc.CallOption) (*QueryErrorsResponse, error) { + out := new(QueryErrorsResponse) + err := c.cc.Invoke(ctx, "/rollapp.cwerrors.v1.Query/Errors", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) IsSubscribed(ctx context.Context, in *QueryIsSubscribedRequest, opts ...grpc.CallOption) (*QueryIsSubscribedResponse, error) { + out := new(QueryIsSubscribedResponse) + err := c.cc.Invoke(ctx, "/rollapp.cwerrors.v1.Query/IsSubscribed", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Params queries all the module parameters. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // Errors queries all the errors for a given contract. + Errors(context.Context, *QueryErrorsRequest) (*QueryErrorsResponse, error) + // IsSubscribed queries if a contract is subscribed to sudo error callbacks. + IsSubscribed(context.Context, *QueryIsSubscribedRequest) (*QueryIsSubscribedResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} +func (*UnimplementedQueryServer) Errors(ctx context.Context, req *QueryErrorsRequest) (*QueryErrorsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Errors not implemented") +} +func (*UnimplementedQueryServer) IsSubscribed(ctx context.Context, req *QueryIsSubscribedRequest) (*QueryIsSubscribedResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method IsSubscribed not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/rollapp.cwerrors.v1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Errors_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryErrorsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Errors(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/rollapp.cwerrors.v1.Query/Errors", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Errors(ctx, req.(*QueryErrorsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_IsSubscribed_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryIsSubscribedRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).IsSubscribed(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/rollapp.cwerrors.v1.Query/IsSubscribed", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).IsSubscribed(ctx, req.(*QueryIsSubscribedRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "rollapp.cwerrors.v1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + { + MethodName: "Errors", + Handler: _Query_Errors_Handler, + }, + { + MethodName: "IsSubscribed", + Handler: _Query_IsSubscribed_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "rollapp/cwerrors/v1/query.proto", +} + +func (m *QueryParamsRequest) 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 *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) 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 *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) 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 = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *QueryErrorsRequest) 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 *QueryErrorsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryErrorsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ContractAddress) > 0 { + i -= len(m.ContractAddress) + copy(dAtA[i:], m.ContractAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ContractAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryErrorsResponse) 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 *QueryErrorsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryErrorsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Errors) > 0 { + for iNdEx := len(m.Errors) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Errors[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryIsSubscribedRequest) 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 *QueryIsSubscribedRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryIsSubscribedRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ContractAddress) > 0 { + i -= len(m.ContractAddress) + copy(dAtA[i:], m.ContractAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ContractAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryIsSubscribedResponse) 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 *QueryIsSubscribedResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryIsSubscribedResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.SubscriptionValidTill != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.SubscriptionValidTill)) + i-- + dAtA[i] = 0x10 + } + if m.Subscribed { + i-- + if m.Subscribed { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryErrorsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ContractAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryErrorsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Errors) > 0 { + for _, e := range m.Errors { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryIsSubscribedRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ContractAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryIsSubscribedResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Subscribed { + n += 2 + } + if m.SubscriptionValidTill != 0 { + n += 1 + sovQuery(uint64(m.SubscriptionValidTill)) + } + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsResponse: 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 ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryErrorsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryErrorsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryErrorsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContractAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryErrorsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryErrorsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryErrorsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Errors", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Errors = append(m.Errors, SudoError{}) + if err := m.Errors[len(m.Errors)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryIsSubscribedRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryIsSubscribedRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryIsSubscribedRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContractAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryIsSubscribedResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryIsSubscribedResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryIsSubscribedResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Subscribed", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Subscribed = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SubscriptionValidTill", wireType) + } + m.SubscriptionValidTill = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SubscriptionValidTill |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQuery(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, ErrIntOverflowQuery + } + 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, ErrIntOverflowQuery + } + 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, ErrIntOverflowQuery + } + 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, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/cwerrors/types/query.pb.gw.go b/x/cwerrors/types/query.pb.gw.go new file mode 100644 index 0000000..93cc6f8 --- /dev/null +++ b/x/cwerrors/types/query.pb.gw.go @@ -0,0 +1,319 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: rollapp/cwerrors/v1/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage +var _ = metadata.Join + +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_Errors_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_Errors_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryErrorsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_Errors_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Errors(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Errors_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryErrorsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_Errors_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Errors(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_IsSubscribed_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_IsSubscribed_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryIsSubscribedRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_IsSubscribed_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.IsSubscribed(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_IsSubscribed_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryIsSubscribedRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_IsSubscribed_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.IsSubscribed(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Errors_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Errors_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Errors_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_IsSubscribed_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_IsSubscribed_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_IsSubscribed_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "QueryClient" to call the correct interceptors. +func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Params_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Errors_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Errors_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Errors_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_IsSubscribed_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_IsSubscribed_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_IsSubscribed_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"rollapp", "cwerrors", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_Errors_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"rollapp", "cwerrors", "v1", "errors"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_IsSubscribed_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"rollapp", "cwerrors", "v1", "is_subscribed"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Query_Params_0 = runtime.ForwardResponseMessage + + forward_Query_Errors_0 = runtime.ForwardResponseMessage + + forward_Query_IsSubscribed_0 = runtime.ForwardResponseMessage +) diff --git a/x/cwerrors/types/sudo_errors_test.go b/x/cwerrors/types/sudo_errors_test.go new file mode 100644 index 0000000..fdea2f9 --- /dev/null +++ b/x/cwerrors/types/sudo_errors_test.go @@ -0,0 +1,40 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + e2eTesting "github.com/dymensionxyz/rollapp-wasm/e2e/testing" + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +// TestSudoErrorValidate tests the json encoding of the sudo callback which is sent to the contract +func TestSudoErrorMsgString(t *testing.T) { + contractAddr := e2eTesting.GenContractAddresses(1)[0] + testCases := []struct { + testCase string + msg types.SudoError + expectedMsg string + }{ + { + "ok", + types.SudoError{ + ModuleName: "callback", + ContractAddress: contractAddr.String(), + ErrorCode: 1, + InputPayload: "hello", + ErrorMessage: "world", + }, + `{"module_name":"callback","error_code":1,"contract_address":"cosmos1w0w8sasnut0jx0vvsnvlc8nayq0q2ej8xgrpwgel05tn6wy4r57q8wwdxx","input_payload":"hello","error_message":"world"}`, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.testCase, func(t *testing.T) { + res := tc.msg.Bytes() + require.EqualValues(t, tc.expectedMsg, string(res)) + }) + } +} diff --git a/x/cwerrors/types/sudo_msg.go b/x/cwerrors/types/sudo_msg.go new file mode 100644 index 0000000..5128932 --- /dev/null +++ b/x/cwerrors/types/sudo_msg.go @@ -0,0 +1,31 @@ +package types + +import "encoding/json" + +// SudoMsg is message sent to a contract. +// This is encoded as JSON input to the contract when executing the callback +type SudoMsg struct { + // Error is the endpoint name at the contract which is called + Error *SudoError `json:"error,omitempty"` +} + +// NewSudoMsg creates a new SudoMsg instance. +func NewSudoMsg(sudoErr SudoError) SudoMsg { + return SudoMsg{ + Error: &sudoErr, + } +} + +// Bytes returns the sudo message as JSON bytes +func (s SudoMsg) Bytes() []byte { + msgBz, err := json.Marshal(s) + if err != nil { + panic(err) + } + return msgBz +} + +// String returns the sudo message as JSON string +func (s SudoMsg) String() string { + return string(s.Bytes()) +} diff --git a/x/cwerrors/types/tx.pb.go b/x/cwerrors/types/tx.pb.go new file mode 100644 index 0000000..b0a7d4c --- /dev/null +++ b/x/cwerrors/types/tx.pb.go @@ -0,0 +1,688 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: rollapp/cwerrors/v1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/msgservice" + _ "github.com/cosmos/gogoproto/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 + +// MsgSubscribeToError is the Msg/SubscribeToError request type. +type MsgSubscribeToError struct { + // sender is the address of who is registering the contarcts for callback on + // error + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + // contract is the address of the contract that will be called on error + ContractAddress string `protobuf:"bytes,2,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` + // fee is the subscription fee for the feature (current no fee is charged for + // this feature) + Fee types.Coin `protobuf:"bytes,3,opt,name=fee,proto3" json:"fee"` +} + +func (m *MsgSubscribeToError) Reset() { *m = MsgSubscribeToError{} } +func (m *MsgSubscribeToError) String() string { return proto.CompactTextString(m) } +func (*MsgSubscribeToError) ProtoMessage() {} +func (*MsgSubscribeToError) Descriptor() ([]byte, []int) { + return fileDescriptor_86f9be05f20db712, []int{0} +} +func (m *MsgSubscribeToError) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSubscribeToError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSubscribeToError.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 *MsgSubscribeToError) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSubscribeToError.Merge(m, src) +} +func (m *MsgSubscribeToError) XXX_Size() int { + return m.Size() +} +func (m *MsgSubscribeToError) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSubscribeToError.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSubscribeToError proto.InternalMessageInfo + +func (m *MsgSubscribeToError) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgSubscribeToError) GetContractAddress() string { + if m != nil { + return m.ContractAddress + } + return "" +} + +func (m *MsgSubscribeToError) GetFee() types.Coin { + if m != nil { + return m.Fee + } + return types.Coin{} +} + +// MsgSubscribeToErrorResponse defines the response structure for executing a +// MsgSubscribeToError message. +type MsgSubscribeToErrorResponse struct { + // subscription_valid_till is the block height till which the subscription is + // valid + SubscriptionValidTill int64 `protobuf:"varint,1,opt,name=subscription_valid_till,json=subscriptionValidTill,proto3" json:"subscription_valid_till,omitempty"` +} + +func (m *MsgSubscribeToErrorResponse) Reset() { *m = MsgSubscribeToErrorResponse{} } +func (m *MsgSubscribeToErrorResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSubscribeToErrorResponse) ProtoMessage() {} +func (*MsgSubscribeToErrorResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_86f9be05f20db712, []int{1} +} +func (m *MsgSubscribeToErrorResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSubscribeToErrorResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSubscribeToErrorResponse.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 *MsgSubscribeToErrorResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSubscribeToErrorResponse.Merge(m, src) +} +func (m *MsgSubscribeToErrorResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSubscribeToErrorResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSubscribeToErrorResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSubscribeToErrorResponse proto.InternalMessageInfo + +func (m *MsgSubscribeToErrorResponse) GetSubscriptionValidTill() int64 { + if m != nil { + return m.SubscriptionValidTill + } + return 0 +} + +func init() { + proto.RegisterType((*MsgSubscribeToError)(nil), "rollapp.cwerrors.v1.MsgSubscribeToError") + proto.RegisterType((*MsgSubscribeToErrorResponse)(nil), "rollapp.cwerrors.v1.MsgSubscribeToErrorResponse") +} + +func init() { proto.RegisterFile("rollapp/cwerrors/v1/tx.proto", fileDescriptor_86f9be05f20db712) } + +var fileDescriptor_86f9be05f20db712 = []byte{ + // 382 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x3d, 0x6f, 0xd3, 0x40, + 0x18, 0xc7, 0x6d, 0x8c, 0x22, 0x71, 0x19, 0x88, 0x1c, 0x20, 0x21, 0x20, 0x13, 0x65, 0x0a, 0x48, + 0xdc, 0xe1, 0x20, 0x18, 0xd8, 0x08, 0x62, 0x8c, 0x90, 0x4c, 0x60, 0xe8, 0x12, 0xf9, 0xe5, 0xea, + 0x9e, 0x64, 0xdf, 0x63, 0xdd, 0x73, 0x71, 0x92, 0x8e, 0xfd, 0x04, 0x1d, 0xfb, 0x31, 0xfa, 0x31, + 0x32, 0x66, 0xec, 0x54, 0x55, 0xc9, 0xd0, 0xaf, 0x51, 0xf9, 0x25, 0x6a, 0xa5, 0x66, 0xe8, 0x66, + 0x3f, 0xbf, 0xbf, 0xee, 0xff, 0xbb, 0x17, 0xf2, 0x5e, 0x41, 0x92, 0xf8, 0x59, 0xc6, 0xc2, 0x05, + 0x57, 0x0a, 0x14, 0xb2, 0xdc, 0x65, 0x7a, 0x49, 0x33, 0x05, 0x1a, 0xec, 0x76, 0x4d, 0xe9, 0x9e, + 0xd2, 0xdc, 0xed, 0xbd, 0x8a, 0x21, 0x86, 0x92, 0xb3, 0xe2, 0xab, 0x8a, 0xf6, 0x3a, 0x21, 0x60, + 0x0a, 0xc8, 0x52, 0x8c, 0x8b, 0x25, 0x52, 0x8c, 0x6b, 0xe0, 0xd4, 0x20, 0xf0, 0x91, 0xb3, 0xdc, + 0x0d, 0xb8, 0xf6, 0x5d, 0x16, 0x82, 0x90, 0x15, 0x1f, 0x5c, 0x98, 0xa4, 0x3d, 0xc1, 0xf8, 0xef, + 0x3c, 0xc0, 0x50, 0x89, 0x80, 0x4f, 0xe1, 0x77, 0x51, 0x65, 0xbf, 0x21, 0x0d, 0xe4, 0x32, 0xe2, + 0xaa, 0x6b, 0xf6, 0xcd, 0xe1, 0x0b, 0xaf, 0xfe, 0xb3, 0x3f, 0x92, 0x56, 0x08, 0x52, 0x2b, 0x3f, + 0xd4, 0x33, 0x3f, 0x8a, 0x14, 0x47, 0xec, 0x3e, 0x2b, 0x13, 0x2f, 0xf7, 0xf3, 0x9f, 0xd5, 0xd8, + 0x76, 0x89, 0x75, 0xcc, 0x79, 0xd7, 0xea, 0x9b, 0xc3, 0xe6, 0xe8, 0x2d, 0xad, 0x44, 0x68, 0x21, + 0x42, 0x6b, 0x11, 0xfa, 0x0b, 0x84, 0x1c, 0x3f, 0x5f, 0x5f, 0x7f, 0x30, 0xbc, 0x22, 0xfb, 0xa3, + 0x79, 0x76, 0x7b, 0xf9, 0xa9, 0xae, 0x1a, 0xfc, 0x23, 0xef, 0x0e, 0x98, 0x79, 0x1c, 0x33, 0x90, + 0xc8, 0xed, 0xef, 0xa4, 0x83, 0x15, 0xcb, 0xb4, 0x00, 0x39, 0xcb, 0xfd, 0x44, 0x44, 0x33, 0x2d, + 0x92, 0xa4, 0x54, 0xb6, 0xbc, 0xd7, 0x0f, 0xf1, 0xff, 0x82, 0x4e, 0x45, 0x92, 0x8c, 0xe6, 0xc4, + 0x9a, 0x60, 0x6c, 0x4b, 0xd2, 0x7a, 0xb4, 0xe9, 0x21, 0x3d, 0x70, 0xe2, 0xf4, 0x80, 0x44, 0xef, + 0xcb, 0x53, 0x93, 0x7b, 0xdd, 0xf1, 0x9f, 0xf5, 0xd6, 0x31, 0x37, 0x5b, 0xc7, 0xbc, 0xd9, 0x3a, + 0xe6, 0xf9, 0xce, 0x31, 0x36, 0x3b, 0xc7, 0xb8, 0xda, 0x39, 0xc6, 0xd1, 0xb7, 0x58, 0xe8, 0x93, + 0x79, 0x40, 0x43, 0x48, 0x59, 0xb4, 0x4a, 0xb9, 0x44, 0x01, 0x72, 0xb9, 0x3a, 0x65, 0x75, 0xc5, + 0xe7, 0x85, 0x8f, 0x29, 0x5b, 0xde, 0xbf, 0x11, 0xbd, 0xca, 0x38, 0x06, 0x8d, 0xf2, 0x02, 0xbf, + 0xde, 0x05, 0x00, 0x00, 0xff, 0xff, 0xed, 0x22, 0xdc, 0x7a, 0x44, 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 { + // SubscribeToError defines an operation which will register a contract for a + // sudo callback on errors + SubscribeToError(ctx context.Context, in *MsgSubscribeToError, opts ...grpc.CallOption) (*MsgSubscribeToErrorResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) SubscribeToError(ctx context.Context, in *MsgSubscribeToError, opts ...grpc.CallOption) (*MsgSubscribeToErrorResponse, error) { + out := new(MsgSubscribeToErrorResponse) + err := c.cc.Invoke(ctx, "/rollapp.cwerrors.v1.Msg/SubscribeToError", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // SubscribeToError defines an operation which will register a contract for a + // sudo callback on errors + SubscribeToError(context.Context, *MsgSubscribeToError) (*MsgSubscribeToErrorResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) SubscribeToError(ctx context.Context, req *MsgSubscribeToError) (*MsgSubscribeToErrorResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SubscribeToError not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_SubscribeToError_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSubscribeToError) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SubscribeToError(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/rollapp.cwerrors.v1.Msg/SubscribeToError", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SubscribeToError(ctx, req.(*MsgSubscribeToError)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "rollapp.cwerrors.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SubscribeToError", + Handler: _Msg_SubscribeToError_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "rollapp/cwerrors/v1/tx.proto", +} + +func (m *MsgSubscribeToError) 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 *MsgSubscribeToError) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSubscribeToError) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Fee.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.ContractAddress) > 0 { + i -= len(m.ContractAddress) + copy(dAtA[i:], m.ContractAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.ContractAddress))) + i-- + dAtA[i] = 0x12 + } + 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 *MsgSubscribeToErrorResponse) 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 *MsgSubscribeToErrorResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSubscribeToErrorResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.SubscriptionValidTill != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.SubscriptionValidTill)) + i-- + dAtA[i] = 0x8 + } + 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 *MsgSubscribeToError) 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)) + } + l = len(m.ContractAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Fee.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgSubscribeToErrorResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SubscriptionValidTill != 0 { + n += 1 + sovTx(uint64(m.SubscriptionValidTill)) + } + 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 *MsgSubscribeToError) 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: MsgSubscribeToError: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSubscribeToError: 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 != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractAddress", 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.ContractAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Fee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Fee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + 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 *MsgSubscribeToErrorResponse) 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: MsgSubscribeToErrorResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSubscribeToErrorResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SubscriptionValidTill", wireType) + } + m.SubscriptionValidTill = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SubscriptionValidTill |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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/cwerrors/types/types.go b/x/cwerrors/types/types.go new file mode 100644 index 0000000..b8fa6e4 --- /dev/null +++ b/x/cwerrors/types/types.go @@ -0,0 +1,28 @@ +package types + +import ( + "encoding/json" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Validate perform object fields validation. +func (s SudoError) Validate() error { + _, err := sdk.AccAddressFromBech32(s.ContractAddress) + if err != nil { + return err + } + if s.ModuleName == "" { + return ErrModuleNameMissing + } + return nil +} + +// Bytes returns the json encoding of the sudo callback which is sent to the contract +func (s SudoError) Bytes() []byte { + msgBz, err := json.Marshal(s) + if err != nil { + panic(err) + } + return msgBz +} diff --git a/x/cwerrors/types/types_test.go b/x/cwerrors/types/types_test.go new file mode 100644 index 0000000..742adbd --- /dev/null +++ b/x/cwerrors/types/types_test.go @@ -0,0 +1,72 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + e2eTesting "github.com/dymensionxyz/rollapp-wasm/e2e/testing" + "github.com/dymensionxyz/rollapp-wasm/x/cwerrors/types" +) + +func TestSudoErrorValidate(t *testing.T) { + contractAddr := e2eTesting.GenContractAddresses(1)[0] + + type testCase struct { + name string + sudoError types.SudoError + errExpected bool + } + + testCases := []testCase{ + { + name: "Fail: Empty values", + sudoError: types.SudoError{}, + errExpected: true, + }, + { + name: "Fail: Invalid contract address", + sudoError: types.SudoError{ + ContractAddress: "👻", + ModuleName: "test", + ErrorCode: 1, + InputPayload: "test", + ErrorMessage: "test", + }, + errExpected: true, + }, + { + name: "Fail: Invalid module name", + sudoError: types.SudoError{ + ContractAddress: contractAddr.String(), + ModuleName: "", + ErrorCode: 1, + InputPayload: "test", + ErrorMessage: "test", + }, + errExpected: true, + }, + { + name: "OK: Valid callback", + sudoError: types.SudoError{ + ContractAddress: contractAddr.String(), + ModuleName: "test", + ErrorCode: 1, + InputPayload: "test", + ErrorMessage: "test", + }, + errExpected: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tc.sudoError.Validate() + if tc.errExpected { + assert.Error(t, err) + return + } + assert.NoError(t, err) + }) + } +}