package main import ( "flag" "fmt" "log" "os" "os/signal" "path/filepath" "sync" "syscall" "time" "github.com/cometbft/cometbft/p2p" "github.com/cometbft/cometbft/privval" "github.com/cometbft/cometbft/proxy" "github.com/samber/lo" "tangled.org/gbl08ma.com/didplcbft/abciapp" "tangled.org/gbl08ma.com/didplcbft/httpapi" bftconfig "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/dgraph-io/badger/v4" "github.com/dgraph-io/badger/v4/options" "github.com/spf13/viper" ) var homeDir string func init() { flag.StringVar(&homeDir, "data-dir", "", "Path to the CometBFT config directory (if empty, uses ./didplcbft-data)") } func main() { flag.Parse() if homeDir == "" { homeDir = filepath.Join(lo.Must(os.Getwd()), "didplcbft-data") } config := DefaultConfig() config.SetRoot(homeDir) viper.SetConfigFile(fmt.Sprintf("%s/%s", homeDir, "config/config.toml")) if err := viper.ReadInConfig(); err != nil { log.Fatalf("Reading config: %v", err) } if err := viper.Unmarshal(config); err != nil { log.Fatalf("Decoding config: %v", err) } if err := config.ValidateBasic(); err != nil { log.Fatalf("Invalid configuration data: %v", err) } badgerDBPath := filepath.Join(homeDir, "badger") badgerDB, err := badger.Open(badger. DefaultOptions(badgerDBPath). WithBlockCacheSize(2 << 30). WithBlockSize(8 * 1024). WithNumMemtables(3). WithNumLevelZeroTables(3). WithCompression(options.Snappy)) if err != nil { log.Fatalf("Opening badger database: %v", err) } for err == nil { err = badgerDB.RunValueLogGC(0.5) } /*err = badgerDB.Flatten(4) if err != nil { log.Fatalf("Flattening badger database: %v", err) }*/ var wg sync.WaitGroup closeGoroutinesCh := make(chan struct{}) wg.Go(func() { ticker := time.NewTicker(5 * time.Minute) defer ticker.Stop() for { select { case <-ticker.C: var err error for err == nil { err = badgerDB.RunValueLogGC(0.5) } case <-closeGoroutinesCh: return } } }) defer func() { if err := badgerDB.Close(); err != nil { log.Printf("Closing badger database: %v", err) } }() app, plc, cleanup, err := abciapp.NewDIDPLCApplication(badgerDB, filepath.Join(homeDir, "snapshots")) if err != nil { log.Fatalf("failed to create DIDPLC application: %v", err) } defer cleanup() pv := privval.LoadFilePV( config.PrivValidatorKeyFile(), config.PrivValidatorStateFile(), ) nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile()) if err != nil { log.Fatalf("failed to load node's key: %v", err) } logger := cmtlog.NewTMLogger(cmtlog.NewSyncWriter(os.Stdout)) logger, err = cmtflags.ParseLogLevel(config.LogLevel, logger, bftconfig.DefaultLogLevel) if err != nil { log.Fatalf("failed to parse log level: %v", err) } node, err := nm.NewNode( config.Config, pv, nodeKey, proxy.NewLocalClientCreator(app), nm.DefaultGenesisDocProviderFunc(config.Config), bftconfig.DefaultDBProvider, nm.DefaultMetricsProvider(config.Config.Instrumentation), logger, ) if err != nil { log.Fatalf("Creating node: %v", err) } err = node.Start() if err != nil { log.Fatalf("Starting node: %v", err) } defer func() { node.Stop() node.Wait() }() if config.PLC.ListenAddress != "" { plcAPIServer, err := httpapi.NewServer(plc, node, config.PLC.ListenAddress, 30*time.Second) if err != nil { log.Fatalf("Creating PLC API server: %v", err) } err = plcAPIServer.Start() if err != nil { log.Fatalf("Starting PLC API server: %v", err) } defer func() { plcAPIServer.Stop() plcAPIServer.Wait() }() } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) <-c close(closeGoroutinesCh) wg.Wait() }