From d875234d49cf515d14c8d170698e5032d69cb726 Mon Sep 17 00:00:00 2001 From: Spoorthi <9302666+spoo-bar@users.noreply.github.com> Date: Mon, 8 Apr 2024 15:35:18 +0530 Subject: [PATCH] docs: Adding ADR-012 for x/cwerrors (#551) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * proto modifications * changing module error code from uint32 to int32 * integrating cwica and errros * Update CHANGELOG.md * linting 🧹 * updating tests * refactor * linting 🧹 * refactor * adding sudo msg wrapper * adding err wrapping for failed callback exec * cleanup * adding err codes for cwerrors * fixing tests * Update cwica_test.go * Update cwica_test.go * adding adr 012 * typo fix Signed-off-by: Spoorthi <9302666+spoo-bar@users.noreply.github.com> --------- Signed-off-by: Spoorthi <9302666+spoo-bar@users.noreply.github.com> --- CHANGELOG.md | 1 + docs/README.md | 2 + docs/adr/ADR-012-cw-errors.md | 168 ++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 docs/adr/ADR-012-cw-errors.md diff --git a/CHANGELOG.md b/CHANGELOG.md index d5afba77..5ae40862 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ Contains all the PRs that improved the code without changing the behaviors. - [#546](https://github.com/archway-network/archway/pull/546) - Adding x/cwerrors module - [#549](https://github.com/archway-network/archway/pull/549) - Integrating x/cwerrors into x/cwica - [#550](https://github.com/archway-network/archway/pull/550) - Integrating x/cwerrors into x/callback module += [#551](https://github.com/archway-network/archway/pull/550) - Adding ADR-012 for x/cwerrors ### Improvements diff --git a/docs/README.md b/docs/README.md index 965b2abd..7798ea7e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -13,6 +13,7 @@ 9. [ADR-009: Callback module](adr/ADR-009-callback-module.md) - **Implemented** 10. [ADR-010: CW Fees](adr/ADR-010-cw-fees.md) - **Implemented** 11. [ADR-011: CW ICA](adr/ADR-011-cw-ica.md) - **Implemented** +12. [ADR-012: CW Errors](adr/ADR-012-cw-errors.md) - **Implemented** ## Module Specifications @@ -21,3 +22,4 @@ 3. [x/callback](../x/callback/spec/README.md) 4. [x/cwfees]() 5. [x/cwica](../x/cwica/spec/README.md) +6. [x/cwerrors](../x/cwerrors/spec/README.md) diff --git a/docs/adr/ADR-012-cw-errors.md b/docs/adr/ADR-012-cw-errors.md new file mode 100644 index 00000000..a436a702 --- /dev/null +++ b/docs/adr/ADR-012-cw-errors.md @@ -0,0 +1,168 @@ +# ADR-012 – CW Errors module + +Date: 2023-04-01 + +## Status + +Implemented + +## Abstract + +We propose a x/cw-errors module which enables a standardized way for the CosmWasm smart contracts to receive errors regarding their executions which are initiated by protocol. + +This is useful for contracts who use the features provided by x/callback anc x/cwica modules. + +## Context + +With the introduction of x/callback and x/cwica modules, there are multiple mechanisms where the protocol executes a smart contract. When a user executes a contract and there is an error, the user receives the error and can act on it. However, when the protocol executes a contract and there is an error, the error could get lost in the node logs and there is no way for the contract or the developer to become aware of the error being thrown at all. + +### x/callback + +When the protocol executes a callback, there could be error thrown due to +1. The contract does not have the expected sudo entrypoint +2. The callback execution might consume more gas than is allowed +3. There could be an error thrown from the contract + +### x/cwica + +When a contract wants to execute an ica tx on another chain, there could be error thrown due to +1. The counterparty could not unmarshall the msg, or did not recognize the msg +2. The counterparty tx execution failed +3. The ibc packet timedout + +Under the above mentioned situations, we need a way to convey the error and its details to the contract. + +## Decision + +We will implement a new module called x/cwerrors which allows the protocol invoked contract errors to be able know an error has occurred. + +When a module which does Sudo execution on a contract encounters an error, it will execute the `cwErrorsKeeper.SetError(ctx, sudoErr)` where the `sudoErr` is of the following type + +```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. Every module needs to implement an error id for unique errors they want to throw + 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; +} +``` +When the x/cwerror module receives a contract error (lets call these sudoErrs for the rest of the ADR), it will handle the sudoErr in one of two ways. + +### 1. Read the SudoErr - (*Default*) + +The default behaviour is that the sudoErr will be stored in the module state for x number of blocks. The duration is dependent on a module parameter. + +This sudoErr can be queried by the contract using the Stargate Querier. +```protobuf +service Query { + // Errors queries all the errors for a given contract. + rpc Errors(QueryErrorsRequest) returns (QueryErrorsResponse) { + option (google.api.http).get = "/archway/cwerrors/v1/errors"; + } +} + +message QueryErrorsRequest { + // contract_address is the address of the contract whose errors to query for + string contract_address = 1; +} + +message QueryErrorsResponse { + // errors defines all the contract errors which will be returned + repeated SudoError errors = 1 [ (gogoproto.nullable) = false ]; +} +``` + +### 2. Receive the SudoErr - (*Opt-in*) + +The module exposes a subscription service where a contract can register a SudoErr Subscription for x number of blocks by paying y amount of tokens. The x and y values are dependent on module parameters. + + +In case, a contract has the subscription, the sudoErr will be stored in the transient store. At the end of the block, all the sudoErrs are executed as `Sudo::Error` on the contract entrypoint. In case, this execution fails, the execution error will be stored in the state so as to not allow cascading error executions. This execution error will be wrapped in a new SudoError msg where the module_name would be `cwerror` and the error_code would be `ERR_CALLBACK_EXECUTION_FAILED` + +The max gas allowed for these sudoErr executions will be as small as possible while still be useful. This execution is only meant for error handling and not for complex logic prosessing. + +The contract will receive a callback at the following entrypoint + +```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 + } +} +``` + +A contract can subscribe to the error by registering with the following Msg + +```protobuf +service Msg { + // SubscribeToError defines an operation which will register a contract for a sudo callback on errors + rpc SubscribeToError(MsgSubscribeToError) returns (MsgSubscribeToErrorResponse); +} + +message MsgSubscribeToError { + // 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 ; +} + +message MsgSubscribeToErrorResponse { + // subscription_valid_till is the block height till which the subscription is valid + int64 subscription_valid_till = 1; +} +``` + +As mentioned above, the module params will be following: +```protobuf +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; + // subscription_period is the period for which the subscription is valid + int64 subscription_period = 3; +} +``` + +## Consequences + +### Backwards Compatibility + +Since the feature is being added as a new module, this should not cause any backwards compatibility issues. + +### Positive + +1. Allows for future proofing against the growing codebase and having a dedicated error handling mechanism for sudo calls. +2. Easier dapp development on top of Archway as only one error entrypoint needs to be implemented for all protocol invoked sudoErrs. +3. Increases protocol revenue for the validators and delegators + +### Negative + +1. Increases the exposure of custom wasm bindings of the protocol +2. Introduces a layer of complexity to the protocol architecture + +## Further Discussions + +Future iterations of the module could include the following: + +1. Subscribe to errors from other contracts. e.g a dapp/multisig consists of many contracts working together. One contract can choose to receive errors about all its dependent contracts in one place. +2. Customize which module/error codes a contract wants a sudo call for, and which error codes its fine reading via stargate query. + +## References + +1. [RFC: x/cwerrors module](https://github.com/orgs/archway-network/discussions/35) +2. [AIP: x/cwerrors module](https://github.com/archway-network/archway/issues/544) +3. [SPEC: x/cwerrors module](../../x/cwerrors/spec/README.md)