Skip to content

Commit

Permalink
refactor(simapp,runtime): audit changes (#21310)
Browse files Browse the repository at this point in the history
(cherry picked from commit 03d3a93)

# Conflicts:
#	runtime/v2/builder.go
#	runtime/v2/module.go
  • Loading branch information
julienrbrt authored and mergify[bot] committed Aug 27, 2024
1 parent 316c67a commit f46b863
Show file tree
Hide file tree
Showing 17 changed files with 656 additions and 104 deletions.
20 changes: 18 additions & 2 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,22 @@ need to remove them both from your app.go code, they will yield to unresolvable

The Cosmos SDK now supports unordered transactions. This means that transactions
can be executed in any order and doesn't require the client to deal with or manage
nonces. This also means the order of execution is not guaranteed. To enable unordered
transactions in your application:
nonces. This also means the order of execution is not guaranteed.

Unordered transactions are automatically enabled when using `depinject` / app di, simply supply the `servertypes.AppOptions` in `app.go`:

```diff
depinject.Supply(
+ // supply the application options
+ appOpts,
// supply the logger
logger,
)
```

<details>
<summary>Step-by-step Wiring </summary>
If you are still using the legacy wiring, you must enable unordered transactions manually:

* Update the `App` constructor to create, load, and save the unordered transaction
manager.
Expand Down Expand Up @@ -143,6 +157,8 @@ transactions in your application:
}
```

</details>

To submit an unordered transaction, the client must set the `unordered` flag to
`true` and ensure a reasonable `timeout_height` is set. The `timeout_height` is
used as a TTL for the transaction and is used to provide replay protection. See
Expand Down
47 changes: 37 additions & 10 deletions runtime/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"cosmossdk.io/core/legacy"
"cosmossdk.io/log"
storetypes "cosmossdk.io/store/types"
"cosmossdk.io/x/auth/ante/unorderedtx"
authtx "cosmossdk.io/x/auth/tx"

"github.com/cosmos/cosmos-sdk/baseapp"
Expand Down Expand Up @@ -41,7 +42,9 @@ import (
type App struct {
*baseapp.BaseApp

ModuleManager *module.Manager
ModuleManager *module.Manager
UnorderedTxManager *unorderedtx.Manager

configurator module.Configurator // nolint:staticcheck // SA1019: Configurator is deprecated but still used in runtime v1.
config *runtimev1alpha1.Module
storeKeys []storetypes.StoreKey
Expand Down Expand Up @@ -154,6 +157,20 @@ func (a *App) Load(loadLatest bool) error {
return nil
}

// Close closes all necessary application resources.
// It implements servertypes.Application.
func (a *App) Close() error {
// the unordered tx manager could be nil (unlikely but possible)
// if the app has no app options supplied.
if a.UnorderedTxManager != nil {
if err := a.UnorderedTxManager.Close(); err != nil {
return err
}
}

return a.BaseApp.Close()
}

// PreBlocker application updates every pre block
func (a *App) PreBlocker(ctx sdk.Context, _ *abci.FinalizeBlockRequest) error {
return a.ModuleManager.PreBlock(ctx)
Expand All @@ -171,16 +188,14 @@ func (a *App) EndBlocker(ctx sdk.Context) (sdk.EndBlock, error) {

// Precommiter application updates every commit
func (a *App) Precommiter(ctx sdk.Context) {
err := a.ModuleManager.Precommit(ctx)
if err != nil {
if err := a.ModuleManager.Precommit(ctx); err != nil {
panic(err)
}
}

// PrepareCheckStater application updates every commit
func (a *App) PrepareCheckStater(ctx sdk.Context) {
err := a.ModuleManager.PrepareCheckState(ctx)
if err != nil {
if err := a.ModuleManager.PrepareCheckState(ctx); err != nil {
panic(err)
}
}
Expand Down Expand Up @@ -247,18 +262,30 @@ func (a *App) DefaultGenesis() map[string]json.RawMessage {
return a.ModuleManager.DefaultGenesis()
}

// GetStoreKeys returns all the stored store keys.
func (a *App) GetStoreKeys() []storetypes.StoreKey {
return a.storeKeys
}

// SetInitChainer sets the init chainer function
// It wraps `BaseApp.SetInitChainer` to allow setting a custom init chainer from an app.
func (a *App) SetInitChainer(initChainer sdk.InitChainer) {
a.initChainer = initChainer
a.BaseApp.SetInitChainer(initChainer)
}

// GetStoreKeys returns all the stored store keys.
func (a *App) GetStoreKeys() []storetypes.StoreKey {
return a.storeKeys
}

// GetKey returns the KVStoreKey for the provided store key.
//
// NOTE: This should only be used in testing.
func (a *App) GetKey(storeKey string) *storetypes.KVStoreKey {
sk := a.UnsafeFindStoreKey(storeKey)
kvStoreKey, ok := sk.(*storetypes.KVStoreKey)
if !ok {
return nil
}
return kvStoreKey
}

// UnsafeFindStoreKey fetches a registered StoreKey from the App in linear time.
//
// NOTE: This should only be used in testing.
Expand Down
64 changes: 64 additions & 0 deletions runtime/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,19 @@ package runtime

import (
"encoding/json"
"fmt"
"io"
"path/filepath"

dbm "github.com/cosmos/cosmos-db"
"github.com/spf13/cast"

storetypes "cosmossdk.io/store/types"
"cosmossdk.io/x/auth/ante/unorderedtx"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client/flags"
servertypes "github.com/cosmos/cosmos-sdk/server/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/version"
)
Expand All @@ -16,6 +24,8 @@ import (
// the existing app.go initialization conventions.
type AppBuilder struct {
app *App

appOptions servertypes.AppOptions
}

// DefaultGenesis returns a default genesis from the registered modules.
Expand All @@ -40,9 +50,63 @@ func (a *AppBuilder) Build(db dbm.DB, traceStore io.Writer, baseAppOptions ...fu
a.app.BaseApp = bApp
a.app.configurator = module.NewConfigurator(a.app.cdc, a.app.MsgServiceRouter(), a.app.GRPCQueryRouter())

if a.appOptions != nil {
// register unordered tx manager
if err := a.registerUnorderedTxManager(); err != nil {
panic(err)
}

// register indexer if enabled
if err := a.registerIndexer(); err != nil {
panic(err)
}
}

// register services
if err := a.app.ModuleManager.RegisterServices(a.app.configurator); err != nil {
panic(err)
}

return a.app
}

// register unordered tx manager
func (a *AppBuilder) registerUnorderedTxManager() error {
// create, start, and load the unordered tx manager
utxDataDir := filepath.Join(cast.ToString(a.appOptions.Get(flags.FlagHome)), "data")
a.app.UnorderedTxManager = unorderedtx.NewManager(utxDataDir)
a.app.UnorderedTxManager.Start()

if err := a.app.UnorderedTxManager.OnInit(); err != nil {
return fmt.Errorf("failed to initialize unordered tx manager: %w", err)
}

return nil
}

// register indexer
func (a *AppBuilder) registerIndexer() error {
// if we have indexer options in app.toml, then enable the built-in indexer framework
if indexerOpts := a.appOptions.Get("indexer"); indexerOpts != nil {
moduleSet := map[string]any{}
for modName, mod := range a.app.ModuleManager.Modules {
moduleSet[modName] = mod
}

return a.app.EnableIndexer(indexerOpts, a.kvStoreKeys(), moduleSet)
}

// register legacy streaming services if we don't have the built-in indexer enabled
return a.app.RegisterStreamingServices(a.appOptions, a.kvStoreKeys())
}

func (a *AppBuilder) kvStoreKeys() map[string]*storetypes.KVStoreKey {
keys := make(map[string]*storetypes.KVStoreKey)
for _, k := range a.app.GetStoreKeys() {
if kv, ok := k.(*storetypes.KVStoreKey); ok {
keys[kv.Name()] = kv
}
}

return keys
}
11 changes: 8 additions & 3 deletions runtime/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
servertypes "github.com/cosmos/cosmos-sdk/server/types"
"github.com/cosmos/cosmos-sdk/std"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/types/msgservice"
Expand Down Expand Up @@ -120,7 +121,6 @@ func ProvideApp(
appmodule.AppModule,
protodesc.Resolver,
protoregistry.MessageTypeResolver,
error,
) {
protoFiles := proto.HybridResolver
protoTypes := protoregistry.GlobalTypes
Expand All @@ -145,9 +145,9 @@ func ProvideApp(
msgServiceRouter: msgServiceRouter,
grpcQueryRouter: grpcQueryRouter,
}
appBuilder := &AppBuilder{app}
appBuilder := &AppBuilder{app: app}

return appBuilder, msgServiceRouter, grpcQueryRouter, appModule{app}, protoFiles, protoTypes, nil
return appBuilder, msgServiceRouter, grpcQueryRouter, appModule{app}, protoFiles, protoTypes
}

type AppInputs struct {
Expand All @@ -160,6 +160,7 @@ type AppInputs struct {
BaseAppOptions []BaseAppOption
InterfaceRegistry codectypes.InterfaceRegistry
LegacyAmino legacy.Amino
AppOptions servertypes.AppOptions `optional:"true"` // can be nil in client wiring
}

func SetupAppBuilder(inputs AppInputs) {
Expand All @@ -170,6 +171,10 @@ func SetupAppBuilder(inputs AppInputs) {
app.ModuleManager = inputs.ModuleManager
app.ModuleManager.RegisterInterfaces(inputs.InterfaceRegistry)
app.ModuleManager.RegisterLegacyAminoCodec(inputs.LegacyAmino)

if inputs.AppOptions != nil {
inputs.AppBuilder.appOptions = inputs.AppOptions
}
}

func registerStoreKey(wrapper *AppBuilder, key storetypes.StoreKey) {
Expand Down
Loading

0 comments on commit f46b863

Please sign in to comment.