diff --git a/cli/node/main.go b/cli/node/main.go index 0138640..b95e141 100644 --- a/cli/node/main.go +++ b/cli/node/main.go @@ -143,6 +143,42 @@ func cmdRun(c *cli.Context) error { return nil } +func cmdServeAPI(c *cli.Context) error { + cfg, err := parseCli(c) + if err != nil { + return tracerr.Wrap(fmt.Errorf("error parsing flags and config: %w", err)) + } + node, err := node.NewNode(cfg.mode, cfg.node) + if err != nil { + return tracerr.Wrap(fmt.Errorf("error starting node: %w", err)) + } + node.Start() + + stopCh := make(chan interface{}) + + // catch ^C to send the stop signal + ossig := make(chan os.Signal, 1) + signal.Notify(ossig, os.Interrupt) + const forceStopCount = 3 + go func() { + n := 0 + for sig := range ossig { + if sig == os.Interrupt { + log.Info("Received Interrupt Signal") + stopCh <- nil + n++ + if n == forceStopCount { + log.Fatalf("Received %v Interrupt Signals", forceStopCount) + } + } + } + }() + <-stopCh + node.Stop() + + return nil +} + func cmdDiscard(c *cli.Context) error { _cfg, err := parseCli(c) if err != nil { @@ -304,6 +340,12 @@ func main() { Usage: "Run the hermez-node in the indicated mode", Action: cmdRun, }, + { + Name: "serveapi", + Aliases: []string{}, + Usage: "Serve the API only", + Action: cmdServeAPI, + }, { Name: "discard", Aliases: []string{}, diff --git a/config/config.go b/config/config.go index 63158c7..a7ad88a 100644 --- a/config/config.go +++ b/config/config.go @@ -44,6 +44,13 @@ type ForgeBatchGasCost struct { L2Tx uint64 `validate:"required"` } +// CoordinatorAPI specifies the configuration parameters of the API in mode +// coordinator +type CoordinatorAPI struct { + // Coordinator enables the coordinator API endpoints + Coordinator bool +} + // Coordinator is the coordinator specific configuration. type Coordinator struct { // ForgerAddress is the address under which this coordinator is forging @@ -193,10 +200,7 @@ type Coordinator struct { // ForgeBatch transaction. ForgeBatchGasCost ForgeBatchGasCost `validate:"required"` } `validate:"required"` - API struct { - // Coordinator enables the coordinator API endpoints - Coordinator bool - } `validate:"required"` + API CoordinatorAPI `validate:"required"` Debug struct { // BatchPath if set, specifies the path where batchInfo is stored // in JSON in every step/update of the pipeline @@ -211,6 +215,64 @@ type Coordinator struct { } } +// NodeAPI specifies the configuration parameters of the API +type NodeAPI struct { + // Address where the API will listen if set + Address string + // Explorer enables the Explorer API endpoints + Explorer bool + // UpdateMetricsInterval is the interval between updates of the + // API metrics + UpdateMetricsInterval Duration + // UpdateRecommendedFeeInterval is the interval between updates of the + // recommended fees + UpdateRecommendedFeeInterval Duration + // Maximum concurrent connections allowed between API and SQL + MaxSQLConnections int `validate:"required"` + // SQLConnectionTimeout is the maximum amount of time that an API request + // can wait to stablish a SQL connection + SQLConnectionTimeout Duration +} + +// It's possible to use diferentiated SQL connections for read/write. +// If the read configuration is not provided, the write one it's going to be used +// for both reads and writes +type PostgreSQL struct { + // Port of the PostgreSQL write server + PortWrite int `validate:"required"` + // Host of the PostgreSQL write server + HostWrite string `validate:"required"` + // User of the PostgreSQL write server + UserWrite string `validate:"required"` + // Password of the PostgreSQL write server + PasswordWrite string `validate:"required"` + // Name of the PostgreSQL write server database + NameWrite string `validate:"required"` + // Port of the PostgreSQL read server + PortRead int + // Host of the PostgreSQL read server + HostRead string + // User of the PostgreSQL read server + UserRead string + // Password of the PostgreSQL read server + PasswordRead string + // Name of the PostgreSQL read server database + NameRead string +} + +// NodeDebug specifies debug configuration parameters +type NodeDebug struct { + // APIAddress is the address where the debugAPI will listen if + // set + APIAddress string + // MeddlerLogs enables meddler debug mode, where unused columns and struct + // fields will be logged + MeddlerLogs bool + // GinDebugMode sets Gin-Gonic (the web framework) to run in + // debug mode + GinDebugMode bool +} + // Node is the hermez node configuration. type Node struct { PriceUpdater struct { @@ -227,32 +289,8 @@ type Node struct { // Keep is the number of checkpoints to keep Keep int `validate:"required"` } `validate:"required"` - // It's possible to use diferentiated SQL connections for read/write. - // If the read configuration is not provided, the write one it's going to be used - // for both reads and writes - PostgreSQL struct { - // Port of the PostgreSQL write server - PortWrite int `validate:"required"` - // Host of the PostgreSQL write server - HostWrite string `validate:"required"` - // User of the PostgreSQL write server - UserWrite string `validate:"required"` - // Password of the PostgreSQL write server - PasswordWrite string `validate:"required"` - // Name of the PostgreSQL write server database - NameWrite string `validate:"required"` - // Port of the PostgreSQL read server - PortRead int - // Host of the PostgreSQL read server - HostRead string - // User of the PostgreSQL read server - UserRead string - // Password of the PostgreSQL read server - PasswordRead string - // Name of the PostgreSQL read server database - NameRead string - } `validate:"required"` - Web3 struct { + PostgreSQL PostgreSQL `validate:"required"` + Web3 struct { // URL is the URL of the web3 ethereum-node RPC server URL string `validate:"required"` } `validate:"required"` @@ -282,37 +320,23 @@ type Node struct { // TokenHEZ address TokenHEZName string `validate:"required"` } `validate:"required"` - API struct { - // Address where the API will listen if set - Address string - // Explorer enables the Explorer API endpoints - Explorer bool - // UpdateMetricsInterval is the interval between updates of the - // API metrics - UpdateMetricsInterval Duration - // UpdateRecommendedFeeInterval is the interval between updates of the - // recommended fees - UpdateRecommendedFeeInterval Duration - // Maximum concurrent connections allowed between API and SQL - MaxSQLConnections int `validate:"required"` - // SQLConnectionTimeout is the maximum amount of time that an API request - // can wait to stablish a SQL connection - SQLConnectionTimeout Duration - } `validate:"required"` - Debug struct { - // APIAddress is the address where the debugAPI will listen if - // set - APIAddress string - // MeddlerLogs enables meddler debug mode, where unused columns and struct - // fields will be logged - MeddlerLogs bool - // GinDebugMode sets Gin-Gonic (the web framework) to run in - // debug mode - GinDebugMode bool - } + API NodeAPI `validate:"required"` + Debug NodeDebug `validate:"required"` Coordinator Coordinator `validate:"-"` } +type APIServer struct { + API NodeAPI `validate:"required"` + PostgreSQL PostgreSQL `validate:"required"` + Coordinator struct { + API struct { + // Coordinator enables the coordinator API endpoints + Coordinator bool + } `validate:"required"` + } `validate:"required"` + Debug NodeDebug `validate:"required"` +} + // Load loads a generic config. func Load(path string, cfg interface{}) error { bs, err := ioutil.ReadFile(path) //nolint:gosec