[wip] refactor laconic app cli

- use runtime, server v2

- move generic tx decoder to utils
This commit is contained in:
Roy Crihfield 2024-10-17 16:39:44 +08:00
parent c7e0753f08
commit dd39fb2365
10 changed files with 435 additions and 398 deletions

View File

@ -2,35 +2,36 @@ package app
import (
_ "embed"
"io"
"os"
"path/filepath"
corestore "cosmossdk.io/core/store"
"github.com/spf13/viper"
"cosmossdk.io/core/transaction"
"cosmossdk.io/depinject"
"cosmossdk.io/depinject/appconfig"
"cosmossdk.io/log"
storetypes "cosmossdk.io/store/types"
"cosmossdk.io/runtime/v2"
server "cosmossdk.io/server/v2"
serverstore "cosmossdk.io/server/v2/store"
"cosmossdk.io/store/v2"
"cosmossdk.io/store/v2/root"
// crypto/keys/ed25519/keys.pb.go:123
coreserver "cosmossdk.io/core/server"
bankkeeper "cosmossdk.io/x/bank/keeper"
consensuskeeper "cosmossdk.io/x/consensus/keeper"
distrkeeper "cosmossdk.io/x/distribution/keeper"
govkeeper "cosmossdk.io/x/gov/keeper"
slashingkeeper "cosmossdk.io/x/slashing/keeper"
stakingkeeper "cosmossdk.io/x/staking/keeper"
"git.vdb.to/cerc-io/laconicd/utils"
auctionkeeper "git.vdb.to/cerc-io/laconicd/x/auction/keeper"
bondkeeper "git.vdb.to/cerc-io/laconicd/x/bond/keeper"
onboardingkeeper "git.vdb.to/cerc-io/laconicd/x/onboarding/keeper"
registrykeeper "git.vdb.to/cerc-io/laconicd/x/registry/keeper"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/server/api"
"github.com/cosmos/cosmos-sdk/server/config"
servertypes "github.com/cosmos/cosmos-sdk/server/types"
"github.com/cosmos/cosmos-sdk/types/module"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
@ -48,6 +49,7 @@ import (
_ "git.vdb.to/cerc-io/laconicd/x/bond/module" // import for side-effects
_ "git.vdb.to/cerc-io/laconicd/x/onboarding/module" // import for side-effects
_ "git.vdb.to/cerc-io/laconicd/x/registry/module" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/crypto/codec" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/auth" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/auth/tx/config" // import for side-effects
)
@ -59,19 +61,19 @@ var DefaultNodeHome string
var AppConfigYAML []byte
var (
_ runtime.AppI = (*LaconicApp)(nil)
_ servertypes.Application = (*LaconicApp)(nil)
_ server.AppI[transaction.Tx] = (*LaconicApp)(nil)
)
// LaconicApp extends an ABCI application, but with most of its parameters exported.
// They are exported for convenience in creating helper functions, as object
// capabilities aren't needed for testing.
type LaconicApp struct {
*runtime.App
*runtime.App[transaction.Tx]
// legacyAmino *codec.LegacyAmino
appCodec codec.Codec
txConfig client.TxConfig
interfaceRegistry codectypes.InterfaceRegistry
store store.RootStore
// keepers
AccountKeeper authkeeper.AccountKeeper
@ -92,6 +94,11 @@ type LaconicApp struct {
sm *module.SimulationManager
}
type (
AppTx = transaction.Tx
AppBuilder = runtime.AppBuilder[AppTx]
)
func init() {
userHomeDir, err := os.UserHomeDir()
if err != nil {
@ -105,32 +112,53 @@ func init() {
func AppConfig() depinject.Config {
return depinject.Configs(
appconfig.LoadYAML(AppConfigYAML),
runtime.DefaultServiceBindings(),
depinject.Supply(
utils.NewAddressCodec,
utils.NewValAddressCodec,
utils.NewConsAddressCodec,
),
depinject.Provide(
codec.ProvideInterfaceRegistry,
codec.ProvideAddressCodec,
codec.ProvideProtoCodec,
codec.ProvideLegacyAmino,
),
)
}
// NewLaconicApp returns a reference to an initialized LaconicApp.
func NewLaconicApp(
logger log.Logger,
db corestore.KVStoreWithBatch,
traceStore io.Writer,
// db corestore.KVStoreWithBatch,
// traceStore io.Writer,
viper *viper.Viper,
loadLatest bool,
appOpts servertypes.AppOptions,
baseAppOptions ...func(*baseapp.BaseApp),
// appOpts server.AppOptions, // TODO migrate?
appBuilderOpts ...runtime.AppBuilderOption[AppTx],
) (*LaconicApp, error) {
var (
app = &LaconicApp{}
appBuilder *runtime.AppBuilder
app LaconicApp
appBuilder *AppBuilder
storeBuilder root.Builder
err error
)
if err := depinject.Inject(
logfile := "/Users/roy/vulcanize/dump/laconic-debug/app_depinject.log"
if err := depinject.InjectDebug(
// depinject.Debug(),
depinject.DebugOptions(
depinject.FileLogger(logfile),
),
depinject.Configs(
AppConfig(),
depinject.Supply(
logger,
appOpts,
// appOpts,
),
),
&appBuilder,
&storeBuilder,
&app.appCodec,
// &app.legacyAmino,
&app.txConfig,
@ -150,27 +178,44 @@ func NewLaconicApp(
return nil, err
}
app.App = appBuilder.Build(db, traceStore, baseAppOptions...)
// store/v2 follows a slightly more eager config life cycle than server components
storeConfig, err := serverstore.UnmarshalConfig(viper.AllSettings())
if err != nil {
panic(err)
}
app.store, err = storeBuilder.Build(logger, storeConfig)
if err != nil {
panic(err)
}
// register streaming services
if err := app.RegisterStreamingServices(appOpts, app.kvStoreKeys()); err != nil {
// TODO: db config correct?
// TODO: store tracer injection?
if app.App, err = appBuilder.Build(appBuilderOpts...); err != nil {
return nil, err
}
// TODO
// // register streaming services
// if err := app.RegisterStreamingServices(appOpts, app.kvStoreKeys()); err != nil {
// return nil, err
// }
/**** Module Options ****/
// create the simulation manager and define the order of the modules for deterministic simulations
// NOTE: this is not required apps that don't use the simulator for fuzz testing transactions
// NOTE: this is not required for apps that don't use the simulator for fuzz testing transactions
app.sm = module.NewSimulationManagerFromAppModules(
app.ModuleManager.Modules,
app.ModuleManager().Modules(),
make(map[string]module.AppModuleSimulation, 0))
app.sm.RegisterStoreDecoders()
if err := app.Load(loadLatest); err != nil {
return nil, err
// TODO: remove
if loadLatest {
if err = app.LoadLatest(); err != nil {
return nil, err
}
}
return app, nil
return &app, nil
}
// // LegacyAmino returns LaconicApp's amino codec.
@ -178,38 +223,27 @@ func NewLaconicApp(
// return app.legacyAmino
// }
func (app *LaconicApp) InterfaceRegistry() coreserver.InterfaceRegistry {
return app.interfaceRegistry
}
// GetKey returns the KVStoreKey for the provided store key.
func (app *LaconicApp) GetKey(storeKey string) *storetypes.KVStoreKey {
sk := app.UnsafeFindStoreKey(storeKey)
kvStoreKey, ok := sk.(*storetypes.KVStoreKey)
if !ok {
return nil
}
return kvStoreKey
func (app *LaconicApp) GetStore() store.RootStore {
return app.store
}
func (app *LaconicApp) kvStoreKeys() map[string]*storetypes.KVStoreKey {
keys := make(map[string]*storetypes.KVStoreKey)
for _, k := range app.GetStoreKeys() {
if kv, ok := k.(*storetypes.KVStoreKey); ok {
keys[kv.Name()] = kv
}
}
// func (app *LaconicApp) kvStoreKeys() map[string]*store.KVStoreKey {
// keys := make(map[string]*store.KVStoreKey)
// for _, k := range app.GetStoreKeys() {
// if kv, ok := k.(*storetypes.KVStoreKey); ok {
// keys[kv.Name()] = kv
// }
// }
return keys
}
// return keys
// }
// SimulationManager implements the SimulationApp interface
func (app *LaconicApp) SimulationManager() *module.SimulationManager {
return app.sm
}
// RegisterAPIRoutes registers all application module routes with the provided
// API server.
func (app *LaconicApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) {
app.App.RegisterAPIRoutes(apiSvr, apiConfig)
// register swagger API in app.go so that other applications can override easily
if err := server.RegisterSwaggerAPI(apiSvr.ClientCtx, apiSvr.Router, apiConfig.Swagger); err != nil {
panic(err)
}
}

View File

@ -25,6 +25,10 @@ modules:
- bond
- registry
- onboarding
gas_config:
validate_tx_gas_limit: 100000,
query_gas_limit: 100000,
simulation_gas_limit: 100000,
override_store_keys:
- module_name: auth
kv_store_key: acc

View File

@ -1,264 +1,48 @@
package app
import (
"encoding/json"
"errors"
"fmt"
"log"
"context"
"cosmossdk.io/collections"
storetypes "cosmossdk.io/store/types"
slashingtypes "cosmossdk.io/x/slashing/types"
"cosmossdk.io/runtime/v2/services"
"cosmossdk.io/x/staking"
stakingtypes "cosmossdk.io/x/staking/types"
tmproto "github.com/cometbft/cometbft/api/cometbft/types/v1"
cmttypes "github.com/cometbft/cometbft/types"
servertypes "github.com/cosmos/cosmos-sdk/server/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"git.vdb.to/cerc-io/laconicd/utils"
v2 "github.com/cosmos/cosmos-sdk/x/genutil/v2"
)
// ExportAppStateAndValidators exports the state of the application for a genesis file.
// ExportAppStateAndValidators exports the state of the application for a genesis
// file.
// This is a demonstation of how to export a genesis file. Export may need extended at
// the user discretion for cleaning the genesis state at the end provided with jailAllowedAddrs
func (app *LaconicApp) ExportAppStateAndValidators(
forZeroHeight bool,
jailAllowedAddrs []string,
modulesToExport []string,
) (servertypes.ExportedApp, error) {
// as if they could withdraw from the start of the next block
ctx := app.NewContextLegacy(true, tmproto.Header{Height: app.LastBlockHeight()})
) (v2.ExportedApp, error) {
ctx := context.Background()
var exportedApp v2.ExportedApp
// We export at last height + 1, because that's the height at which
// CometBFT will start InitChain.
height := app.LastBlockHeight() + 1
if forZeroHeight {
height = 0
app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs)
}
genState, err := app.ModuleManager.ExportGenesis(ctx)
latestHeight, err := app.LoadLatestHeight()
if err != nil {
return servertypes.ExportedApp{}, fmt.Errorf("failed to export genesis state: %w", err)
return exportedApp, err
}
appState, err := json.MarshalIndent(genState, "", " ")
genesis, err := app.ExportGenesis(ctx, latestHeight)
if err != nil {
return servertypes.ExportedApp{}, err
return exportedApp, err
}
validators, err := staking.WriteValidators(ctx, app.StakingKeeper)
readerMap, err := app.GetStore().StateAt(latestHeight)
if err != nil {
return servertypes.ExportedApp{}, err
return exportedApp, err
}
genesisCtx := services.NewGenesisContext(readerMap)
err = genesisCtx.Read(ctx, func(ctx context.Context) error {
exportedApp.Validators, err = staking.WriteValidators(ctx, app.StakingKeeper)
return err
})
if err != nil {
return exportedApp, err
}
cmtvalidators := make([]cmttypes.GenesisValidator, len(validators))
for i := range validators {
cmtvalidators[i] = cmttypes.GenesisValidator{
Address: validators[i].Address.Bytes(),
PubKey: validators[i].PubKey,
Power: validators[i].Power,
Name: validators[i].Name,
}
}
return servertypes.ExportedApp{
AppState: appState,
Validators: cmtvalidators,
Height: height,
ConsensusParams: app.BaseApp.GetConsensusParams(ctx),
}, err
}
// prepare for fresh start at zero height
// NOTE zero height genesis is a temporary feature, which will be deprecated in favor of export at a block height
func (app *LaconicApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) {
applyAllowedAddrs := false
// check if there is a allowed address list
if len(jailAllowedAddrs) > 0 {
applyAllowedAddrs = true
}
addrCodec := utils.NewAddressCodec()
valAddrCodec := utils.NewValAddressCodec()
allowedAddrsMap := make(map[string]bool)
for _, addr := range jailAllowedAddrs {
_, err := valAddrCodec.StringToBytes(addr)
if err != nil {
log.Fatal(err)
}
allowedAddrsMap[addr] = true
}
/* Handle fee distribution state. */
// withdraw all validator commission
_ = app.StakingKeeper.IterateValidators(ctx, func(_ int64, val sdk.ValidatorI) (stop bool) {
valBz, err := app.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator())
if err != nil {
panic(err)
}
_, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, valBz)
return false
})
// withdraw all delegator rewards
dels, err := app.StakingKeeper.GetAllDelegations(ctx)
if err != nil {
panic(err)
}
for _, delegation := range dels {
valAddr, err := valAddrCodec.StringToBytes(delegation.ValidatorAddress)
if err != nil {
panic(err)
}
delAddr, err := addrCodec.StringToBytes(delegation.DelegatorAddress)
if err != nil {
panic(err)
}
_, _ = app.DistrKeeper.WithdrawDelegationRewards(ctx, delAddr, valAddr)
}
// clear validator slash events
app.DistrKeeper.ValidatorSlashEvents.Clear(ctx, nil)
// clear validator historical rewards
app.DistrKeeper.ValidatorHistoricalRewards.Clear(ctx, nil)
// set context height to zero
height := ctx.BlockHeight()
ctx = ctx.WithBlockHeight(0)
// reinitialize all validators
_ = app.StakingKeeper.IterateValidators(ctx, func(_ int64, val sdk.ValidatorI) (stop bool) {
valBz, err := app.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator())
if err != nil {
panic(err)
}
// donate any unwithdrawn outstanding reward fraction tokens to the community pool
scraps, err := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, valBz)
if err != nil {
panic(err)
}
feePool, err := app.DistrKeeper.FeePool.Get(ctx)
if err != nil {
panic(err)
}
feePool.CommunityPool = feePool.CommunityPool.Add(scraps...)
if err := app.DistrKeeper.FeePool.Set(ctx, feePool); err != nil {
panic(err)
}
if err := app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, valBz); err != nil {
panic(err)
}
return false
})
// reinitialize all delegations
for _, del := range dels {
valAddr, err := valAddrCodec.StringToBytes(del.ValidatorAddress)
if err != nil {
panic(err)
}
delAddr, err := addrCodec.StringToBytes(del.DelegatorAddress)
if err != nil {
panic(err)
}
if err := app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr); err != nil {
// never called as BeforeDelegationCreated always returns nil
panic(fmt.Errorf("error while incrementing period: %w", err))
}
if err := app.DistrKeeper.Hooks().AfterDelegationModified(ctx, delAddr, valAddr); err != nil {
// never called as AfterDelegationModified always returns nil
panic(fmt.Errorf("error while creating a new delegation period record: %w", err))
}
}
// reset context height
ctx = ctx.WithBlockHeight(height)
/* Handle staking state. */
// iterate through redelegations, reset creation height
_ = app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) {
for i := range red.Entries {
red.Entries[i].CreationHeight = 0
}
_ = app.StakingKeeper.SetRedelegation(ctx, red)
return false
})
// iterate through unbonding delegations, reset creation height
_ = app.StakingKeeper.UnbondingDelegations.Walk(
ctx, nil,
func(_ collections.Pair[[]byte, []byte], ubd stakingtypes.UnbondingDelegation) (stop bool, err error) {
for i := range ubd.Entries {
ubd.Entries[i].CreationHeight = 0
}
_ = app.StakingKeeper.SetUnbondingDelegation(ctx, ubd)
return false, nil
})
// Iterate through validators by power descending, reset bond heights, and
// update bond intra-tx counters.
store := ctx.KVStore(app.GetKey(stakingtypes.StoreKey))
iter := storetypes.KVStoreReversePrefixIterator(store, stakingtypes.ValidatorsKey)
counter := int16(0)
for ; iter.Valid(); iter.Next() {
addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key()))
validator, err := app.StakingKeeper.GetValidator(ctx, addr)
if errors.Is(err, stakingtypes.ErrNoValidatorFound) {
panic("expected validator, not found")
} else if err != nil {
panic(err)
}
validator.UnbondingHeight = 0
if applyAllowedAddrs && !allowedAddrsMap[addr.String()] {
validator.Jailed = true
}
_ = app.StakingKeeper.SetValidator(ctx, validator)
counter++
}
if err := iter.Close(); err != nil {
app.Logger().Error("error while closing the key-value store reverse prefix iterator: ", err)
return
}
_, err = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx)
if err != nil {
log.Fatal(err)
}
/* Handle slashing state. */
// reset start height on signing infos
err = app.SlashingKeeper.ValidatorSigningInfo.Walk(
ctx, nil,
func(addr sdk.ConsAddress, info slashingtypes.ValidatorSigningInfo) (stop bool, err error) {
info.StartHeight = 0
err = app.SlashingKeeper.ValidatorSigningInfo.Set(ctx, addr, info)
if err != nil {
return true, err
}
return false, nil
},
)
if err != nil {
log.Fatal(err)
}
exportedApp.AppState = genesis
exportedApp.Height = int64(latestHeight)
return exportedApp, nil
}

View File

@ -2,33 +2,35 @@ package cmd
import (
"context"
"errors"
"io"
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"golang.org/x/sync/errgroup"
corestore "cosmossdk.io/core/store"
corectx "cosmossdk.io/core/context"
"cosmossdk.io/log"
serverv2 "cosmossdk.io/server/v2"
"cosmossdk.io/server/v2/api/grpc"
"cosmossdk.io/server/v2/api/rest"
"cosmossdk.io/server/v2/api/telemetry"
"cosmossdk.io/server/v2/cometbft"
serverstore "cosmossdk.io/server/v2/store"
confixcmd "cosmossdk.io/tools/confix/cmd"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/debug"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/pruning"
"github.com/cosmos/cosmos-sdk/client/snapshot"
"github.com/cosmos/cosmos-sdk/server"
servertypes "github.com/cosmos/cosmos-sdk/server/types"
sdk "github.com/cosmos/cosmos-sdk/types"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
"github.com/cosmos/cosmos-sdk/x/genutil"
genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
genutilv2 "github.com/cosmos/cosmos-sdk/x/genutil/v2"
genutilv2cli "github.com/cosmos/cosmos-sdk/x/genutil/v2/cli"
"git.vdb.to/cerc-io/laconicd/app"
"git.vdb.to/cerc-io/laconicd/gql"
"git.vdb.to/cerc-io/laconicd/utils"
)
func initRootCmd(rootCmd *cobra.Command, txConfig client.TxConfig, moduleManager moduleManager) {
@ -39,30 +41,47 @@ func initRootCmd(rootCmd *cobra.Command, txConfig client.TxConfig, moduleManager
genutilcli.InitCmd(moduleManager),
debug.Cmd(),
confixcmd.ConfigCommand(),
pruning.Cmd(newApp),
snapshot.Cmd(newApp),
// TODO
// pruning.Cmd(newApp),
// snapshot.Cmd(newApp),
)
server.AddCommands(rootCmd, newApp, server.StartCmdOptions[*app.LaconicApp]{
PostSetup: func(
_ *app.LaconicApp,
svrCtx *server.Context,
clientCtx client.Context,
ctx context.Context,
g *errgroup.Group,
) error {
g.Go(func() error {
return gql.Server(ctx, clientCtx, svrCtx.Logger.With("module", "gql-server"))
})
return nil
},
},
)
if err := serverv2.AddCommands(
rootCmd,
newApp,
initServerConfig(),
cometbft.New(
utils.GenericTxDecoder[app.AppTx]{txConfig},
initCometOptions[app.AppTx](),
initCometConfig(),
),
grpc.New[app.AppTx](),
serverstore.New[app.AppTx](),
telemetry.New[app.AppTx](),
rest.New[app.AppTx](),
// serverv2.StartCmdOptions[*app.LaconicApp]{
// PostSetup: func(
// _ *app.LaconicApp,
// svrCtx *server.Context,
// clientCtx client.Context,
// ctx context.Context,
// g *errgroup.Group,
// ) error {
// g.Go(func() error {
// return gql.Server(ctx, clientCtx, svrCtx.Logger.With("module", "gql-server"))
// })
// return nil
// },
// },
); err != nil {
panic(err)
}
// add keybase, auxiliary RPC, query, genesis, and tx child commands
rootCmd.AddCommand(
server.StatusCommand(),
genutilcli.Commands(
genutilv2cli.Commands(
moduleManager.Modules()[genutiltypes.ModuleName].(genutil.AppModule),
moduleManager,
appExport),
@ -117,69 +136,118 @@ func txCommand() *cobra.Command {
return cmd
}
// newApp is an appCreator
func newApp(
logger log.Logger,
db corestore.KVStoreWithBatch,
traceStore io.Writer,
appOpts servertypes.AppOptions,
) *app.LaconicApp {
var _ = db
baseappOptions := server.DefaultBaseappOptions(appOpts)
app, err := app.NewLaconicApp(logger, db, traceStore, true, appOpts, baseappOptions...)
func newApp(logger log.Logger, viper *viper.Viper) serverv2.AppI[app.AppTx] {
viper.Set(serverv2.FlagHome, app.DefaultNodeHome)
lacapp, err := app.NewLaconicApp(logger, viper, true)
if err != nil {
panic(err)
}
return app
return serverv2.AppI[app.AppTx](lacapp)
}
// appExport creates a new app (optionally at a given height) and exports state.
// // newApp is an appCreator
// func newApp(
// logger log.Logger,
// db corestore.KVStoreWithBatch,
// traceStore io.Writer,
// appOpts servertypes.AppOptions,
// ) *app.LaconicApp {
// var _ = db
// // baseappOptions := server.DefaultBaseappOptions(appOpts)
// app, err := app.NewLaconicApp(logger, viper, true)
// if err != nil {
// panic(err)
// }
// return app
// }
// appExport creates a new simapp (optionally at a given height) and exports state.
func appExport(
logger log.Logger,
db corestore.KVStoreWithBatch,
traceStore io.Writer,
ctx context.Context,
height int64,
forZeroHeight bool,
jailAllowedAddrs []string,
appOpts servertypes.AppOptions,
modulesToExport []string,
) (servertypes.ExportedApp, error) {
var (
laconicApp *app.LaconicApp
err error
)
// this check is necessary as we use the flag in x/upgrade.
// we can exit more gracefully by checking the flag here.
homePath, ok := appOpts.Get(flags.FlagHome).(string)
if !ok || homePath == "" {
return servertypes.ExportedApp{}, errors.New("application home not set")
}
viperAppOpts, ok := appOpts.(*viper.Viper)
) (genutilv2.ExportedApp, error) {
value := ctx.Value(corectx.ViperContextKey)
viper, ok := value.(*viper.Viper)
if !ok {
return servertypes.ExportedApp{}, errors.New("appOpts is not viper.Viper")
return genutilv2.ExportedApp{},
fmt.Errorf("incorrect viper type %T: expected *viper.Viper in context", value)
}
value = ctx.Value(corectx.LoggerContextKey)
logger, ok := value.(log.Logger)
if !ok {
return genutilv2.ExportedApp{},
fmt.Errorf("incorrect logger type %T: expected log.Logger in context", value)
}
// overwrite the FlagInvCheckPeriod
viperAppOpts.Set(server.FlagInvCheckPeriod, 1)
appOpts = viperAppOpts
viper.Set(server.FlagInvCheckPeriod, 1)
viper.Set(serverv2.FlagHome, app.DefaultNodeHome)
lacapp, err := app.NewLaconicApp(logger, viper, false)
if err != nil {
return genutilv2.ExportedApp{}, err
}
if height != -1 {
laconicApp, err = app.NewLaconicApp(logger, db, traceStore, false, appOpts)
if err != nil {
return servertypes.ExportedApp{}, err
}
if err := laconicApp.LoadHeight(height); err != nil {
return servertypes.ExportedApp{}, err
}
err = lacapp.LoadHeight(uint64(height))
} else {
laconicApp, err = app.NewLaconicApp(logger, db, traceStore, true, appOpts)
if err != nil {
return servertypes.ExportedApp{}, err
}
err = lacapp.LoadLatest()
}
if err != nil {
return genutilv2.ExportedApp{}, err
}
return laconicApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs, modulesToExport)
return lacapp.ExportAppStateAndValidators(jailAllowedAddrs)
}
// // appExport creates a new app (optionally at a given height) and exports state.
// func appExport(
// logger log.Logger,
// db corestore.KVStoreWithBatch,
// traceStore io.Writer,
// height int64,
// forZeroHeight bool,
// jailAllowedAddrs []string,
// appOpts servertypes.AppOptions,
// modulesToExport []string,
// ) (genutilv2.ExportedApp, error) {
// var (
// laconicApp *app.LaconicApp
// err error
// )
// // this check is necessary as we use the flag in x/upgrade.
// // we can exit more gracefully by checking the flag here.
// homePath, ok := appOpts.Get(flags.FlagHome).(string)
// if !ok || homePath == "" {
// return servertypes.ExportedApp{}, errors.New("application home not set")
// }
// viperAppOpts, ok := appOpts.(*viper.Viper)
// if !ok {
// return servertypes.ExportedApp{}, errors.New("appOpts is not viper.Viper")
// }
// // overwrite the FlagInvCheckPeriod
// viperAppOpts.Set(server.FlagInvCheckPeriod, 1)
// appOpts = viperAppOpts
// if height != -1 {
// laconicApp, err = app.NewLaconicApp(logger, db, false)
// if err != nil {
// return servertypes.ExportedApp{}, err
// }
// if err := laconicApp.LoadHeight(uint64(height)); err != nil {
// return servertypes.ExportedApp{}, err
// }
// } else {
// panic("height cannot be < 0")
// laconicApp, err = app.NewLaconicApp(logger, db, true)
// if err != nil {
// return servertypes.ExportedApp{}, err
// }
// }
// return laconicApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs, modulesToExport)
// }

106
cmd/laconicd/cmd/config.go Normal file
View File

@ -0,0 +1,106 @@
package cmd
import (
"strings"
"time"
cmtcfg "github.com/cometbft/cometbft/config"
"cosmossdk.io/core/transaction"
serverv2 "cosmossdk.io/server/v2"
"cosmossdk.io/server/v2/cometbft"
clientconfig "github.com/cosmos/cosmos-sdk/client/config"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
)
// initAppConfig helps to override default client config template and configs.
// return "", nil if no custom configuration is required for the application.
func initClientConfig() (string, interface{}) {
type GasConfig struct {
GasAdjustment float64 `mapstructure:"gas-adjustment"`
}
type CustomClientConfig struct {
clientconfig.Config `mapstructure:",squash"`
GasConfig GasConfig `mapstructure:"gas"`
}
// Optionally allow the chain developer to overwrite the SDK's default client config.
clientCfg := clientconfig.DefaultConfig()
// The SDK's default keyring backend is set to "os".
// This is more secure than "test" and is the recommended value.
//
// In simapp, we set the default keyring backend to test, as SimApp is meant
// to be an example and testing application.
clientCfg.KeyringBackend = keyring.BackendTest
// Now we set the custom config default values.
customClientConfig := CustomClientConfig{
Config: *clientCfg,
GasConfig: GasConfig{
GasAdjustment: 1.5,
},
}
// The default SDK app template is defined in serverconfig.DefaultConfigTemplate.
// We append the custom config template to the default one.
// And we set the default config to the custom app template.
customClientConfigTemplate := clientconfig.DefaultClientConfigTemplate + strings.TrimSpace(`
# This is default the gas adjustment factor used in tx commands.
# It can be overwritten by the --gas-adjustment flag in each tx command.
gas-adjustment = {{ .GasConfig.GasAdjustment }}
`)
return customClientConfigTemplate, customClientConfig
}
// Allow the chain developer to overwrite the server default app toml config.
func initServerConfig() serverv2.ServerConfig {
serverCfg := serverv2.DefaultServerConfig()
// The server's default minimum gas price is set to "0stake" inside
// app.toml. However, the chain developer can set a default app.toml value for their
// validators here. Please update value based on chain denom.
//
// In summary:
// - if you set serverCfg.MinGasPrices value, validators CAN tweak their
// own app.toml to override, or use this default value.
//
// In simapp, we set the min gas prices to 0.
serverCfg.MinGasPrices = "0stake"
return serverCfg
}
// initCometConfig helps to override default comet config template and configs.
func initCometConfig() cometbft.CfgOption {
cfg := cmtcfg.DefaultConfig()
// display only warn logs by default except for p2p and state
cfg.LogLevel = "*:warn,server:info,p2p:info,state:info"
// increase block timeout
cfg.Consensus.TimeoutCommit = 5 * time.Second
// overwrite default pprof listen address
cfg.RPC.PprofListenAddress = "localhost:6060"
return cometbft.OverwriteDefaultConfigTomlConfig(cfg)
}
func initCometOptions[T transaction.Tx]() cometbft.ServerOptions[T] {
serverOptions := cometbft.DefaultServerOptions[T]()
// overwrite app mempool, using max-txs option
// serverOptions.Mempool = func(cfg map[string]any) mempool.Mempool[T] {
// if maxTxs := cast.ToInt(cfg[cometbft.FlagMempoolMaxTxs]); maxTxs >= 0 {
// return sdkmempool.NewSenderNonceMempool(
// sdkmempool.SenderNonceMaxTxOpt(maxTxs),
// )
// }
// return mempool.NoOpMempool[T]{}
// }
return serverOptions
}

View File

@ -31,7 +31,6 @@ import (
"git.vdb.to/cerc-io/laconicd/app"
"git.vdb.to/cerc-io/laconicd/gql"
"git.vdb.to/cerc-io/laconicd/utils"
)
const EnvPrefix = "LACONIC"
@ -54,16 +53,8 @@ func NewRootCmd() *cobra.Command {
app.AppConfig(),
depinject.Supply(
log.NewNopLogger(),
utils.NewAddressCodec,
utils.NewValAddressCodec,
utils.NewConsAddressCodec,
),
runtime.DefaultServiceBindings(),
depinject.Provide(
codec.ProvideInterfaceRegistry,
codec.ProvideAddressCodec,
codec.ProvideProtoCodec,
codec.ProvideLegacyAmino,
ProvideClientContext,
ProvideKeyring,
),
@ -171,8 +162,8 @@ func ProvideClientContext(
panic(err)
}
// Workaround: Unset clientCtx.HomeDir and clientCtx.KeyringDir from depinject clientCtx as they are given precedence over
// the CLI args (--home flag) in some commands
// Workaround: Unset clientCtx.HomeDir and clientCtx.KeyringDir from depinject clientCtx as they
// are given precedence over the CLI args (--home flag) in some commands
// TODO: Implement proper fix
clientCtx.HomeDir = ""
clientCtx.KeyringDir = ""
@ -186,5 +177,5 @@ func ProvideKeyring(clientCtx client.Context, addressCodec address.Codec) (clien
return nil, err
}
return keyring.NewAutoCLIKeyring(kb)
return keyring.NewAutoCLIKeyring(kb, addressCodec)
}

View File

@ -6,8 +6,8 @@ import (
"github.com/stretchr/testify/require"
svrcmd "cosmossdk.io/server/v2"
"github.com/cosmos/cosmos-sdk/client/flags"
svrcmd "github.com/cosmos/cosmos-sdk/server/cmd"
"github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
"git.vdb.to/cerc-io/laconicd/app"

View File

@ -4,18 +4,18 @@ import (
"fmt"
"os"
svrcmd "github.com/cosmos/cosmos-sdk/server/cmd"
"git.vdb.to/cerc-io/laconicd/app"
"git.vdb.to/cerc-io/laconicd/app/params"
"git.vdb.to/cerc-io/laconicd/cmd/laconicd/cmd"
clientv2helpers "cosmossdk.io/client/v2/helpers"
serverv2 "cosmossdk.io/server/v2"
_ "git.vdb.to/cerc-io/laconicd/app/params" // import for side-effects
)
func main() {
params.SetAddressPrefixes()
rootCmd := cmd.NewRootCmd()
if err := svrcmd.Execute(rootCmd, "", app.DefaultNodeHome); err != nil {
if err := serverv2.Execute(rootCmd, clientv2helpers.EnvPrefix, app.DefaultNodeHome); err != nil {
fmt.Fprintln(rootCmd.OutOrStderr(), err)
os.Exit(1)
}

12
go.mod
View File

@ -27,7 +27,7 @@ require (
cosmossdk.io/tools/confix v0.1.0
github.com/99designs/gqlgen v0.17.22
github.com/cometbft/cometbft v1.0.0-rc1.0.20240908111210-ab0be101882f
github.com/cometbft/cometbft/api v1.0.0-rc.1
github.com/cometbft/cometbft/api v1.0.0-rc.1 // indirect
github.com/cosmos/cosmos-db v1.0.3-0.20240911104526-ddc3f09bfc22
github.com/cosmos/cosmos-proto v1.0.0-beta.5
github.com/cosmos/cosmos-sdk v1.0.0
@ -48,7 +48,7 @@ require (
github.com/statechannels/go-nitro v0.1.2
github.com/stretchr/testify v1.9.0
github.com/vektah/gqlparser/v2 v2.5.11
golang.org/x/sync v0.8.0
golang.org/x/sync v0.8.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142
google.golang.org/grpc v1.67.1
google.golang.org/protobuf v1.35.1
@ -56,6 +56,8 @@ require (
)
require (
cosmossdk.io/server/v2/cometbft v0.0.0-20241017154543-c1707b830856
cosmossdk.io/store/v2 v2.0.0-00010101000000-000000000000
cosmossdk.io/x/accounts v1.0.0-alpha.4
cosmossdk.io/x/bank v1.0.0-alpha.4
cosmossdk.io/x/consensus v0.0.0-20241007000829-38662ecb209f
@ -73,9 +75,8 @@ require (
cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29 // indirect
cosmossdk.io/errors/v2 v2.0.0-20240731132947-df72853b3ca5 // indirect
cosmossdk.io/schema v0.3.1-0.20241010135032-192601639cac // indirect
cosmossdk.io/server/v2/appmanager v0.0.0-00010101000000-000000000000 // indirect
cosmossdk.io/server/v2/stf v0.0.0-00010101000000-000000000000 // indirect
cosmossdk.io/store/v2 v2.0.0-00010101000000-000000000000 // indirect
cosmossdk.io/server/v2/appmanager v0.0.0-20240802110823-cffeedff643d // indirect
cosmossdk.io/server/v2/stf v0.0.0-20240708142107-25e99c54bac1 // indirect
cosmossdk.io/x/epochs v0.0.0-20240522060652-a1ae4c3e0337 // indirect
cosmossdk.io/x/tx v0.13.3 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
@ -254,6 +255,7 @@ replace (
cosmossdk.io/runtime/v2 => ../cosmos-sdk/runtime/v2
cosmossdk.io/server/v2 => ../cosmos-sdk/server/v2
cosmossdk.io/server/v2/appmanager => ../cosmos-sdk/server/v2/appmanager
cosmossdk.io/server/v2/cometbft => ../cosmos-sdk/server/v2/cometbft
cosmossdk.io/server/v2/stf => ../cosmos-sdk/server/v2/stf
cosmossdk.io/store => ../cosmos-sdk/store
cosmossdk.io/store/v2 => ../cosmos-sdk/store/v2

48
utils/tx.go Normal file
View File

@ -0,0 +1,48 @@
package utils
import (
"errors"
"cosmossdk.io/core/transaction"
"github.com/cosmos/cosmos-sdk/client"
)
var _ transaction.Codec[transaction.Tx] = &GenericTxDecoder[transaction.Tx]{}
type GenericTxDecoder[T transaction.Tx] struct {
TxConfig client.TxConfig
}
// Decode implements transaction.Codec.
func (t GenericTxDecoder[T]) Decode(bz []byte) (T, error) {
var out T
tx, err := t.TxConfig.TxDecoder()(bz)
if err != nil {
return out, err
}
var ok bool
out, ok = tx.(T)
if !ok {
return out, errors.New("unexpected Tx type")
}
return out, nil
}
// DecodeJSON implements transaction.Codec.
func (t GenericTxDecoder[T]) DecodeJSON(bz []byte) (T, error) {
var out T
tx, err := t.TxConfig.TxJSONDecoder()(bz)
if err != nil {
return out, err
}
var ok bool
out, ok = tx.(T)
if !ok {
return out, errors.New("unexpected Tx type")
}
return out, nil
}