The main function in the forum.go
file is responsible for running the Forum Application blockchain.
package main
import (
"context"
"flag"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"path/filepath"
"syscall"
"time"
"github.com/spf13/viper"
"github.com/cometbft/cometbft/abci/tutorials/abci-v2-forum-app/abci"
cfg "github.com/cometbft/cometbft/config"
cmtflags "github.com/cometbft/cometbft/libs/cli/flags"
cmtlog "github.com/cometbft/cometbft/libs/log"
nm "github.com/cometbft/cometbft/node"
"github.com/cometbft/cometbft/p2p"
"github.com/cometbft/cometbft/privval"
"github.com/cometbft/cometbft/proxy"
)
var homeDir string
func init() {
flag.StringVar(&homeDir, "home", "", "Path to the CometBFT config directory (if empty, uses $HOME/.forumapp)")
}
func main() {
flag.Parse()
if homeDir == "" {
homeDir = os.ExpandEnv("$HOME/.forumapp")
}
config := cfg.DefaultConfig()
config.SetRoot(homeDir)
viper.SetConfigFile(fmt.Sprintf("%s/%s", homeDir, "config/config.toml"))
if err := viper.ReadInConfig(); err != nil {
log.Fatalf("failed to read config: %v", err)
}
logger := cmtlog.NewLogger(os.Stdout)
logger, err := cmtflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel)
if err != nil {
log.Printf("failed to parse log level: %v", err)
defer os.Exit(1)
}
dbPath := filepath.Join(homeDir, "forum-db")
appConfigPath := "app.toml"
app, err := abci.NewForumApp(dbPath, appConfigPath, logger)
if err != nil {
log.Printf("failed to create Forum Application: %v\n", err)
os.Exit(1)
}
nodeKey, err := nodekey.LoadNodeKey(config.NodeKeyFile())
if err != nil {
log.Printf("failed to load node key: %v", err)
os.Exit(1)
}
pv := privval.LoadFilePV(
config.PrivValidatorKeyFile(),
config.PrivValidatorStateFile(),
)
node, err := nm.NewNode(
context.Background(),
config,
pv,
nodeKey,
proxy.NewLocalClientCreator(app),
nm.DefaultGenesisDocProviderFunc(config),
cfg.DefaultDBProvider,
nm.DefaultMetricsProvider(config.Instrumentation),
logger,
)
if err != nil {
log.Printf("failed to create CometBFT node")
os.Exit(1)
}
if err := node.Start(); err != nil {
log.Printf("failed to start CometBFT node")
os.Exit(1)
}
defer func() {
_ = node.Stop()
node.Wait()
}()
httpAddr := "127.0.0.1:8080"
server := &http.Server{
Addr: httpAddr,
ReadHeaderTimeout: 5 * time.Second,
}
if err := server.ListenAndServe(); err != nil {
log.Printf("failed to start HTTP server: %v", err)
os.Exit(1)
}
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
log.Println("Forum application stopped")
}
Explanation of code
The program begins by parsing command-line flags using flag.Parse().
It then checks if the homeDir
variable is empty
and assigns a default value if it is.
Next, it creates a configuration object using cfg.DefaultConfig
() and sets the root directory using config.SetRoot(homeDir).
It also sets the configuration file path using viper.SetConfigFile(fmt.Sprintf("%s/%s", homeDir, "config.toml")).
The program attempts to read the configuration file using viper.ReadInConfig()
, and if there is an error, it logs
the failure and exits.
It then creates a database using db.NewPebbleDB(filepath.Join(homeDir, "forum-db"), ".").
If there is an error during
the creation of the database, it logs the failure and exits.
The program proceeds to create an instance of the ForumApp object using forum.NewForumApp(dbPath, appConfigPath)
.
If there is an error during the creation of the ForumApp instance, it logs the failure and exits.
The program then sets up logging using cmtlog.NewLogger(os.Stdout)
and parses the log level
using cmtflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel).
If there is an error during log level parsing,
it logs the failure and exits.
The program loads the node key using nodekey.LoadNodeKey(config.NodeKeyFile()).
If there is an error during the loading
of the node key, it logs the failure and exits.
Next, it loads the private validator using privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()).
The program creates a CometBFT node using nm.NewNode()
and passes various parameters such as configuration, private
validator, node key, client creator, genesis doc provider, database provider, metrics provider, and logger. If there is
an error during the creation of the CometBFT node, it logs the failure and exits.
The program starts the CometBFT node using node.Start().
If there is an error during node startup, it logs the failure
and exits.
If there is an error starting the HTTP server, it logs the failure and exits.
The program sets up a signal channel to handle SIGINT
and SIGTERM
signals. It waits for a signal to be received on
the channel, and when it does, it stops the CometBFT node using node.Stop()
and waits for it to terminate using node.Wait().
Finally, it prints a message indicating that the forum application has stopped.
In the next session, you will learn about Vote Extension that lets validators extend their vote in the forum application blockchain.