cosmos-sdk/server/v2/cometbft/server.go
2024-05-31 00:30:15 +00:00

226 lines
6.4 KiB
Go

package cometbft
import (
"context"
"crypto/sha256"
"encoding/json"
"fmt"
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"
"cosmossdk.io/core/log"
"cosmossdk.io/core/transaction"
serverv2 "cosmossdk.io/server/v2"
"cosmossdk.io/server/v2/appmanager"
"cosmossdk.io/server/v2/cometbft/handlers"
cometlog "cosmossdk.io/server/v2/cometbft/log"
"cosmossdk.io/server/v2/cometbft/mempool"
"cosmossdk.io/server/v2/cometbft/types"
"cosmossdk.io/store/v2/snapshots"
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
)
const (
flagWithComet = "with-comet"
flagAddress = "address"
flagTransport = "transport"
flagTraceStore = "trace-store"
flagCPUProfile = "cpu-profile"
FlagMinGasPrices = "minimum-gas-prices"
FlagQueryGasLimit = "query-gas-limit"
FlagHaltHeight = "halt-height"
FlagHaltTime = "halt-time"
FlagTrace = "trace"
)
var _ serverv2.ServerModule = (*CometBFTServer[transaction.Tx])(nil)
type CometBFTServer[T transaction.Tx] struct {
Node *node.Node
App *Consensus[T]
logger log.Logger
config Config
cleanupFn func()
}
// App is an interface that represents an application in the CometBFT server.
// It provides methods to access the app manager, logger, and store.
type App[T transaction.Tx] interface {
GetApp() *appmanager.AppManager[T]
GetLogger() log.Logger
GetStore() types.Store
}
func NewCometBFTServer[T transaction.Tx](
app *appmanager.AppManager[T],
store types.Store,
logger log.Logger,
cfg Config,
txCodec transaction.Codec[T],
) *CometBFTServer[T] {
logger = logger.With("module", "cometbft-server")
// create noop mempool
mempool := mempool.NoOpMempool[T]{}
// create consensus
consensus := NewConsensus[T](app, mempool, store, cfg, txCodec, logger)
consensus.SetPrepareProposalHandler(handlers.NoOpPrepareProposal[T]())
consensus.SetProcessProposalHandler(handlers.NoOpProcessProposal[T]())
consensus.SetVerifyVoteExtension(handlers.NoOpVerifyVoteExtensionHandler())
consensus.SetExtendVoteExtension(handlers.NoOpExtendVote())
// TODO: set these; what is the appropriate presence of the Store interface here?
var ss snapshots.StorageSnapshotter
var sc snapshots.CommitSnapshotter
snapshotStore, err := GetSnapshotStore(cfg.CmtConfig.RootDir)
if err != nil {
panic(err)
}
sm := snapshots.NewManager(snapshotStore, snapshots.SnapshotOptions{}, sc, ss, nil, logger) // TODO: set options somehow
consensus.SetSnapshotManager(sm)
return &CometBFTServer[T]{
logger: logger,
App: consensus,
config: cfg,
}
}
func (s *CometBFTServer[T]) Name() string {
return "cometbft"
}
func (s *CometBFTServer[T]) Start(ctx context.Context) error {
wrappedLogger := cometlog.CometLoggerWrapper{Logger: s.logger}
if s.config.Standalone {
svr, err := abciserver.NewServer(s.config.Addr, s.config.Transport, s.App)
if err != nil {
return fmt.Errorf("error creating listener: %w", err)
}
svr.SetLogger(wrappedLogger)
return svr.Start()
}
nodeKey, err := p2p.LoadOrGenNodeKey(s.config.CmtConfig.NodeKeyFile())
if err != nil {
return err
}
s.Node, err = node.NewNode(
ctx,
s.config.CmtConfig,
pvm.LoadOrGenFilePV(s.config.CmtConfig.PrivValidatorKeyFile(), s.config.CmtConfig.PrivValidatorStateFile()),
nodeKey,
proxy.NewLocalClientCreator(s.App),
getGenDocProvider(s.config.CmtConfig),
cmtcfg.DefaultDBProvider,
node.DefaultMetricsProvider(s.config.CmtConfig.Instrumentation),
wrappedLogger,
)
if err != nil {
return err
}
s.cleanupFn = func() {
if s.Node != nil && s.Node.IsRunning() {
_ = s.Node.Stop()
}
}
return s.Node.Start()
}
func (s *CometBFTServer[T]) Stop(_ context.Context) error {
defer s.cleanupFn()
if s.Node != nil {
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[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[T]) CLICommands() serverv2.CLIConfig {
return serverv2.CLIConfig{
Commands: []*cobra.Command{
s.StatusCommand(),
s.ShowNodeIDCmd(),
s.ShowValidatorCmd(),
s.ShowAddressCmd(),
s.VersionCmd(),
s.QueryBlockCmd(),
s.QueryBlocksCmd(),
s.QueryBlockResultsCmd(),
cmtcmd.ResetAllCmd,
cmtcmd.ResetStateCmd,
},
}
}