mirror of
https://github.com/luxfi/indexer.git
synced 2026-09-24 00:25:37 +00:00
Co-authored-by: dhrubabasu <[email protected]> Co-authored-by: Patrick O'Grady <[email protected]> Co-authored-by: Stephen <[email protected]>
410 lines
11 KiB
Go
410 lines
11 KiB
Go
// (c) 2021, Ava Labs, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"math/rand"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/ava-labs/avalanchego/utils/logging"
|
|
"github.com/ava-labs/ortelius/api"
|
|
"github.com/ava-labs/ortelius/balance"
|
|
"github.com/ava-labs/ortelius/cfg"
|
|
"github.com/ava-labs/ortelius/db"
|
|
"github.com/ava-labs/ortelius/models"
|
|
"github.com/ava-labs/ortelius/replay"
|
|
oreliusRpc "github.com/ava-labs/ortelius/rpc"
|
|
"github.com/ava-labs/ortelius/services/rewards"
|
|
"github.com/ava-labs/ortelius/servicesctrl"
|
|
"github.com/ava-labs/ortelius/stream"
|
|
"github.com/ava-labs/ortelius/stream/consumers"
|
|
"github.com/ava-labs/ortelius/utils"
|
|
"github.com/go-sql-driver/mysql"
|
|
"github.com/gorilla/rpc/v2"
|
|
"github.com/gorilla/rpc/v2/json2"
|
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
"github.com/spf13/cobra"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
const (
|
|
// rootCmdUse = "orteliusd [command]\nex: orteliusd api"
|
|
rootCmdUse = "orteliusd [command]"
|
|
rootCmdDesc = "Daemons for Ortelius."
|
|
|
|
apiCmdUse = "api"
|
|
apiCmdDesc = "Runs the API daemon"
|
|
|
|
streamCmdUse = "stream"
|
|
streamCmdDesc = "Runs stream commands"
|
|
|
|
streamReplayCmdUse = "replay"
|
|
streamReplayCmdDesc = "Runs the replay"
|
|
|
|
streamIndexerCmdUse = "indexer"
|
|
streamIndexerCmdDesc = "Runs the stream indexer daemon"
|
|
|
|
envCmdUse = "env"
|
|
envCmdDesc = "Displays information about the Ortelius environment"
|
|
|
|
defaultReplayQueueSize = int(2000)
|
|
defaultReplayQueueThreads = int(4)
|
|
)
|
|
|
|
func main() {
|
|
if err := execute(); err != nil {
|
|
log.Fatalln("Failed to run:", err.Error())
|
|
}
|
|
}
|
|
|
|
// Execute runs the root command for ortelius
|
|
func execute() error {
|
|
rand.Seed(time.Now().UnixNano())
|
|
|
|
var (
|
|
runErr error
|
|
config = &cfg.Config{}
|
|
serviceControl = &servicesctrl.Control{}
|
|
configFile = func() *string { s := ""; return &s }()
|
|
replayqueuesize = func() *int { i := defaultReplayQueueSize; return &i }()
|
|
replayqueuethreads = func() *int { i := defaultReplayQueueThreads; return &i }()
|
|
cmd = &cobra.Command{Use: rootCmdUse, Short: rootCmdDesc, Long: rootCmdDesc,
|
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
|
c, err := cfg.NewFromFile(*configFile)
|
|
if err != nil {
|
|
log.Fatalln("Failed to read config file", *configFile, ":", err.Error())
|
|
}
|
|
lf := logging.NewFactory(c.Logging)
|
|
alog, err := lf.Make("ortelius")
|
|
if err != nil {
|
|
log.Fatalln("Failed to create log", c.Logging.Directory, ":", err.Error())
|
|
}
|
|
|
|
mysqllogger := &MysqlLogger{
|
|
Log: alog,
|
|
}
|
|
_ = mysql.SetLogger(mysqllogger)
|
|
|
|
models.SetBech32HRP(c.NetworkID)
|
|
|
|
serviceControl.Log = alog
|
|
serviceControl.Services = c.Services
|
|
serviceControl.ServicesCfg = *c
|
|
serviceControl.Chains = c.Chains
|
|
serviceControl.Persist = db.NewPersist()
|
|
serviceControl.Features = c.Features
|
|
persist := db.NewPersist()
|
|
serviceControl.BalanceManager = balance.NewManager(persist, serviceControl)
|
|
err = serviceControl.Init(c.NetworkID)
|
|
if err != nil {
|
|
log.Fatalln("Failed to create service control", ":", err.Error())
|
|
}
|
|
|
|
*config = *c
|
|
|
|
if config.MetricsListenAddr != "" {
|
|
sm := http.NewServeMux()
|
|
sm.Handle("/metrics", promhttp.Handler())
|
|
go func() {
|
|
server := &http.Server{
|
|
Addr: config.MetricsListenAddr,
|
|
Handler: sm,
|
|
ReadHeaderTimeout: 5 * time.Second,
|
|
}
|
|
err = server.ListenAndServe()
|
|
if err != nil {
|
|
log.Fatalln("Failed to start metrics listener", err.Error())
|
|
}
|
|
}()
|
|
alog.Info("starting metrics handler",
|
|
zap.String("addr", config.MetricsListenAddr),
|
|
)
|
|
}
|
|
if config.AdminListenAddr != "" {
|
|
rpcServer := rpc.NewServer()
|
|
codec := json2.NewCodec()
|
|
rpcServer.RegisterCodec(codec, "application/json")
|
|
rpcServer.RegisterCodec(codec, "application/json;charset=UTF-8")
|
|
api := oreliusRpc.NewAPI(alog)
|
|
if err := rpcServer.RegisterService(api, "api"); err != nil {
|
|
log.Fatalln("Failed to start admin listener", err.Error())
|
|
}
|
|
sm := http.NewServeMux()
|
|
sm.Handle("/api", rpcServer)
|
|
go func() {
|
|
server := &http.Server{
|
|
Handler: sm,
|
|
Addr: config.AdminListenAddr,
|
|
ReadHeaderTimeout: 5 * time.Second,
|
|
}
|
|
err = server.ListenAndServe()
|
|
if err != nil {
|
|
log.Fatalln("Failed to start metrics listener", err.Error())
|
|
}
|
|
}()
|
|
}
|
|
},
|
|
}
|
|
)
|
|
|
|
// Add flags and commands
|
|
cmd.PersistentFlags().StringVarP(configFile, "config", "c", "config.json", "config file")
|
|
cmd.PersistentFlags().IntVarP(replayqueuesize, "replayqueuesize", "", defaultReplayQueueSize, fmt.Sprintf("replay queue size default %d", defaultReplayQueueSize))
|
|
cmd.PersistentFlags().IntVarP(replayqueuethreads, "replayqueuethreads", "", defaultReplayQueueThreads, fmt.Sprintf("replay queue size threads default %d", defaultReplayQueueThreads))
|
|
|
|
cmd.AddCommand(
|
|
createReplayCmds(serviceControl, config, &runErr, replayqueuesize, replayqueuethreads),
|
|
createStreamCmds(serviceControl, config, &runErr),
|
|
createAPICmds(serviceControl, config, &runErr),
|
|
createEnvCmds(config, &runErr))
|
|
|
|
// Execute the command and return the runErr to the caller
|
|
if err := cmd.Execute(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return runErr
|
|
}
|
|
|
|
func createAPICmds(sc *servicesctrl.Control, config *cfg.Config, runErr *error) *cobra.Command {
|
|
return &cobra.Command{
|
|
Use: apiCmdUse,
|
|
Short: apiCmdDesc,
|
|
Long: apiCmdDesc,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
lc, err := api.NewServer(sc, *config)
|
|
if err != nil {
|
|
*runErr = err
|
|
return
|
|
}
|
|
runListenCloser(lc)
|
|
},
|
|
}
|
|
}
|
|
|
|
func createReplayCmds(sc *servicesctrl.Control, config *cfg.Config, runErr *error, replayqueuesize *int, replayqueuethreads *int) *cobra.Command {
|
|
replayCmd := &cobra.Command{
|
|
Use: streamReplayCmdUse,
|
|
Short: streamReplayCmdDesc,
|
|
Long: streamReplayCmdDesc,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
replay := replay.NewDB(sc, config, *replayqueuesize, *replayqueuethreads)
|
|
err := replay.Start()
|
|
if err != nil {
|
|
*runErr = err
|
|
}
|
|
},
|
|
}
|
|
|
|
return replayCmd
|
|
}
|
|
|
|
func createStreamCmds(sc *servicesctrl.Control, config *cfg.Config, runErr *error) *cobra.Command {
|
|
streamCmd := &cobra.Command{
|
|
Use: streamCmdUse,
|
|
Short: streamCmdDesc,
|
|
Long: streamCmdDesc,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
_ = cmd.Help()
|
|
os.Exit(0)
|
|
},
|
|
}
|
|
|
|
// Add sub commands
|
|
streamCmd.AddCommand(&cobra.Command{
|
|
Use: streamIndexerCmdUse,
|
|
Short: streamIndexerCmdDesc,
|
|
Long: streamIndexerCmdDesc,
|
|
Run: func(cmd *cobra.Command, arg []string) {
|
|
runStreamProcessorManagers(
|
|
sc,
|
|
config,
|
|
runErr,
|
|
producerFactories(sc, config),
|
|
[]consumers.ConsumerFactory{
|
|
consumers.IndexerConsumer,
|
|
},
|
|
[]stream.ProcessorFactoryChainDB{
|
|
consumers.IndexerDB,
|
|
consumers.IndexerConsensusDB,
|
|
},
|
|
[]stream.ProcessorFactoryInstDB{
|
|
consumers.IndexerCChainDB(),
|
|
},
|
|
)(cmd, arg)
|
|
},
|
|
})
|
|
|
|
return streamCmd
|
|
}
|
|
|
|
func producerFactories(sc *servicesctrl.Control, cfg *cfg.Config) []utils.ListenCloser {
|
|
var factories []utils.ListenCloser
|
|
factories = append(factories, stream.NewProducerCChain(sc, *cfg))
|
|
for _, v := range cfg.Chains {
|
|
switch v.VMType {
|
|
case consumers.IndexerAVMName:
|
|
p, err := stream.NewProducerChain(sc, *cfg, v.ID, stream.EventTypeDecisions, stream.IndexTypeTransactions, stream.IndexXChain)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
factories = append(factories, p)
|
|
p, err = stream.NewProducerChain(sc, *cfg, v.ID, stream.EventTypeConsensus, stream.IndexTypeVertices, stream.IndexXChain)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
factories = append(factories, p)
|
|
case consumers.IndexerPVMName:
|
|
p, err := stream.NewProducerChain(sc, *cfg, v.ID, stream.EventTypeDecisions, stream.IndexTypeBlocks, stream.IndexPChain)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
factories = append(factories, p)
|
|
}
|
|
}
|
|
|
|
if sc.IsCChainIndex {
|
|
p, err := stream.NewProducerChain(sc, *cfg, cfg.CchainID, stream.EventTypeDecisions, stream.IndexTypeBlocks, stream.IndexCChain)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
factories = append(factories, p)
|
|
}
|
|
|
|
return factories
|
|
}
|
|
|
|
func createEnvCmds(config *cfg.Config, runErr *error) *cobra.Command {
|
|
return &cobra.Command{
|
|
Use: envCmdUse,
|
|
Short: envCmdDesc,
|
|
Long: envCmdDesc,
|
|
Run: func(_ *cobra.Command, _ []string) {
|
|
configBytes, err := json.MarshalIndent(config, "", " ")
|
|
if err != nil {
|
|
*runErr = err
|
|
return
|
|
}
|
|
|
|
fmt.Println(string(configBytes))
|
|
},
|
|
}
|
|
}
|
|
|
|
// runListenCloser runs the ListenCloser until signaled to stop
|
|
func runListenCloser(lc utils.ListenCloser) {
|
|
// Start listening in the background
|
|
go func() {
|
|
if err := lc.Listen(); err != nil {
|
|
log.Fatalln("Daemon listen error:", err.Error())
|
|
}
|
|
}()
|
|
|
|
// Wait for exit signal
|
|
sigCh := make(chan os.Signal, 1)
|
|
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
|
|
<-sigCh
|
|
|
|
// Stop server
|
|
if err := lc.Close(); err != nil {
|
|
log.Fatalln("Daemon shutdown error:", err.Error())
|
|
}
|
|
}
|
|
|
|
// runStreamProcessorManagers returns a cobra command that instantiates and runs
|
|
// a set of stream process managers
|
|
func runStreamProcessorManagers(
|
|
sc *servicesctrl.Control,
|
|
config *cfg.Config,
|
|
runError *error,
|
|
listenCloseFactories []utils.ListenCloser,
|
|
consumerFactories []consumers.ConsumerFactory,
|
|
factoriesChainDB []stream.ProcessorFactoryChainDB,
|
|
factoriesInstDB []stream.ProcessorFactoryInstDB,
|
|
) func(_ *cobra.Command, _ []string) {
|
|
return func(_ *cobra.Command, _ []string) {
|
|
wg := &sync.WaitGroup{}
|
|
|
|
bm, _ := sc.BalanceManager.(*balance.Manager)
|
|
err := bm.Start(sc.IsAccumulateBalanceIndexer)
|
|
if err != nil {
|
|
*runError = err
|
|
return
|
|
}
|
|
defer func() {
|
|
bm.Close()
|
|
}()
|
|
|
|
rh := &rewards.Handler{}
|
|
err = rh.Start(sc)
|
|
if err != nil {
|
|
*runError = err
|
|
return
|
|
}
|
|
defer func() {
|
|
rh.Close()
|
|
}()
|
|
|
|
err = consumers.Bootstrap(sc, config.NetworkID, config.Chains, consumerFactories)
|
|
if err != nil {
|
|
*runError = err
|
|
return
|
|
}
|
|
|
|
sc.BalanceManager.Exec()
|
|
|
|
runningControl := utils.NewRunning()
|
|
|
|
err = consumers.IndexerFactories(sc, config, factoriesChainDB, factoriesInstDB, wg, runningControl)
|
|
if err != nil {
|
|
*runError = err
|
|
return
|
|
}
|
|
|
|
for _, listenCloseFactory := range listenCloseFactories {
|
|
wg.Add(1)
|
|
go func(lc utils.ListenCloser) {
|
|
wg.Done()
|
|
if err := lc.Listen(); err != nil {
|
|
log.Fatalln("Daemon listen error:", err.Error())
|
|
}
|
|
}(listenCloseFactory)
|
|
}
|
|
|
|
// Wait for exit signal
|
|
sigCh := make(chan os.Signal, 1)
|
|
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
|
|
<-sigCh
|
|
|
|
for _, listenCloseFactory := range listenCloseFactories {
|
|
if err := listenCloseFactory.Close(); err != nil {
|
|
log.Println("Daemon shutdown error:", err.Error())
|
|
}
|
|
}
|
|
|
|
runningControl.Close()
|
|
|
|
wg.Wait()
|
|
}
|
|
}
|
|
|
|
type MysqlLogger struct {
|
|
Log logging.Logger
|
|
}
|
|
|
|
func (m *MysqlLogger) Print(v ...interface{}) {
|
|
s := fmt.Sprint(v...)
|
|
m.Log.Warn("mysql",
|
|
zap.String("body", s),
|
|
)
|
|
}
|