Skip to content

Commit

Permalink
docs(core): create docs for environment and core API (backport #22156) (
Browse files Browse the repository at this point in the history
#22159)

Co-authored-by: Matt Kocubinski <mkocubinski@gmail.com>
Co-authored-by: Julien Robert <julien@rbrt.fr>
  • Loading branch information
3 people authored Oct 7, 2024
1 parent befdf58 commit e442264
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 19 deletions.
35 changes: 16 additions & 19 deletions docs/learn/advanced/05-encoding.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,35 @@ While encoding in the Cosmos SDK used to be mainly handled by `go-amino` codec,

## Encoding

The Cosmos SDK utilizes two binary wire encoding protocols, [Amino](https://github.com/tendermint/go-amino/) which is an object encoding specification and [Protocol Buffers](https://developers.google.com/protocol-buffers), a subset of Proto3 with an extension for
interface support. See the [Proto3 spec](https://developers.google.com/protocol-buffers/docs/proto3)
for more information on Proto3, which Amino is largely compatible with (but not with Proto2).
The Cosmos SDK supports two wire encoding protocols. Binary encoding is fulfilled by [Protocol
Buffers](https://developers.google.com/protocol-buffers), specifically the
[gogoprotobuf](https://github.com/cosmos/gogoproto/) implementation, which is a subset of
[Proto3](https://developers.google.com/protocol-buffers/docs/proto3) with an extension for
interface support. Text encoding is fulfilled by [Amino](https://github.com/tendermint/go-amino).

Due to Amino having significant performance drawbacks, being reflection-based, and
not having any meaningful cross-language/client support, Protocol Buffers, specifically
[gogoprotobuf](https://github.com/cosmos/gogoproto/), is being used in place of Amino.
Note, this process of using Protocol Buffers over Amino is still an ongoing process.
Due to Amino having significant performance drawbacks, being reflection-based, and not having
any meaningful cross-language/client support, Amino is only used to generate JSON (Amino
JSON) in order to support the Amino JSON sign mode, and for JSON RPC endpoints.

Binary wire encoding of types in the Cosmos SDK can be broken down into two main
categories, client encoding and store encoding. Client encoding mainly revolves
around transaction processing and signing, whereas store encoding revolves around
types used in state-machine transitions and what is ultimately stored in the Merkle
tree.

For store encoding, protobuf definitions can exist for any type and will typically
have an Amino-based "intermediary" type. Specifically, the protobuf-based type
definition is used for serialization and persistence, whereas the Amino-based type
is used for business logic in the state-machine where they may convert back-n-forth.
Note, the Amino-based types may slowly be phased-out in the future, so developers
should take note to use the protobuf message definitions where possible.
For storage encoding, module developers are encouraged to use Protobuf encoding for their types
but may choose any encoding schema they like. The
[collections](../../build/packages/02-collections.md) package automatically handles encoding and
decoding of state for you.

In the `codec` package, there exists two core interfaces, `BinaryCodec` and `JSONCodec`,
where the former encapsulates the current Amino interface except it operates on
types implementing the latter instead of generic `interface{}` types.

The `ProtoCodec`, where both binary and JSON serialization is handled
via Protobuf. This means that modules may use Protobuf encoding, but the types must
implement `ProtoMarshaler`. If modules wish to avoid implementing this interface
for their types, this is autogenerated via [buf](https://buf.build/)

Modules are recommended to use [collections](../../build/packages/02-collections.md) for handling encoding and decoding of state. Usage of collections handles marshal and unmarshal for you. By default protobuf is used but other encodings can be used if preferred.
The `ProtoCodec`, where both binary and JSON serialization is handled via Protobuf. This means
that modules may use Protobuf encoding, but the types must implement `ProtoMarshaler`. If
modules wish to avoid implementing this interface for their types, this is autogenerated via
[buf](https://buf.build/)

### Gogoproto

Expand Down
141 changes: 141 additions & 0 deletions docs/learn/advanced/17-core.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
---
sidebar_position: 1
---

# Core

Core is package which specifies the interfaces for core components of the Cosmos SDK. Other
packages in the SDK implement these interfaces to provide the core functionality. This design
provides modularity and flexibility to the SDK, allowing developers to swap out implementations
of core components as needed. As such it is often referred to as the Core API.

## Environment

The `Environment` struct is a core component of the Cosmos SDK. It provides access to the core
services of the SDK, such as the KVStore, EventManager, and Logger. The `Environment` struct is
passed to modules and other components of the SDK to provide access to these services.

```go reference
https://github.com/cosmos/cosmos-sdk/blob/core/v1.0.0-alpha.4/core/appmodule/v2/environment.go#L16-L29
```

Historically the SDK has used an [sdk.Context](02-context.md) to pass around services and data.
`Environment` is a newer construct that is intended to replace an `sdk.Context` in many cases.
`sdk.Context` will be deprecated in the future on the same timeline as [Baseapp](00-baseapp.md).

## Branch Service

The [BranchService](https://pkg.go.dev/cosmossdk.io/core/branch#Service.Execute) provides an
interface to execute arbitrary code in a branched store. This is useful for executing code
that needs to make changes to the store, but may need to be rolled back if an error occurs.
Below is a contrived example based on the `x/epoch` module's BeginBlocker logic.

```go
func (k Keeper) BeginBlocker(ctx context.Context) error {
err := k.EpochInfo.Walk(
// ...
ctx,
nil,
func(key string, epochInfo types.EpochInfo) (stop bool, err error) {
// ...
if err := k.BranchService.Execute(ctx, func(ctx context.Context) error {
return k.AfterEpochEnd(ctx, epochInfo.Identifier, epochInfo.CurrentEpoch)
}); err != nil {
return true, err
}
})
}
```

Note that calls to `BranchService.Execute` are atomic and cannot share state with each other
except when the transaction is successful. If successful, the changes made to the store will be
committed. If an error occurs, the changes will be rolled back.

## Event Service

The Event Service returns a handle to an [EventManager](https://pkg.go.dev/cosmossdk.io/core@v1.0.0-alpha.4/event#Manager)
which can be used to emit events. For information on how to emit events and their meaning
in the SDK see the [Events](08-events.md) document.

Note that core's `EventManager` API is a subset of the EventManager API described above; the
latter will be deprecated and removed in the future. Roughly speaking legacy `EmitTypeEvent`
maps to `Emit` and legacy `EmitEvent` maps to `EmitKV`.

```go reference
https://github.com/cosmos/cosmos-sdk/blob/core/v1.0.0-alpha.4/core/event/service.go#L18-L29
```

## Gas Service

The gas service encapsulates both gas configuration and a gas meter. Gas consumption is largely
handled at the framework level for transaction processing and state access but modules can
choose to use the gas service directly if needed.

```go reference
https://github.com/cosmos/cosmos-sdk/blob/core/v1.0.0-alpha.4/core/gas/service.go#L26-L54
```

## Header Service

The header service provides access to the current block header. This is useful for modules that
need to access the block header fields like `Time` and `Height` during transaction processing.

```go reference
https://github.com/cosmos/cosmos-sdk/blob/a3729c1ad6ba2fb46f879ec7ea67c3afc02e9859/core/header/service.go#L11-L23
```

### Custom Header Service

Core's service oriented architecture (SOA) allows for chain developers to define a custom
implementation of the `HeaderService` interface. This would involve creating a new struct that
satisfies `HeaderService` but composes additional logic on top. An example of where this would
happen (when using depinject is shown below). Note this example is taken from `runtime/v2` but
could easily be adapted to `runtime/v1` (the default runtime 0.52). This same pattern can be
replicated for any core service.

```go reference
https://github.com/cosmos/cosmos-sdk/blob/489aaae40234f1015a7bbcfa9384a89dc8de8153/runtime/v2/module.go#L262-L288
```

These bindings are applied to the `depinject` container in simapp/v2 as shown below.

```go reference
https://github.com/cosmos/cosmos-sdk/blob/489aaae40234f1015a7bbcfa9384a89dc8de8153/simapp/v2/app_di.go#L72-L74
```

## Query and Message Router Service

Both the query and message router services are implementation of the same interface, `router.Service`.

```go reference
https://github.com/cosmos/cosmos-sdk/blob/core/v1.0.0-alpha.4/core/router/service.go#L11-L16
```

Both are exposed to modules so that arbitrary messages and queries can be routed to the
appropriate handler. This powerful abstraction allows module developers to fully decouple
modules from each other by using only the proto message for dispatching. This is particularly
useful for modules like `x/accounts` which require a dynamic dispatch mechanism in order to
function.

## TransactionService

```go reference
https://github.com/cosmos/cosmos-sdk/blob/core/v1.0.0-alpha.4/core/transaction/service.go#L21-L25
```

The transaction service provides access to the execution mode a state machine transaction is
running in, which may be one of `Check`, `Recheck`, `Simulate` or `Finalize`. The SDK primarily
uses these flags in ante handlers to skip certain checks while in `Check` or `Simulate` modes,
but module developers may find uses for them as well.

## KVStore Service

```go reference
https://github.com/cosmos/cosmos-sdk/blob/core/v1.0.0-alpha.4/core/store/service.go#L5-L11
```

The KVStore service abstracts access to, and creation of, key-value stores. Most use cases will
be backed by a merkle-tree store, but developers can provide their own implementations if
needed. In the case of the `KVStoreService` implementation provided in `Environment`, module
developers should understand that calling `OpenKVStore` will return a store already scoped to
the module's prefix. The wiring for this scoping is specified in `runtime`.

0 comments on commit e442264

Please sign in to comment.