feat: add valset change and block info service to runtime (#15811)
Co-authored-by: Aaron Craelius <aaron@regen.network>
This commit is contained in:
parent
70436fa5ec
commit
f82cbbbeeb
@ -14,33 +14,36 @@ ACCEPTED Partially Implemented
|
||||
|
||||
A new core API is proposed as a way to develop cosmos-sdk applications that will eventually replace the existing
|
||||
`AppModule` and `sdk.Context` frameworks a set of core services and extension interfaces. This core API aims to:
|
||||
- be simpler,
|
||||
- more extensible,
|
||||
- more stable than the current framework,
|
||||
- enable deterministic events and queries,
|
||||
- support event listeners and
|
||||
- [ADR 033: Protobuf-based Inter-Module Communication](./adr-033-protobuf-inter-module-comm.md) clients.
|
||||
|
||||
* be simpler
|
||||
* more extensible
|
||||
* more stable than the current framework
|
||||
* enable deterministic events and queries,
|
||||
* support event listeners
|
||||
* [ADR 033: Protobuf-based Inter-Module Communication](./adr-033-protobuf-inter-module-comm.md) clients.
|
||||
|
||||
## Context
|
||||
|
||||
Historically modules have exposed their functionality to the framework via the `AppModule` and `AppModuleBasic`
|
||||
interfaces which have the following shortcomings:
|
||||
|
||||
* both `AppModule` and `AppModuleBasic` need to be defined and registered which is counter-intuitive
|
||||
* apps need to implement the full interfaces, even parts they don't need (although there are workarounds for this),
|
||||
* interface methods depend heavily on unstable third party dependencies, in particular Tendermint,
|
||||
* interface methods depend heavily on unstable third party dependencies, in particular Comet,
|
||||
* legacy required methods have littered these interfaces for far too long
|
||||
|
||||
In order to interact with the state machine, modules have needed to do a combination of these things:
|
||||
|
||||
* get store keys from the app
|
||||
* call methods on `sdk.Context` which contains more or less the full set of capability available to modules.
|
||||
|
||||
By isolating all the state machine functionality into `sdk.Context`, the set of functionalities available to
|
||||
modules are tightly coupled to this type. If there are changes to upstream dependencies (such as Tendermint)
|
||||
modules are tightly coupled to this type. If there are changes to upstream dependencies (such as Comet)
|
||||
or new functionalities are desired (such as alternate store types), the changes need impact `sdk.Context` and all
|
||||
consumers of it (basically all modules). Also, all modules now receive `context.Context` and need to convert these
|
||||
to `sdk.Context`'s with a non-ergonomic unwrapping function.
|
||||
|
||||
Any breaking changes to these interfaces, such as ones imposed by third-party dependencies like Tendermint, have the
|
||||
Any breaking changes to these interfaces, such as ones imposed by third-party dependencies like Comet, have the
|
||||
side effect of forcing all modules in the ecosystem to update in lock-step. This means it is almost impossible to have
|
||||
a version of the module which can be run with 2 or 3 different versions of the SDK or 2 or 3 different versions of
|
||||
another module. This lock-step coupling slows down overall development within the ecosystem and causes updates to
|
||||
@ -50,11 +53,13 @@ components to be delayed longer than they would if things were more stable and l
|
||||
|
||||
The `core` API proposes a set of core APIs that modules can rely on to interact with the state machine and expose their
|
||||
functionalities to it that are designed in a principled way such that:
|
||||
|
||||
* tight coupling of dependencies and unrelated functionalities is minimized or eliminated
|
||||
* APIs can have long-term stability guarantees
|
||||
* the SDK framework is extensible in a safe and straightforward way
|
||||
|
||||
The design principles of the core API are as follows:
|
||||
|
||||
* everything that a module wants to interact with in the state machine is a service
|
||||
* all services coordinate state via `context.Context` and don't try to recreate the "bag of variables" approach of `sdk.Context`
|
||||
* all independent services are isolated in independent packages with minimal APIs and minimal dependencies
|
||||
@ -63,7 +68,7 @@ The design principles of the core API are as follows:
|
||||
functionalities exposed by core extension interfaces
|
||||
* other non-core and/or non-LTS services can be exposed by specific versions of runtime modules or other modules
|
||||
following the same design principles, this includes functionality that interacts with specific non-stable versions of
|
||||
third party dependencies such as Tendermint
|
||||
third party dependencies such as Comet
|
||||
* the core API doesn't implement *any* functionality, it just defines types
|
||||
* go stable API compatibility guidelines are followed: https://go.dev/blog/module-compatibility
|
||||
|
||||
@ -74,7 +79,7 @@ SDK's current tightly coupled `BaseApp` design while still allowing for a high d
|
||||
compatibility.
|
||||
|
||||
Modules which are built only against the core API don't need to know anything about which version of runtime,
|
||||
`BaseApp` or Tendermint in order to be compatible. Modules from the core mainline SDK could be easily composed
|
||||
`BaseApp` or Comet in order to be compatible. Modules from the core mainline SDK could be easily composed
|
||||
with a forked version of runtime with this pattern.
|
||||
|
||||
This design is intended to enable matrices of compatible dependency versions. Ideally a given version of any module
|
||||
@ -112,6 +117,7 @@ type TransientStoreService interface {
|
||||
```
|
||||
|
||||
Modules can use these services like this:
|
||||
|
||||
```go
|
||||
func (k msgServer) Send(ctx context.Context, msg *types.MsgSend) (*types.MsgSendResponse, error) {
|
||||
store := k.kvStoreSvc.OpenKVStore(ctx)
|
||||
@ -166,6 +172,7 @@ by other modules. If there is a client-side need to add events in patch releases
|
||||
Modules will provide their core services to the runtime module via extension interfaces built on top of the
|
||||
`cosmossdk.io/core/appmodule.AppModule` tag interface. This tag interface requires only two empty methods which
|
||||
allow `depinject` to identify implementors as `depinject.OnePerModule` types and as app module implementations:
|
||||
|
||||
```go
|
||||
type AppModule interface {
|
||||
depinject.OnePerModuleType
|
||||
@ -226,6 +233,7 @@ streaming genesis and modules using these frameworks generally do not need to wr
|
||||
genesis code.
|
||||
|
||||
To support genesis, modules should implement the `HasGenesis` extension interface:
|
||||
|
||||
```go
|
||||
type HasGenesis interface {
|
||||
AppModule
|
||||
@ -248,6 +256,7 @@ type HasGenesis interface {
|
||||
|
||||
Modules that have functionality that runs before transactions (begin blockers) or after transactions
|
||||
(end blockers) should implement the has `HasBeginBlocker` and/or `HasEndBlocker` interfaces:
|
||||
|
||||
```go
|
||||
type HasBeginBlocker interface {
|
||||
AppModule
|
||||
@ -261,25 +270,34 @@ type HasEndBlocker interface {
|
||||
```
|
||||
|
||||
The `BeginBlock` and `EndBlock` methods will take a `context.Context`, because:
|
||||
* most modules don't need Tendermint information other than `BlockInfo` so we can eliminate dependencies on specific
|
||||
Tendermint versions
|
||||
* for the few modules that need Tendermint block headers and/or return validator updates, specific versions of the
|
||||
runtime module will provide specific functionality for interacting with the specific version(s) of Tendermint
|
||||
|
||||
* most modules don't need Comet information other than `BlockInfo` so we can eliminate dependencies on specific
|
||||
Comet versions
|
||||
* for the few modules that need Comet block headers and/or return validator updates, specific versions of the
|
||||
runtime module will provide specific functionality for interacting with the specific version(s) of Comet
|
||||
supported
|
||||
|
||||
In order for `BeginBlock`, `EndBlock` and `InitGenesis` to send back validator updates and retrieve full Tendermint
|
||||
block headers, the runtime module for a specific version of Tendermint could provide services like this:
|
||||
In order for `BeginBlock`, `EndBlock` and `InitGenesis` to send back validator updates and retrieve full Comet
|
||||
block headers, the runtime module for a specific version of Comet could provide services like this:
|
||||
|
||||
```go
|
||||
type ValidatorUpdateService interface {
|
||||
SetValidatorUpdates(context.Context, []abci.ValidatorUpdate)
|
||||
}
|
||||
|
||||
type BeginBlockService interface {
|
||||
GetBeginBlockRequest(context.Context) abci.RequestBeginBlock
|
||||
type BlockInfoService interface {
|
||||
GetHeight() int64 // GetHeight returns the height of the block
|
||||
Misbehavior() []abci.Misbehavior // Misbehavior returns the misbehavior of the block
|
||||
GetHeaderHash() []byte // GetHeaderHash returns the hash of the block header
|
||||
// GetValidatorsHash returns the hash of the validators
|
||||
// For Comet, it is the hash of the next validators
|
||||
GetValidatorsHash() []byte
|
||||
GetProposerAddress() []byte // GetProposerAddress returns the address of the block proposer
|
||||
GetDecidedLastCommit() abci.CommitInfo // GetDecidedLastCommit returns the last commit info
|
||||
}
|
||||
```
|
||||
|
||||
We know these types will change at the Tendermint level and that also a very limited set of modules actually need this
|
||||
We know these types will change at the Comet level and that also a very limited set of modules actually need this
|
||||
functionality, so they are intentionally kept out of core to keep core limited to the necessary, minimal set of stable
|
||||
APIs.
|
||||
|
||||
@ -287,6 +305,7 @@ APIs.
|
||||
|
||||
The current `AppModule` framework handles a number of additional concerns which aren't addressed by this core API.
|
||||
These include:
|
||||
|
||||
* gas
|
||||
* block headers
|
||||
* upgrades
|
||||
@ -307,6 +326,7 @@ gRPC gateway registration should probably be handled by the runtime module, but
|
||||
gateway types as 1) we are already using an older version and 2) it's possible the framework can do this registration
|
||||
automatically in the future. So for now, the runtime module should probably provide some sort of specific type for doing
|
||||
this registration ex:
|
||||
|
||||
```go
|
||||
type GrpcGatewayInfo struct {
|
||||
Handlers []GrpcGatewayHandler
|
||||
@ -316,6 +336,7 @@ type GrpcGatewayHandler func(ctx context.Context, mux *runtime.ServeMux, client
|
||||
```
|
||||
|
||||
which modules can return in a provider:
|
||||
|
||||
```go
|
||||
func ProvideGrpcGateway() GrpcGatewayInfo {
|
||||
return GrpcGatewayinfo {
|
||||
@ -373,7 +394,7 @@ allow tests to observe service behavior or provide a non-production implementati
|
||||
stores can be used to mock stores.
|
||||
|
||||
For integration testing, a mock runtime implementation should be provided that allows composing different app modules
|
||||
together for testing without a dependency on runtime or Tendermint.
|
||||
together for testing without a dependency on runtime or Comet.
|
||||
|
||||
## Consequences
|
||||
|
||||
@ -381,7 +402,7 @@ together for testing without a dependency on runtime or Tendermint.
|
||||
|
||||
Early versions of runtime modules should aim to support as much as possible modules built with the existing
|
||||
`AppModule`/`sdk.Context` framework. As the core API is more widely adopted, later runtime versions may choose to
|
||||
drop support and only support the core API plus any runtime module specific APIs (like specific versions of Tendermint).
|
||||
drop support and only support the core API plus any runtime module specific APIs (like specific versions of Comet).
|
||||
|
||||
The core module itself should strive to remain at the go semantic version `v1` as long as possible and follow design
|
||||
principles that allow for strong long-term support (LTS).
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1"
|
||||
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
|
||||
reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1"
|
||||
abci "github.com/cometbft/cometbft/abci/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/runtime/services"
|
||||
"github.com/cosmos/cosmos-sdk/types/module"
|
||||
@ -21,3 +24,24 @@ func (a *App) registerRuntimeServices(cfg module.Configurator) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// ValidatorUpdateService & BlockInfoService
|
||||
// ======================================================
|
||||
|
||||
// ValidatorUpdateService is the service that runtime will provide to the module that sets validator updates.
|
||||
type ValidatorUpdateService interface {
|
||||
SetValidatorUpdates(context.Context, []abci.ValidatorUpdate)
|
||||
}
|
||||
|
||||
// BlockInfoService is the service that runtime will provide to modules which need Comet block information.
|
||||
type BlockInfoService interface {
|
||||
GetHeight() int64 // GetHeight returns the height of the block
|
||||
Misbehavior() []abci.Misbehavior // Misbehavior returns the misbehavior of the block
|
||||
GetHeaderHash() []byte // GetHeaderHash returns the hash of the block header
|
||||
// GetValidatorsHash returns the hash of the validators
|
||||
// For Comet, it is the hash of the next validators
|
||||
GetValidatorsHash() []byte
|
||||
GetProposerAddress() []byte // GetProposerAddress returns the address of the block proposer
|
||||
GetDecidedLastCommit() abci.CommitInfo // GetDecidedLastCommit returns the last commit info
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user