Skip to content

Commit

Permalink
docs: update docs and UPGRADING.md on facultative ValidateBasic (#15743)
Browse files Browse the repository at this point in the history
Co-authored-by: Marko <[email protected]>
  • Loading branch information
julienrbrt and tac0turtle authored Apr 8, 2023
1 parent 722c3f9 commit 31c084f
Show file tree
Hide file tree
Showing 8 changed files with 39 additions and 17 deletions.
6 changes: 6 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
19 changes: 12 additions & 7 deletions docs/docs/basics/01-tx-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
3 changes: 1 addition & 2 deletions docs/docs/building-modules/02-messages-and-queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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.

Expand Down
13 changes: 10 additions & 3 deletions docs/docs/building-modules/03-msg-services.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/building-modules/06-keeper.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/building-modules/09-module-interfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 3 additions & 2 deletions docs/docs/core/00-baseapp.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.

Expand Down
6 changes: 5 additions & 1 deletion docs/docs/core/01-transactions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down

0 comments on commit 31c084f

Please sign in to comment.