Skip to content

Commit

Permalink
Update 01-tx-lifecycle.md
Browse files Browse the repository at this point in the history
  • Loading branch information
samricotta committed May 8, 2024
1 parent 5ea4174 commit c0ace62
Showing 1 changed file with 69 additions and 11 deletions.
80 changes: 69 additions & 11 deletions docs/learn/beginner/01-tx-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,40 +85,79 @@ To adapt this function for different consensus engines, ensure that the `clientC

## Transaction Processing

After a transaction is broadcasted to the network, it undergoes several processing steps to ensure its validity. These steps are run by the application's `BaseApp`, which is in chargee of trasaction processing. Within the SDK we wrap the `BaseApp` with a runtime layer defined in `runtime/app.go`. This is where `BaseApp` is extended with additional functionality to handle transactions allowing for more flexibility and customisation, for example being able to swap out CometBFT for another consensus engine. The key steps in transaction processing are:
After a transaction is broadcasted to the network, it undergoes several processing steps to ensure its validity. These steps are managed by the application's core transaction processing layer, which is responsible for handling transactions. Within the SDK, this core layer is wrapped with a runtime layer defined in `runtime/app.go`. This layer extends the core functionality with additional features to handle transactions, allowing for more flexibility and customization, such as the ability to swap out different consensus engines. The key steps in transaction processing are:

### Decoding

The transaction is decoded from its binary format into a structured format that the application can understand.

### Validation

Preliminary checks are performed. These include signature verification to ensure the transaction hasn't been tampered with and checking if the transaction meets the minimum fee requirements, which is handled by the AnteHandler. The Antehandler is invoked during the `runTx` method in `BaseApp`.
* **During Transaction Processing:** Transactions are received in the encoded `[]byte` form. Nodes first unmarshal the transaction using the configuration defined in the app, then proceed to execute the transaction, which includes state changes.

### Routing

How Routing Works:
The transaction is routed to the appropriate module based on the message type. Each message type is associated with a specific module, which is responsible for processing the message. The `BaseApp` uses a `MsgServiceRouter` to direct the transaction to the correct module.
The transaction is routed to the appropriate module based on the message type. Each message type is associated with a specific module, which is responsible for processing the message. The core transaction processing layer uses a `MsgServiceRouter` to direct the transaction to the correct module.

1. **Transaction Type Identification:**
Each transaction contains one or more messages (`sdk.Msg`), and each message has a `Type()` method that identifies its type. This type is used to determine the appropriate module to handle the message.
2. **Module Routing:**
The BaseApp holds a `MsgServiceRouter` which maps each message type to a specific module's handler. When a transaction is processed, `BaseApp` uses this router to direct the message to the correct module.

The core transaction processing layer holds a `MsgServiceRouter` which maps each message type to a specific module's handler. When a transaction is processed, this router is used to direct the message to the correct module.

### Example of Routing

Let's say there is a transaction that involves transferring tokens. The message type might be `MsgSend`, and the `MsgServiceRouter` in `BaseApp` would route this message to the bank module's handler. The bank module would then validate the transaction details (like sender balance) and update the state to reflect the transfer if valid...

### Validation

Preliminary checks are performed. These include signature verification to ensure the transaction hasn't been tampered with and checking if the transaction meets the minimum fee requirements, which is handled by the AnteHandler. The Antehandler is invoked during the `runTx` method in `BaseApp`.

#### ValidateBasic (deprecated)

* Messages ([`sdk.Msg`](../advanced/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`](../advanced/00-baseapp.md#checktx) and [`DeliverTx`](../advanced/00-baseapp.md#delivertx) transactions.
`ValidateBasic` can include only **stateless** checks (the checks that do not require access to the state).

:::warning
The `ValidateBasic` method on messages has been deprecated in favor of validating messages directly in their respective [`Msg` services](../../build/building-modules/03-msg-services.md#Validation).

Read [RFC 001](https://docs.cosmos.network/main/rfc/rfc-001-tx-validation) for more details.
:::

### Discard or Addition to Mempool

If at any point during the initial transaction validation the transaction (`Tx`) fails, it is discarded, and the transaction lifecycle ends there. Otherwise, if it passes this preliminary check successfully, the general protocol is to relay it to peer nodes and add it to the node's transaction pool (often referred to as the mempool). This makes the `Tx` a candidate for inclusion in the next block, pending further consensus processes.

The **app-side mempool**, serves the purpose of keeping track of transactions seen by all full-nodes. Full-nodes maintain a **mempool cache** of the last `mempool.cache_size` transactions they have seen, serving as a first line of defense to prevent replay attacks. Ideally, `mempool.cache_size` should be large enough to encompass all transactions in the full mempool. If the mempool cache is too small to track all transactions, the initial transaction validation process is responsible for identifying and rejecting replayed transactions.

Currently existing preventative measures include fees and a `sequence` (nonce) counter to distinguish replayed transactions from identical but valid ones. If an attacker tries to spam nodes with many copies of a `Tx`, full-nodes maintaining a transaction cache reject all identical copies. Even if the copies have incremented sequence numbers, attackers are disincentivized by the need to pay fees.

Validator nodes maintain a transaction pool to prevent replay attacks, similar to full-nodes, but also use it to hold unconfirmed transactions in preparation for block inclusion. It's important to note that even if a `Tx` passes all preliminary checks, it can still be found invalid later on, as these initial checks do not fully execute the transaction's logic.


### Module Execution

1. The transaction is first routed to the appropriate module based on the message type. This is handled by the `MsgServiceRouter`.

2. Each module has specific handlers that are triggered once the message is routed to the module. These handlers contain the logic needed to process the transaction, such as updating account balances, transferring tokens, or other state changes.

3. During the execution, the module's handler will modify the state as required by the business logic. This could involve writing to the module's portion of the state store.
3. After initial checks are completed in the transaction processing phase, each message within the transaction is executed. This execution is managed by a routing component, which ensures that each message is directed to the correct module for handling. The routing is based on the message type, utilizing a mechanism similar to the `MsgServiceRouter`. This ensures that messages are processed by the appropriate module's handler, according to the specific business logic required.

4. Modules can emit events and log information during execution, which are used for monitoring and querying transaction outcomes.
For messages that adhere to older standards or specific formats, a routing function retrieves the route name from the message, identifying the corresponding module. The message is then processed by the designated handler within that module, ensuring accurate and consistent application of the transaction's logic.

4. During the execution, the module's handler will modify the state as required by the business logic. This could involve writing to the module's portion of the state store.

5. Modules can emit events and log information during execution, which are used for monitoring and querying transaction outcomes.


### State Changes During Consensus

Before finalizing the transactions within a block, full-nodes perform a second round of checks using `validateBasicMsgs` and `AnteHandler`. This is crucial to ensure that all transactions are valid, especially since a malicious proposer might include invalid transactions. Unlike the checks during the transaction addition to the Mempool, the `AnteHandler` in this phase does not compare the transaction's `gas-prices` to the node's `min-gas-prices`. This is because `min-gas-prices` can vary between nodes, and using them here would lead to nondeterministic results across the network.

* After module execution, the transactions are included in a block proposal by the proposer.

* All full-nodes that receive this block proposal execute the transactions to ensure that the state changes are applied consistently across all nodes, maintaining the deterministic nature of the blockchain. This includes the execution of initial, transaction-specific, and finalizing operations.

The following code snippet demonstrates how a module handler function processes a transaction and applies state changes, reflecting the checks and operations discussed:

```go
func handleMsgSend(ctx sdk.Context, keeper BankKeeper, msg MsgSend) error {
Expand All @@ -133,6 +172,17 @@ func handleMsgSend(ctx sdk.Context, keeper BankKeeper, msg MsgSend) error {
}
```

This function checks the sender's balance, transfers coins if sufficient funds are available, and emits an event to log the transaction. This is an example of state changes being applied during the module execution phase.

## Inclusion in a Block

Consensus is the process through which nodes in a blockchain network agree on which transactions to include in the blockchain. This process typically occurs in rounds, starting with a designated node (often called a proposer) compiling a block from transactions in its transaction pool (mempool). The block is then proposed to other nodes (validators) in the network.

Each validator independently verifies the proposed block against the blockchain's rules. If the block is accepted by a sufficient number of validators according to the network's consensus rules, it is added to the blockchain. If not, the process may repeat, potentially with a different proposer or even resulting in a block that contains no transactions (a nil block).

The specific mechanisms of choosing a proposer, the criteria for a valid block, and the method of achieving agreement among validators can vary depending on the consensus algorithm used by the blockchain.


## Post-Transaction Handling

After execution, any additional actions that need to be taken are processed. This could include updating logs, sending events, or handling errors.
Expand All @@ -147,6 +197,14 @@ After a transaction is executed in the Cosmos SDK, several steps are taken to fi

3. Error Handling: If any errors occur during transaction execution, they are handled appropriately, which may include rolling back certain operations to maintain state consistency.

4. State Commitment: Changes made to the state during the transaction are finalized and written to the blockchain. This step is crucial as it ensures that all state transitions are permanently recorded.
4. State Commitment: Changes made to the state during the transaction are finalised and written to the blockchain. This step is crucial as it ensures that all state transitions are permanently recorded.

5. PostHandlers: After the execution of the message, `PostHandlers` are run. If they fail, the state changes made during `runMsgs` and by the `PostHandlers` themselves are both reverted. This ensures that only successful transactions affect the state.


After post-transaction handling, the exact sequence of the transaction lifecycle is dependent on the consensus mechanism used. This includes how transactions are grouped into blocks, how blocks are validated, and how consensus is achieved among validators to commit the block to the blockchain. Each consensus protocol may implement these steps differently to ensure network agreement and maintain the integrity of the blockchain state.

## Learn More

For a deeper dive into the underlying mechanisms of transaction processing and block commitment in the Cosmos SDK, consider exploring the [BaseApp documentation](../advanced/00-baseapp.md). This advanced documentation provides detailed insights into the internal workings and state management of the Cosmos SDK.

After post-transaction handling in the Cosmos SDK, the exact sequence of the transaction lifecycle is dependent on the consensus mechanism used. This includes how transactions are grouped into blocks, how blocks are validated, and how consensus is achieved among validators to commit the block to the blockchain. Each consensus protocol may implement these steps differently to ensure network agreement and maintain the integrity of the blockchain state.

0 comments on commit c0ace62

Please sign in to comment.