You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

509 lines
13 KiB

Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
3 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
3 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
3 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
3 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
3 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
3 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
3 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
3 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
3 years ago
  1. package main
  2. import (
  3. "encoding/hex"
  4. "fmt"
  5. "os"
  6. "os/signal"
  7. "path"
  8. "strings"
  9. ethKeystore "github.com/ethereum/go-ethereum/accounts/keystore"
  10. "github.com/ethereum/go-ethereum/crypto"
  11. "github.com/hermeznetwork/hermez-node/common"
  12. "github.com/hermeznetwork/hermez-node/config"
  13. dbUtils "github.com/hermeznetwork/hermez-node/db"
  14. "github.com/hermeznetwork/hermez-node/db/historydb"
  15. "github.com/hermeznetwork/hermez-node/db/kvdb"
  16. "github.com/hermeznetwork/hermez-node/db/l2db"
  17. "github.com/hermeznetwork/hermez-node/log"
  18. "github.com/hermeznetwork/hermez-node/node"
  19. "github.com/hermeznetwork/tracerr"
  20. "github.com/iden3/go-iden3-crypto/babyjub"
  21. "github.com/jmoiron/sqlx"
  22. "github.com/urfave/cli/v2"
  23. )
  24. const (
  25. flagCfg = "cfg"
  26. flagMode = "mode"
  27. flagSK = "privatekey"
  28. flagYes = "yes"
  29. flagBlock = "block"
  30. modeSync = "sync"
  31. modeCoord = "coord"
  32. )
  33. var (
  34. // version represents the program based on the git tag
  35. version = "v0.1.0"
  36. // commit represents the program based on the git commit
  37. commit = "dev"
  38. // date represents the date of application was built
  39. date = ""
  40. )
  41. func cmdVersion(c *cli.Context) error {
  42. fmt.Printf("Version = \"%v\"\n", version)
  43. fmt.Printf("Build = \"%v\"\n", commit)
  44. fmt.Printf("Date = \"%v\"\n", date)
  45. return nil
  46. }
  47. func cmdGenBJJ(c *cli.Context) error {
  48. sk := babyjub.NewRandPrivKey()
  49. skBuf := [32]byte(sk)
  50. pk := sk.Public()
  51. fmt.Printf("BJJ = \"0x%s\"\n", pk.String())
  52. fmt.Printf("BJJPrivateKey = \"0x%s\"\n", hex.EncodeToString(skBuf[:]))
  53. return nil
  54. }
  55. func cmdImportKey(c *cli.Context) error {
  56. _cfg, err := parseCli(c)
  57. if err != nil {
  58. return tracerr.Wrap(fmt.Errorf("error parsing flags and config: %w", err))
  59. }
  60. if _cfg.mode != node.ModeCoordinator {
  61. return tracerr.Wrap(fmt.Errorf("importkey must use mode coordinator"))
  62. }
  63. cfg := _cfg.node
  64. scryptN := ethKeystore.StandardScryptN
  65. scryptP := ethKeystore.StandardScryptP
  66. if cfg.Coordinator.Debug.LightScrypt {
  67. scryptN = ethKeystore.LightScryptN
  68. scryptP = ethKeystore.LightScryptP
  69. }
  70. keyStore := ethKeystore.NewKeyStore(cfg.Coordinator.EthClient.Keystore.Path,
  71. scryptN, scryptP)
  72. hexKey := c.String(flagSK)
  73. hexKey = strings.TrimPrefix(hexKey, "0x")
  74. sk, err := crypto.HexToECDSA(hexKey)
  75. if err != nil {
  76. return tracerr.Wrap(err)
  77. }
  78. acc, err := keyStore.ImportECDSA(sk, cfg.Coordinator.EthClient.Keystore.Password)
  79. if err != nil {
  80. return tracerr.Wrap(err)
  81. }
  82. log.Infow("Imported private key", "addr", acc.Address.Hex())
  83. return nil
  84. }
  85. func resetStateDBs(cfg *Config, batchNum common.BatchNum) error {
  86. log.Infof("Reset Synchronizer StateDB to batchNum %v...", batchNum)
  87. // Manually make a checkpoint from batchNum to current to force current
  88. // to be a valid checkpoint. This is useful because in case of a
  89. // crash, current can be corrupted and the first thing that
  90. // `kvdb.NewKVDB` does is read the current checkpoint, which wouldn't
  91. // succeed in case of corruption.
  92. dbPath := cfg.node.StateDB.Path
  93. source := path.Join(dbPath, fmt.Sprintf("%s%d", kvdb.PathBatchNum, batchNum))
  94. current := path.Join(dbPath, kvdb.PathCurrent)
  95. last := path.Join(dbPath, kvdb.PathLast)
  96. if err := os.RemoveAll(last); err != nil {
  97. return tracerr.Wrap(fmt.Errorf("os.RemoveAll: %w", err))
  98. }
  99. if batchNum == 0 {
  100. if err := os.RemoveAll(current); err != nil {
  101. return tracerr.Wrap(fmt.Errorf("os.RemoveAll: %w", err))
  102. }
  103. } else {
  104. if err := kvdb.PebbleMakeCheckpoint(source, current); err != nil {
  105. return tracerr.Wrap(fmt.Errorf("kvdb.PebbleMakeCheckpoint: %w", err))
  106. }
  107. }
  108. db, err := kvdb.NewKVDB(kvdb.Config{
  109. Path: dbPath,
  110. NoGapsCheck: true,
  111. NoLast: true,
  112. })
  113. if err != nil {
  114. return tracerr.Wrap(fmt.Errorf("kvdb.NewKVDB: %w", err))
  115. }
  116. if err := db.Reset(batchNum); err != nil {
  117. return tracerr.Wrap(fmt.Errorf("db.Reset: %w", err))
  118. }
  119. if cfg.mode == node.ModeCoordinator {
  120. log.Infof("Wipe Coordinator StateDBs...")
  121. // We wipe the Coordinator StateDBs entirely (by deleting
  122. // current and resetting to batchNum 0) because the Coordinator
  123. // StateDBs are always reset from Synchronizer when the
  124. // coordinator pipeline starts.
  125. dbPath := cfg.node.Coordinator.TxSelector.Path
  126. current := path.Join(dbPath, kvdb.PathCurrent)
  127. if err := os.RemoveAll(current); err != nil {
  128. return tracerr.Wrap(fmt.Errorf("os.RemoveAll: %w", err))
  129. }
  130. db, err := kvdb.NewKVDB(kvdb.Config{
  131. Path: dbPath,
  132. NoGapsCheck: true,
  133. NoLast: true,
  134. })
  135. if err != nil {
  136. return tracerr.Wrap(fmt.Errorf("kvdb.NewKVDB: %w", err))
  137. }
  138. if err := db.Reset(0); err != nil {
  139. return tracerr.Wrap(fmt.Errorf("db.Reset: %w", err))
  140. }
  141. dbPath = cfg.node.Coordinator.BatchBuilder.Path
  142. current = path.Join(dbPath, kvdb.PathCurrent)
  143. if err := os.RemoveAll(current); err != nil {
  144. return tracerr.Wrap(fmt.Errorf("os.RemoveAll: %w", err))
  145. }
  146. db, err = kvdb.NewKVDB(kvdb.Config{
  147. Path: dbPath,
  148. NoGapsCheck: true,
  149. NoLast: true,
  150. })
  151. if err != nil {
  152. return tracerr.Wrap(fmt.Errorf("statedb.NewKVDB: %w", err))
  153. }
  154. if err := db.Reset(0); err != nil {
  155. return tracerr.Wrap(fmt.Errorf("db.Reset: %w", err))
  156. }
  157. }
  158. return nil
  159. }
  160. func cmdWipeSQL(c *cli.Context) error {
  161. _cfg, err := parseCli(c)
  162. if err != nil {
  163. return tracerr.Wrap(fmt.Errorf("error parsing flags and config: %w", err))
  164. }
  165. cfg := _cfg.node
  166. yes := c.Bool(flagYes)
  167. if !yes {
  168. fmt.Print("*WARNING* Are you sure you want to delete " +
  169. "the SQL DB and StateDBs? [y/N]: ")
  170. var input string
  171. if _, err := fmt.Scanln(&input); err != nil {
  172. return tracerr.Wrap(err)
  173. }
  174. input = strings.ToLower(input)
  175. if !(input == "y" || input == "yes") {
  176. return nil
  177. }
  178. }
  179. db, err := dbUtils.ConnectSQLDB(
  180. cfg.PostgreSQL.PortWrite,
  181. cfg.PostgreSQL.HostWrite,
  182. cfg.PostgreSQL.UserWrite,
  183. cfg.PostgreSQL.PasswordWrite,
  184. cfg.PostgreSQL.NameWrite,
  185. )
  186. if err != nil {
  187. return tracerr.Wrap(err)
  188. }
  189. log.Info("Wiping SQL DB...")
  190. if err := dbUtils.MigrationsDown(db.DB); err != nil {
  191. return tracerr.Wrap(fmt.Errorf("dbUtils.MigrationsDown: %w", err))
  192. }
  193. log.Info("Wiping StateDBs...")
  194. if err := resetStateDBs(_cfg, 0); err != nil {
  195. return tracerr.Wrap(fmt.Errorf("resetStateDBs: %w", err))
  196. }
  197. return nil
  198. }
  199. func waitSigInt() {
  200. stopCh := make(chan interface{})
  201. // catch ^C to send the stop signal
  202. ossig := make(chan os.Signal, 1)
  203. signal.Notify(ossig, os.Interrupt)
  204. const forceStopCount = 3
  205. go func() {
  206. n := 0
  207. for sig := range ossig {
  208. if sig == os.Interrupt {
  209. log.Info("Received Interrupt Signal")
  210. stopCh <- nil
  211. n++
  212. if n == forceStopCount {
  213. log.Fatalf("Received %v Interrupt Signals", forceStopCount)
  214. }
  215. }
  216. }
  217. }()
  218. <-stopCh
  219. }
  220. func cmdRun(c *cli.Context) error {
  221. cfg, err := parseCli(c)
  222. if err != nil {
  223. return tracerr.Wrap(fmt.Errorf("error parsing flags and config: %w", err))
  224. }
  225. node, err := node.NewNode(cfg.mode, cfg.node)
  226. if err != nil {
  227. return tracerr.Wrap(fmt.Errorf("error starting node: %w", err))
  228. }
  229. node.Start()
  230. waitSigInt()
  231. node.Stop()
  232. return nil
  233. }
  234. func cmdServeAPI(c *cli.Context) error {
  235. cfg, err := parseCliAPIServer(c)
  236. if err != nil {
  237. return tracerr.Wrap(fmt.Errorf("error parsing flags and config: %w", err))
  238. }
  239. srv, err := node.NewAPIServer(cfg.mode, cfg.server)
  240. if err != nil {
  241. return tracerr.Wrap(fmt.Errorf("error starting api server: %w", err))
  242. }
  243. srv.Start()
  244. waitSigInt()
  245. srv.Stop()
  246. return nil
  247. }
  248. func cmdDiscard(c *cli.Context) error {
  249. _cfg, err := parseCli(c)
  250. if err != nil {
  251. return tracerr.Wrap(fmt.Errorf("error parsing flags and config: %w", err))
  252. }
  253. cfg := _cfg.node
  254. blockNum := c.Int64(flagBlock)
  255. log.Infof("Discarding all blocks up to block %v...", blockNum)
  256. dbWrite, err := dbUtils.InitSQLDB(
  257. cfg.PostgreSQL.PortWrite,
  258. cfg.PostgreSQL.HostWrite,
  259. cfg.PostgreSQL.UserWrite,
  260. cfg.PostgreSQL.PasswordWrite,
  261. cfg.PostgreSQL.NameWrite,
  262. )
  263. if err != nil {
  264. return tracerr.Wrap(fmt.Errorf("dbUtils.InitSQLDB: %w", err))
  265. }
  266. var dbRead *sqlx.DB
  267. if cfg.PostgreSQL.HostRead == "" {
  268. dbRead = dbWrite
  269. } else if cfg.PostgreSQL.HostRead == cfg.PostgreSQL.HostWrite {
  270. return tracerr.Wrap(fmt.Errorf(
  271. "PostgreSQL.HostRead and PostgreSQL.HostWrite must be different",
  272. ))
  273. } else {
  274. dbRead, err = dbUtils.InitSQLDB(
  275. cfg.PostgreSQL.PortRead,
  276. cfg.PostgreSQL.HostRead,
  277. cfg.PostgreSQL.UserRead,
  278. cfg.PostgreSQL.PasswordRead,
  279. cfg.PostgreSQL.NameRead,
  280. )
  281. if err != nil {
  282. return tracerr.Wrap(fmt.Errorf("dbUtils.InitSQLDB: %w", err))
  283. }
  284. }
  285. historyDB := historydb.NewHistoryDB(dbRead, dbWrite, nil)
  286. if err := historyDB.Reorg(blockNum); err != nil {
  287. return tracerr.Wrap(fmt.Errorf("historyDB.Reorg: %w", err))
  288. }
  289. batchNum, err := historyDB.GetLastBatchNum()
  290. if err != nil {
  291. return tracerr.Wrap(fmt.Errorf("historyDB.GetLastBatchNum: %w", err))
  292. }
  293. l2DB := l2db.NewL2DB(
  294. dbRead, dbWrite,
  295. cfg.Coordinator.L2DB.SafetyPeriod,
  296. cfg.Coordinator.L2DB.MaxTxs,
  297. cfg.Coordinator.L2DB.MinFeeUSD,
  298. cfg.Coordinator.L2DB.MaxFeeUSD,
  299. cfg.Coordinator.L2DB.TTL.Duration,
  300. nil,
  301. )
  302. if err := l2DB.Reorg(batchNum); err != nil {
  303. return tracerr.Wrap(fmt.Errorf("l2DB.Reorg: %w", err))
  304. }
  305. log.Info("Resetting StateDBs...")
  306. if err := resetStateDBs(_cfg, batchNum); err != nil {
  307. return tracerr.Wrap(fmt.Errorf("resetStateDBs: %w", err))
  308. }
  309. return nil
  310. }
  311. // Config is the configuration of the hermez node execution
  312. type Config struct {
  313. mode node.Mode
  314. node *config.Node
  315. }
  316. func parseCli(c *cli.Context) (*Config, error) {
  317. cfg, err := getConfig(c)
  318. if err != nil {
  319. if err := cli.ShowAppHelp(c); err != nil {
  320. panic(err)
  321. }
  322. return nil, tracerr.Wrap(err)
  323. }
  324. return cfg, nil
  325. }
  326. func getConfig(c *cli.Context) (*Config, error) {
  327. var cfg Config
  328. mode := c.String(flagMode)
  329. nodeCfgPath := c.String(flagCfg)
  330. var err error
  331. switch mode {
  332. case modeSync:
  333. cfg.mode = node.ModeSynchronizer
  334. cfg.node, err = config.LoadNode(nodeCfgPath, false)
  335. if err != nil {
  336. return nil, tracerr.Wrap(err)
  337. }
  338. case modeCoord:
  339. cfg.mode = node.ModeCoordinator
  340. cfg.node, err = config.LoadNode(nodeCfgPath, true)
  341. if err != nil {
  342. return nil, tracerr.Wrap(err)
  343. }
  344. default:
  345. return nil, tracerr.Wrap(fmt.Errorf("invalid mode \"%v\"", mode))
  346. }
  347. return &cfg, nil
  348. }
  349. // ConfigAPIServer is the configuration of the api server execution
  350. type ConfigAPIServer struct {
  351. mode node.Mode
  352. server *config.APIServer
  353. }
  354. func parseCliAPIServer(c *cli.Context) (*ConfigAPIServer, error) {
  355. cfg, err := getConfigAPIServer(c)
  356. if err != nil {
  357. if err := cli.ShowAppHelp(c); err != nil {
  358. panic(err)
  359. }
  360. return nil, tracerr.Wrap(err)
  361. }
  362. return cfg, nil
  363. }
  364. func getConfigAPIServer(c *cli.Context) (*ConfigAPIServer, error) {
  365. var cfg ConfigAPIServer
  366. mode := c.String(flagMode)
  367. nodeCfgPath := c.String(flagCfg)
  368. var err error
  369. switch mode {
  370. case modeSync:
  371. cfg.mode = node.ModeSynchronizer
  372. cfg.server, err = config.LoadAPIServer(nodeCfgPath, false)
  373. if err != nil {
  374. return nil, tracerr.Wrap(err)
  375. }
  376. case modeCoord:
  377. cfg.mode = node.ModeCoordinator
  378. cfg.server, err = config.LoadAPIServer(nodeCfgPath, true)
  379. if err != nil {
  380. return nil, tracerr.Wrap(err)
  381. }
  382. default:
  383. return nil, tracerr.Wrap(fmt.Errorf("invalid mode \"%v\"", mode))
  384. }
  385. return &cfg, nil
  386. }
  387. func main() {
  388. app := cli.NewApp()
  389. app.Name = "hermez-node"
  390. app.Version = version
  391. flags := []cli.Flag{
  392. &cli.StringFlag{
  393. Name: flagMode,
  394. Usage: fmt.Sprintf("Set node `MODE` (can be \"%v\" or \"%v\")", modeSync, modeCoord),
  395. Required: true,
  396. },
  397. &cli.StringFlag{
  398. Name: flagCfg,
  399. Usage: "Node configuration `FILE`",
  400. Required: true,
  401. },
  402. }
  403. app.Commands = []*cli.Command{
  404. {
  405. Name: "version",
  406. Aliases: []string{},
  407. Usage: "Show the application version and build",
  408. Action: cmdVersion,
  409. },
  410. {
  411. Name: "importkey",
  412. Aliases: []string{},
  413. Usage: "Import ethereum private key",
  414. Action: cmdImportKey,
  415. Flags: append(flags,
  416. &cli.StringFlag{
  417. Name: flagSK,
  418. Usage: "ethereum `PRIVATE_KEY` in hex",
  419. Required: true,
  420. }),
  421. },
  422. {
  423. Name: "genbjj",
  424. Aliases: []string{},
  425. Usage: "Generate a new BabyJubJub key",
  426. Action: cmdGenBJJ,
  427. },
  428. {
  429. Name: "wipesql",
  430. Aliases: []string{},
  431. Usage: "Wipe the SQL DB (HistoryDB and L2DB) and the StateDBs, " +
  432. "leaving the DB in a clean state",
  433. Action: cmdWipeSQL,
  434. Flags: append(flags,
  435. &cli.BoolFlag{
  436. Name: flagYes,
  437. Usage: "automatic yes to the prompt",
  438. Required: false,
  439. }),
  440. },
  441. {
  442. Name: "run",
  443. Aliases: []string{},
  444. Usage: "Run the hermez-node in the indicated mode",
  445. Action: cmdRun,
  446. Flags: flags,
  447. },
  448. {
  449. Name: "serveapi",
  450. Aliases: []string{},
  451. Usage: "Serve the API only",
  452. Action: cmdServeAPI,
  453. Flags: flags,
  454. },
  455. {
  456. Name: "discard",
  457. Aliases: []string{},
  458. Usage: "Discard blocks up to a specified block number",
  459. Action: cmdDiscard,
  460. Flags: append(flags,
  461. &cli.Int64Flag{
  462. Name: flagBlock,
  463. Usage: "last block number to keep",
  464. Required: false,
  465. }),
  466. },
  467. }
  468. err := app.Run(os.Args)
  469. if err != nil {
  470. fmt.Printf("\nError: %v\n", tracerr.Sprint(err))
  471. os.Exit(1)
  472. }
  473. }