cosmos-sdk/server/v2/cometbft/server.go
2024-07-04 11:13:29 +00:00

220 lines
6.7 KiB
Go

package cometbft
import (
"context"
"crypto/sha256"
"encoding/json"
"fmt"
"path/filepath"
abciserver "github.com/cometbft/cometbft/abci/server"
cmtcmd "github.com/cometbft/cometbft/cmd/cometbft/commands"
cmtcfg "github.com/cometbft/cometbft/config"
"github.com/cometbft/cometbft/node"
"github.com/cometbft/cometbft/p2p"
pvm "github.com/cometbft/cometbft/privval"
"github.com/cometbft/cometbft/proxy"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
corectx "cosmossdk.io/core/context"
"cosmossdk.io/core/log"
"cosmossdk.io/core/transaction"
serverv2 "cosmossdk.io/server/v2"
cometlog "cosmossdk.io/server/v2/cometbft/log"
"cosmossdk.io/server/v2/cometbft/types"
"cosmossdk.io/store/v2/snapshots"
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
)
var (
_ serverv2.ServerComponent[
serverv2.AppI[transaction.Tx], transaction.Tx,
] = (*CometBFTServer[serverv2.AppI[transaction.Tx], transaction.Tx])(nil)
_ serverv2.HasCLICommands = (*CometBFTServer[serverv2.AppI[transaction.Tx], transaction.Tx])(nil)
_ serverv2.HasStartFlags = (*CometBFTServer[serverv2.AppI[transaction.Tx], transaction.Tx])(nil)
)
type CometBFTServer[AppT serverv2.AppI[T], T transaction.Tx] struct {
Node *node.Node
Consensus *Consensus[T]
initTxCodec transaction.Codec[T]
logger log.Logger
config Config
options ServerOptions[T]
cmtConfigOptions []CmtCfgOption
}
func New[AppT serverv2.AppI[T], T transaction.Tx](txCodec transaction.Codec[T], options ServerOptions[T], cfgOptions ...CmtCfgOption) *CometBFTServer[AppT, T] {
return &CometBFTServer[AppT, T]{
initTxCodec: txCodec,
options: options,
cmtConfigOptions: cfgOptions,
}
}
func (s *CometBFTServer[AppT, T]) Init(appI AppT, v *viper.Viper, logger log.Logger) error {
s.config = Config{CmtConfig: GetConfigFromViper(v), ConsensusAuthority: appI.GetConsensusAuthority()}
s.logger = logger.With(log.ModuleKey, s.Name())
// create consensus
store := appI.GetStore().(types.Store)
consensus := NewConsensus[T](appI.GetAppManager(), s.options.Mempool, store, s.config, s.initTxCodec, s.logger)
consensus.prepareProposalHandler = s.options.PrepareProposalHandler
consensus.processProposalHandler = s.options.ProcessProposalHandler
consensus.verifyVoteExt = s.options.VerifyVoteExtensionHandler
consensus.extendVote = s.options.ExtendVoteHandler
// TODO: set these; what is the appropriate presence of the Store interface here?
var ss snapshots.StorageSnapshotter
var sc snapshots.CommitSnapshotter
snapshotStore, err := GetSnapshotStore(s.config.CmtConfig.RootDir)
if err != nil {
return err
}
sm := snapshots.NewManager(snapshotStore, s.options.SnapshotOptions, sc, ss, nil, s.logger)
consensus.SetSnapshotManager(sm)
s.Consensus = consensus
return nil
}
func (s *CometBFTServer[AppT, T]) Name() string {
return "comet"
}
func (s *CometBFTServer[AppT, T]) Start(ctx context.Context) error {
viper := ctx.Value(corectx.ViperContextKey).(*viper.Viper)
cometConfig := GetConfigFromViper(viper)
wrappedLogger := cometlog.CometLoggerWrapper{Logger: s.logger}
if s.config.Standalone {
svr, err := abciserver.NewServer(s.config.Addr, s.config.Transport, s.Consensus)
if err != nil {
return fmt.Errorf("error creating listener: %w", err)
}
svr.SetLogger(wrappedLogger)
return svr.Start()
}
nodeKey, err := p2p.LoadOrGenNodeKey(cometConfig.NodeKeyFile())
if err != nil {
return err
}
s.Node, err = node.NewNode(
ctx,
cometConfig,
pvm.LoadOrGenFilePV(cometConfig.PrivValidatorKeyFile(), cometConfig.PrivValidatorStateFile()),
nodeKey,
proxy.NewConsensusSyncLocalClientCreator(s.Consensus),
getGenDocProvider(cometConfig),
cmtcfg.DefaultDBProvider,
node.DefaultMetricsProvider(cometConfig.Instrumentation),
wrappedLogger,
)
if err != nil {
return err
}
return s.Node.Start()
}
func (s *CometBFTServer[AppT, T]) Stop(context.Context) error {
if s.Node != nil && s.Node.IsRunning() {
return s.Node.Stop()
}
return nil
}
// returns a function which returns the genesis doc from the genesis file.
func getGenDocProvider(cfg *cmtcfg.Config) func() (node.ChecksummedGenesisDoc, error) {
return func() (node.ChecksummedGenesisDoc, error) {
appGenesis, err := genutiltypes.AppGenesisFromFile(cfg.GenesisFile())
if err != nil {
return node.ChecksummedGenesisDoc{
Sha256Checksum: []byte{},
}, err
}
gen, err := appGenesis.ToGenesisDoc()
if err != nil {
return node.ChecksummedGenesisDoc{
Sha256Checksum: []byte{},
}, err
}
genbz, err := gen.AppState.MarshalJSON()
if err != nil {
return node.ChecksummedGenesisDoc{
Sha256Checksum: []byte{},
}, err
}
bz, err := json.Marshal(genbz)
if err != nil {
return node.ChecksummedGenesisDoc{
Sha256Checksum: []byte{},
}, err
}
sum := sha256.Sum256(bz)
return node.ChecksummedGenesisDoc{
GenesisDoc: gen,
Sha256Checksum: sum[:],
}, nil
}
}
func (s *CometBFTServer[AppT, T]) StartCmdFlags() *pflag.FlagSet {
flags := pflag.NewFlagSet("cometbft", pflag.ExitOnError)
flags.Bool(FlagWithComet, true, "Run abci app embedded in-process with CometBFT")
flags.String(FlagAddress, "tcp://127.0.0.1:26658", "Listen address")
flags.String(FlagTransport, "socket", "Transport protocol: socket, grpc")
flags.String(FlagTraceStore, "", "Enable KVStore tracing to an output file")
flags.String(FlagMinGasPrices, "", "Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photino;0.0001stake)")
flags.Uint64(FlagQueryGasLimit, 0, "Maximum gas a Rest/Grpc query can consume. Blank and 0 imply unbounded.")
flags.Uint64(FlagHaltHeight, 0, "Block height at which to gracefully halt the chain and shutdown the node")
flags.Uint64(FlagHaltTime, 0, "Minimum block time (in Unix seconds) at which to gracefully halt the chain and shutdown the node")
flags.String(FlagCPUProfile, "", "Enable CPU profiling and write to the provided file")
flags.Bool(FlagTrace, false, "Provide full stack traces for errors in ABCI Log")
return flags
}
func (s *CometBFTServer[AppT, T]) CLICommands() serverv2.CLIConfig {
return serverv2.CLIConfig{
Commands: []*cobra.Command{
s.StatusCommand(),
s.ShowNodeIDCmd(),
s.ShowValidatorCmd(),
s.ShowAddressCmd(),
s.VersionCmd(),
cmtcmd.ResetAllCmd,
cmtcmd.ResetStateCmd,
},
Queries: []*cobra.Command{
s.QueryBlockCmd(),
s.QueryBlocksCmd(),
s.QueryBlockResultsCmd(),
},
}
}
func (s *CometBFTServer[AppT, T]) WriteDefaultConfigAt(configPath string) error {
cometConfig := cmtcfg.DefaultConfig()
for _, opt := range s.cmtConfigOptions {
opt(cometConfig)
}
cmtcfg.WriteConfigFile(filepath.Join(configPath, "config.toml"), cometConfig)
return nil
}