From 31c084f9f978ffeee4048796444dbd4a5d0c44cd Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Sat, 8 Apr 2023 12:14:55 +0200 Subject: [PATCH] docs: update docs and UPGRADING.md on facultative ValidateBasic (#15743) Co-authored-by: Marko --- UPGRADING.md | 6 ++++++ docs/docs/basics/01-tx-lifecycle.md | 19 ++++++++++++------- .../02-messages-and-queries.md | 3 +-- docs/docs/building-modules/03-msg-services.md | 13 ++++++++++--- docs/docs/building-modules/06-keeper.md | 2 +- .../building-modules/09-module-interfaces.md | 2 +- docs/docs/core/00-baseapp.md | 5 +++-- docs/docs/core/01-transactions.md | 6 +++++- 8 files changed, 39 insertions(+), 17 deletions(-) diff --git a/UPGRADING.md b/UPGRADING.md index c2317f3a30c8..891c6dcba7c0 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -95,6 +95,12 @@ All the store imports are now renamed to use `cosmossdk.io/store` instead of `gi ### Modules +#### `**all**` + +[RFC 001](https://docs.cosmos.network/main/rfc/rfc-001-tx-validation) has defined a simplification of the message validation process for modules. +The `sdk.Msg` interface has been updated to not require the implementation of the `ValidateBasic` method. +It is now recommended to validate message directly in the message server. When the validation is performed in the message server, the `ValidateBasic` method on a message is no longer required and can be removed. + #### `x/auth` Methods in the `AccountKeeper` now use `context.Context` instead of `sdk.Context`. Any module that has an interface for it will need to update and re-generate mocks if needed. diff --git a/docs/docs/basics/01-tx-lifecycle.md b/docs/docs/basics/01-tx-lifecycle.md index 22a8886bffe6..29ed40370dea 100644 --- a/docs/docs/basics/01-tx-lifecycle.md +++ b/docs/docs/basics/01-tx-lifecycle.md @@ -86,24 +86,29 @@ through several steps, beginning with decoding `Tx`. When `Tx` is received by the application from the underlying consensus engine (e.g. CometBFT ), it is still in its [encoded](../core/05-encoding.md) `[]byte` form and needs to be unmarshaled in order to be processed. Then, the [`runTx`](../core/00-baseapp.md#runtx-antehandler-runmsgs-posthandler) function is called to run in `runTxModeCheck` mode, meaning the function runs all checks but exits before executing messages and writing state changes. -### ValidateBasic +### ValidateBasic (deprecated) Messages ([`sdk.Msg`](../core/01-transactions.md#messages)) are extracted from transactions (`Tx`). The `ValidateBasic` method of the `sdk.Msg` interface implemented by the module developer is run for each transaction. To discard obviously invalid messages, the `BaseApp` type calls the `ValidateBasic` method very early in the processing of the message in the [`CheckTx`](../core/00-baseapp.md#checktx) and [`DeliverTx`](../core/00-baseapp.md#delivertx) transactions. `ValidateBasic` can include only **stateless** checks (the checks that do not require access to the state). -#### Guideline +:::warning +The `ValidateBasic` method on messages has been deprecated in favor of validating messages directly in their respective [`Msg` services](../building-modules/03-msg-services.md#Validation). + +Read [RFC 001](https://docs.cosmos.network/main/rfc/rfc-001-tx-validation) for more details. +::: -Gas is not charged when `ValidateBasic` is executed, so we recommend only performing all necessary stateless checks to enable middleware operations (for example, parsing the required signer accounts to validate a signature by a middleware) and stateless sanity checks not impacting performance of the `CheckTx` phase. -Other validation operations must be performed when [handling a message](../building-modules/msg-services#Validation) in a module Msg Server. +:::note +`BaseApp` still calls `ValidateBasic` on messages that implements that method for backwards compatibility. +::: -For example, if the message is to send coins from one address to another, `ValidateBasic` likely checks for non-empty addresses and a non-negative coin amount, but does not require knowledge of state such as the account balance of an address. +#### Guideline -See also [Msg Service Validation](../building-modules/03-msg-services.md#Validation). +`ValidateBasic` should not be used anymore. Message validation should be performed in the `Msg` service when [handling a message](../building-modules/msg-services#Validation) in a module Msg Server. ### AnteHandler -After the `ValidateBasic` checks, the `AnteHandler`s are run. Technically, they are optional, but in practice, they are very often present to perform signature verification, gas calculation, fee deduction, and other core operations related to blockchain transactions. +`AnteHandler`s even though optional, are in practice very often used to perform signature verification, gas calculation, fee deduction, and other core operations related to blockchain transactions. A copy of the cached context is provided to the `AnteHandler`, which performs limited checks specified for the transaction type. Using a copy allows the `AnteHandler` to do stateful checks for `Tx` without modifying the last committed state, and revert back to the original if the execution fails. diff --git a/docs/docs/building-modules/02-messages-and-queries.md b/docs/docs/building-modules/02-messages-and-queries.md index 749f7547ece9..e2cb642ee6ef 100644 --- a/docs/docs/building-modules/02-messages-and-queries.md +++ b/docs/docs/building-modules/02-messages-and-queries.md @@ -38,7 +38,7 @@ Each `Msg` service method must have exactly one argument, which must implement t rpc Send(MsgSend) returns (MsgSendResponse); ``` -`sdk.Msg` interface is a simplified version of the Amino `LegacyMsg` interface described [below](#legacy-amino-msgs) with only `ValidateBasic()` and `GetSigners()` methods. For backwards compatibility with [Amino `LegacyMsg`s](#legacy-amino-msgs), existing `LegacyMsg` types should be used as the request parameter for `service` RPC definitions. Newer `sdk.Msg` types, which only support `service` definitions, should use canonical `Msg...` name. +`sdk.Msg` interface is a simplified version of the Amino `LegacyMsg` interface described [below](#legacy-amino-msgs) with the `GetSigners()` method. For backwards compatibility with [Amino `LegacyMsg`s](#legacy-amino-msgs), existing `LegacyMsg` types should be used as the request parameter for `service` RPC definitions. Newer `sdk.Msg` types, which only support `service` definitions, should use canonical `Msg...` name. The Cosmos SDK uses Protobuf definitions to generate client and server code: @@ -63,7 +63,6 @@ https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/types/tx_msg.go#L14-L26 It extends `proto.Message` and contains the following methods: -* [`ValidateBasic() error`](../basics/01-tx-lifecycle.md#ValidateBasic). * `GetSignBytes() []byte`: Return the canonical byte representation of the message. Used to generate a signature. * `GetSigners() []AccAddress`: Return the list of signers. The Cosmos SDK will make sure that each `message` contained in a transaction is signed by all the signers listed in the list returned by this method. diff --git a/docs/docs/building-modules/03-msg-services.md b/docs/docs/building-modules/03-msg-services.md index b1e9a54d336a..c1c57b3f1fef 100644 --- a/docs/docs/building-modules/03-msg-services.md +++ b/docs/docs/building-modules/03-msg-services.md @@ -43,10 +43,12 @@ https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/x/bank/keeper/msg_server.g `sdk.Msg` processing usually follows these 3 steps: -### Validation +### Validation -Before a `msgServer` method is executed, the message's [`ValidateBasic()`](../basics/01-tx-lifecycle.md#ValidateBasic) method has already been called. Since `msg.ValidateBasic()` performs only the most basic checks, this stage must perform all other validation (both *stateful* and *stateless*) to make sure the `message` is valid. Checks performed in the `msgServer` method can be more expensive and the signer is charged gas for these operations. -For example, a `msgServer` method for a `transfer` message might check that the sending account has enough funds to actually perform the transfer. +The message server must perform all validation required (both *stateful* and *stateless*) to make sure the `message` is valid. +The `signer` is charged for the gas cost of this validation. + +For example, a `msgServer` method for a `transfer` message should check that the sending account has enough funds to actually perform the transfer. It is recommended to implement all validation checks in a separate function that passes state values as arguments. This implementation simplifies testing. As expected, expensive validation functions charge additional gas. Example: @@ -60,6 +62,11 @@ ValidateMsgA(msg MsgA, now Time, gm GasMeter) error { } ``` +:::warning +Previously, the `ValidateBasic` method was used to perform simple and stateless validation checks. +This way of validating is deprecated, this means the `msgServer` must perform all validation checks. +::: + ### State Transition After the validation is successful, the `msgServer` method uses the [`keeper`](./06-keeper.md) functions to access the state and perform a state transition. diff --git a/docs/docs/building-modules/06-keeper.md b/docs/docs/building-modules/06-keeper.md index 946eff6ff137..7f8f4457556f 100644 --- a/docs/docs/building-modules/06-keeper.md +++ b/docs/docs/building-modules/06-keeper.md @@ -56,7 +56,7 @@ Of course, it is possible to define different types of internal `keeper`s for th ## Implementing Methods -`Keeper`s primarily expose getter and setter methods for the store(s) managed by their module. These methods should remain as simple as possible and strictly be limited to getting or setting the requested value, as validity checks should have already been performed via the `ValidateBasic()` method of the [`message`](./02-messages-and-queries.md#messages) and the [`Msg` server](./03-msg-services.md) when `keeper`s' methods are called. +`Keeper`s primarily expose getter and setter methods for the store(s) managed by their module. These methods should remain as simple as possible and strictly be limited to getting or setting the requested value, as validity checks should have already been performed by the [`Msg` server](./03-msg-services.md) when `keeper`s' methods are called. Typically, a *getter* method will have the following signature diff --git a/docs/docs/building-modules/09-module-interfaces.md b/docs/docs/building-modules/09-module-interfaces.md index 12f290183b9f..4059e9a87753 100644 --- a/docs/docs/building-modules/09-module-interfaces.md +++ b/docs/docs/building-modules/09-module-interfaces.md @@ -43,7 +43,7 @@ In general, the getter function does the following: * **RunE:** Defines a function that can return an error. This is the function that is called when the command is executed. This function encapsulates all of the logic to create a new transaction. * The function typically starts by getting the `clientCtx`, which can be done with `client.GetClientTxContext(cmd)`. The `clientCtx` contains information relevant to transaction handling, including information about the user. In this example, the `clientCtx` is used to retrieve the address of the sender by calling `clientCtx.GetFromAddress()`. * If applicable, the command's arguments are parsed. In this example, the arguments `[to_address]` and `[amount]` are both parsed. - * A [message](./02-messages-and-queries.md) is created using the parsed arguments and information from the `clientCtx`. The constructor function of the message type is called directly. In this case, `types.NewMsgSend(fromAddr, toAddr, amount)`. Its good practice to call [`msg.ValidateBasic()`](../basics/01-tx-lifecycle.md#ValidateBasic) and other validation methods before broadcasting the message. + * A [message](./02-messages-and-queries.md) is created using the parsed arguments and information from the `clientCtx`. The constructor function of the message type is called directly. In this case, `types.NewMsgSend(fromAddr, toAddr, amount)`. Its good practice to call, if possible, the necessary [message validation methods](../building-modules/03-msg-services.md#Validation) before broadcasting the message. * Depending on what the user wants, the transaction is either generated offline or signed and broadcasted to the preconfigured node using `tx.GenerateOrBroadcastTxCLI(clientCtx, flags, msg)`. * **Adds transaction flags:** All transaction commands must add a set of transaction [flags](#flags). The transaction flags are used to collect additional information from the user (e.g. the amount of fees the user is willing to pay). The transaction flags are added to the constructed command using `AddTxFlagsToCmd(cmd)`. * **Returns the command:** Finally, the transaction command is returned. diff --git a/docs/docs/core/00-baseapp.md b/docs/docs/core/00-baseapp.md index e18267116ce9..f54053ad8076 100644 --- a/docs/docs/core/00-baseapp.md +++ b/docs/docs/core/00-baseapp.md @@ -320,9 +320,10 @@ In the Cosmos SDK, after [decoding transactions](./05-encoding.md), `CheckTx()` to do the following checks: 1. Extract the `sdk.Msg`s from the transaction. -2. Perform _stateless_ checks by calling `ValidateBasic()` on each of the `sdk.Msg`s. This is done +2. **Optionally** perform _stateless_ checks by calling `ValidateBasic()` on each of the `sdk.Msg`s. This is done first, as _stateless_ checks are less computationally expensive than _stateful_ checks. If `ValidateBasic()` fail, `CheckTx` returns before running _stateful_ checks, which saves resources. + This check is still performed for messages that have not yet migrated to the new message validation mechanism defined in [RFC 001](https://docs.cosmos.network/main/rfc/rfc-001-tx-validation) and still have a `ValidateBasic()` method. 3. Perform non-module related _stateful_ checks on the [account](../basics/03-accounts.md). This step is mainly about checking that the `sdk.Msg` signatures are valid, that enough fees are provided and that the sending account has enough funds to pay for said fees. Note that no precise [`gas`](../basics/04-gas-fees.md) counting occurs here, @@ -408,7 +409,7 @@ At any point, if `GasConsumed > GasWanted`, the function returns with `Code != 0 The first thing `RunTx` does upon being called is to retrieve the `context`'s `CacheMultiStore` by calling the `getContextForTx()` function with the appropriate mode (either `runTxModeCheck` or `runTxModeDeliver`). This `CacheMultiStore` is a branch of the main store, with cache functionality (for query requests), instantiated during `BeginBlock` for `DeliverTx` and during the `Commit` of the previous block for `CheckTx`. After that, two `defer func()` are called for [`gas`](../basics/04-gas-fees.md) management. They are executed when `runTx` returns and make sure `gas` is actually consumed, and will throw errors, if any. -After that, `RunTx()` calls `ValidateBasic()` on each `sdk.Msg`in the `Tx`, which runs preliminary _stateless_ validity checks. If any `sdk.Msg` fails to pass `ValidateBasic()`, `RunTx()` returns with an error. +After that, `RunTx()` calls `ValidateBasic()`, when available and for backward compatibility, on each `sdk.Msg`in the `Tx`, which runs preliminary _stateless_ validity checks. If any `sdk.Msg` fails to pass `ValidateBasic()`, `RunTx()` returns with an error. Then, the [`anteHandler`](#antehandler) of the application is run (if it exists). In preparation of this step, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are branched using the `cacheTxContext()` function. diff --git a/docs/docs/core/01-transactions.md b/docs/docs/core/01-transactions.md index 8c1fa3f9e912..e8446646ae8c 100644 --- a/docs/docs/core/01-transactions.md +++ b/docs/docs/core/01-transactions.md @@ -33,7 +33,11 @@ https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/types/tx_msg.go#L42-L50 It contains the following methods: * **GetMsgs:** unwraps the transaction and returns a list of contained `sdk.Msg`s - one transaction may have one or multiple messages, which are defined by module developers. -* **ValidateBasic:** lightweight, [_stateless_](../basics/01-tx-lifecycle.md#types-of-checks) checks used by ABCI messages [`CheckTx`](./00-baseapp.md#checktx) and [`DeliverTx`](./00-baseapp.md#delivertx) to make sure transactions are not invalid. For example, the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/main/x/auth) module's `ValidateBasic` function checks that its transactions are signed by the correct number of signers and that the fees do not exceed what the user's maximum. Note that this function is to be distinct from `sdk.Msg` [`ValidateBasic`](../basics/01-tx-lifecycle.md#ValidateBasic) methods, which perform basic validity checks on messages only. When [`runTx`](./00-baseapp.md#runtx) is checking a transaction created from the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/main/x/auth/spec) module, it first runs `ValidateBasic` on each message, then runs the `auth` module AnteHandler which calls `ValidateBasic` for the transaction itself. +* **ValidateBasic:** lightweight, [_stateless_](../basics/01-tx-lifecycle.md#types-of-checks) checks used by ABCI messages [`CheckTx`](./00-baseapp.md#checktx) and [`DeliverTx`](./00-baseapp.md#delivertx) to make sure transactions are not invalid. For example, the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/main/x/auth) module's `ValidateBasic` function checks that its transactions are signed by the correct number of signers and that the fees do not exceed what the user's maximum. When [`runTx`](./00-baseapp.md#runtx) is checking a transaction created from the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/main/x/auth/spec) module, it first runs `ValidateBasic` on each message, then runs the `auth` module AnteHandler which calls `ValidateBasic` for the transaction itself. + + :::note + This function is different from the deprecated `sdk.Msg` [`ValidateBasic`](../basics/01-tx-lifecycle.md#ValidateBasic) methods, which was performing basic validity checks on messages only. + ::: As a developer, you should rarely manipulate `Tx` directly, as `Tx` is really an intermediate type used for transaction generation. Instead, developers should prefer the `TxBuilder` interface, which you can learn more about [below](#transaction-generation).