Skip to content

Commit

Permalink
Merge PR #4857: Add Context concept doc
Browse files Browse the repository at this point in the history
  • Loading branch information
glozow authored and alexanderbez committed Aug 28, 2019
1 parent 81cf8e6 commit cef7b4c
Showing 1 changed file with 132 additions and 0 deletions.
132 changes: 132 additions & 0 deletions docs/core/context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Context

## Synopsis

This document details the SDK `Context` type.

- [Context](#context)
- [Synopsis](#synopsis)
- [Prerequisites](#prerequisites)
- [Context Definition](#context-definition)
- [Go Context Package](#go-context-package)
- [Cache Wrapping](#cache-wrapping)
- [Next](#next)

## Prerequisites

- [Anatomy of an SDK Application](../basics/app-anatomy.md)
- [Lifecycle of a Transaction](../basics/tx-lifecycle.md)

## Context Definition

The SDK `Context` is a custom data structure that contains Go's stdlib [`context`](https://golang.org/pkg/context)
as its base, and has many additional types within its definition that are specific to the Cosmos SDK
and Tendermint. The `Context` is directly passed between methods and functions as an argument.
The `Context` is integral to tx processing in that it allows modules to easily access their respective
[state](./multistore.md) and retrieve transactional context such as the block header and gas meter.

```go
type Context struct {
ctx context.Context
ms MultiStore
header abci.Header
chainID string
txBytes []byte
logger log.Logger
voteInfo []abci.VoteInfo
gasMeter GasMeter
blockGasMeter GasMeter
checkTx bool
minGasPrice DecCoins
consParams *abci.ConsensusParams
eventManager *EventManager
}
```

- **Context:** The base type is a Go [Context](https://golang.org/pkg/context), which is explained further in the [Go Context Package](#go-context-package) section below.
- **Multistore:** Every application's `BaseApp` contains a [`CommitMultiStore`](./multistore.md) which is provided when a `Context` is created. Calling the `KVStore()` and `TransientStore()` methods allows modules to fetch their
respective `KVStore` using their unique `StoreKey`.
- **ABCI Header:** The [header](https://tendermint.com/docs/spec/abci/abci.html#header) is an ABCI type. It carries important information about the state of the blockchain, such as block height and proposer of the current block.
- **Chain ID:** The unique identification number of the blockchain a block pertains to.
- **Transaction Bytes:** The `[]byte` representation of a transaction being processed using the context. Every transaction is processed by various parts of the SDK and consensus engine (e.g. Tendermint) throughout its [lifecycle](../basics/tx-lifecycle.md), some of which to not have any understanding of transaction types. Thus, transactions are marshaled into the generic `[]byte` type using some kind of [encoding format](./encoding.md) such as [Amino](./amino.md).
- **Logger:** A [Logger](https://github.com/tendermint/tendermint/blob/master/libs/log/logger.go) from the Tendermint libraries. Learn more about logs [here](https://tendermint.com/docs/tendermint-core/how-to-read-logs.html#how-to-read-logs). Modules call this method to create their own unique module-specific logger.
- **VoteInfo:** A list of the ABCI type [`VoteInfo`](https://tendermint.com/docs/spec/abci/abci.html#voteinfo), which includes the name of a validator and a boolean indicating whether they have signed the block.
- **Gas Meters:** Specifically, a `gasMeter` for the transaction currently being processed using the context and a `blockGasMeter` for the entire block it belongs to. Users specify how much in fees they wish to pay for the execution of their transaction; these gas meters keep track of how much [gas](../basics/accounts-fees-gas.md) has been used in the transaction or block so far. If the gas meter runs out, execution halts.
- **CheckTx Mode:** A boolean value indicating whether a transaction should be processed in `CheckTx` or `DeliverTx` mode.
- **Min Gas Price:** The minimum [gas](../basics/accounts-fees-gas.md) price a node is willing to take in order to include a transaction in its block. This price is a local value configured by each node individually.
- **Consensus Params:** The ABCI type [Consensus Parameters](https://tendermint.com/docs/spec/abci/apps.html#consensus-parameters), which enforce certain limits for the blockchain, such as maximum gas for a block.
- **Event Manager:** The event manager allows any caller with access to a `Context` to emit [`Events`](https://github.com/cosmos/cosmos-sdk/blob/master/types/events.go). Modules may define module specific
`Events` by defining various `Types` and `Attributes` or use the common definitions found in `types/`. Clients
can subscribe or query for these `Events`. These `Events` are collected throughout `DeliverTx`, `BeginBlock`,
and `EndBlock` and are returned to Tendermint for indexing. For example:

```go
ctx.EventManager().EmitEvent(sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory)),
)
```

## Go Context Package

A basic `Context` is defined in the [Golang Context Package](https://golang.org/pkg/context). A `Context`
is an immutable data structure that carries request-scoped data across APIs and processes. Contexts
are also designed to enable concurrency and to be used in goroutines.

Contexts are intended to be **immutable**; they should never be edited. Instead, the convention is
to create a child context from its parent using a `With` function. For example:

``` go
childCtx = parentCtx.WithBlockHeader(header)
```

The [Golang Context Package](https://golang.org/pkg/context) documentation instructs developers to
explicitly pass a context `ctx` as the first argument of a process.

## Cache Wrapping

The `Context` contains a `MultiStore`, which allows for cache-wrapping functionality: a `CacheMultiStore`
where each `KVStore` is is wrapped with an ephemeral cache. Processes are free to write changes to
the `CacheMultiStore`, then write the changes back to the original state or disregard them if something
goes wrong. The pattern of usage for a Context is as follows:

1. A process receives a Context `ctx` from its parent process, which provides information needed to
perform the process.
2. The `ctx.ms` is [**cache wrapped**](./multistore.md), i.e. a cached copy of the [multistore](./multistore.md)
is made so that the process can make changes to the state as it executes, without changing the original`ctx.ms` state.
3. The process may read and write from `ctx` as it is executing. It may call a subprocess and pass
`ctx` to it as needed.
4. When a subprocess returns, it checks if the result is a success or failure. If a failure, nothing
needs to be done - the cache wrapped `ctx` is simply discarded. If successful, the changes made to
the cache-wrapped `MultiStore` can be committed to the original `ctx.ms` via `Write()`.

For example, here is a snippet from the [`runTx`](./baseapp.md#runtx-and-runmsgs) function in
[`baseapp`](./baseapp.md):

```go
runMsgCtx, msCache := app.cacheTxContext(ctx, txBytes)
result = app.runMsgs(runMsgCtx, msgs, mode)
result.GasWanted = gasWanted

if mode != runTxModeDeliver {
return result
}

if result.IsOK() {
msCache.Write()
}
```

Here is the process:

1. Prior to calling `runMsgs` on the message(s) in the transaction, it uses `app.cacheTxContext()`
to cache-wrap the context and multistore.
2. The cache-wrapped context, `runMsgCtx`, is used in `runMsgs` to return a result.
3. If the process is running in [`checkTxMode`](./baseapp.md#checktx), there is no need to write the
changes - the result is returned immediately.
4. If the process is running in [`deliverTxMode`](./baseapp.md#delivertx) and the result indicates
a successful run over all the messages, the cached multistore is written back to the original.

## Next

Read about the next core concept.

0 comments on commit cef7b4c

Please sign in to comment.