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.

370 lines
8.6 KiB

3 years ago
3 years ago
3 years ago
  1. package main
  2. import (
  3. "encoding/hex"
  4. "fmt"
  5. "os"
  6. "os/signal"
  7. "strings"
  8. ethKeystore "github.com/ethereum/go-ethereum/accounts/keystore"
  9. "github.com/ethereum/go-ethereum/crypto"
  10. "github.com/hermeznetwork/hermez-node/config"
  11. dbUtils "github.com/hermeznetwork/hermez-node/db"
  12. "github.com/hermeznetwork/hermez-node/db/historydb"
  13. "github.com/hermeznetwork/hermez-node/db/l2db"
  14. "github.com/hermeznetwork/hermez-node/log"
  15. "github.com/hermeznetwork/hermez-node/node"
  16. "github.com/hermeznetwork/tracerr"
  17. "github.com/iden3/go-iden3-crypto/babyjub"
  18. "github.com/jmoiron/sqlx"
  19. "github.com/urfave/cli/v2"
  20. )
  21. const (
  22. flagCfg = "cfg"
  23. flagMode = "mode"
  24. flagSK = "privatekey"
  25. flagYes = "yes"
  26. flagBlock = "block"
  27. modeSync = "sync"
  28. modeCoord = "coord"
  29. )
  30. func cmdGenBJJ(c *cli.Context) error {
  31. sk := babyjub.NewRandPrivKey()
  32. skBuf := [32]byte(sk)
  33. pk := sk.Public()
  34. fmt.Printf("BJJ = \"0x%s\"\n", pk.String())
  35. fmt.Printf("BJJPrivateKey = \"0x%s\"\n", hex.EncodeToString(skBuf[:]))
  36. return nil
  37. }
  38. func cmdImportKey(c *cli.Context) error {
  39. _cfg, err := parseCli(c)
  40. if err != nil {
  41. return tracerr.Wrap(fmt.Errorf("error parsing flags and config: %w", err))
  42. }
  43. if _cfg.mode != node.ModeCoordinator {
  44. return tracerr.Wrap(fmt.Errorf("importkey must use mode coordinator"))
  45. }
  46. cfg := _cfg.node
  47. scryptN := ethKeystore.StandardScryptN
  48. scryptP := ethKeystore.StandardScryptP
  49. if cfg.Coordinator.Debug.LightScrypt {
  50. scryptN = ethKeystore.LightScryptN
  51. scryptP = ethKeystore.LightScryptP
  52. }
  53. keyStore := ethKeystore.NewKeyStore(cfg.Coordinator.EthClient.Keystore.Path,
  54. scryptN, scryptP)
  55. hexKey := c.String(flagSK)
  56. hexKey = strings.TrimPrefix(hexKey, "0x")
  57. sk, err := crypto.HexToECDSA(hexKey)
  58. if err != nil {
  59. return tracerr.Wrap(err)
  60. }
  61. acc, err := keyStore.ImportECDSA(sk, cfg.Coordinator.EthClient.Keystore.Password)
  62. if err != nil {
  63. return tracerr.Wrap(err)
  64. }
  65. log.Infow("Imported private key", "addr", acc.Address.Hex())
  66. return nil
  67. }
  68. func cmdWipeSQL(c *cli.Context) error {
  69. _cfg, err := parseCli(c)
  70. if err != nil {
  71. return tracerr.Wrap(fmt.Errorf("error parsing flags and config: %w", err))
  72. }
  73. cfg := _cfg.node
  74. yes := c.Bool(flagYes)
  75. if !yes {
  76. fmt.Print("*WARNING* Are you sure you want to delete the SQL DB? [y/N]: ")
  77. var input string
  78. if _, err := fmt.Scanln(&input); err != nil {
  79. return tracerr.Wrap(err)
  80. }
  81. input = strings.ToLower(input)
  82. if !(input == "y" || input == "yes") {
  83. return nil
  84. }
  85. }
  86. db, err := dbUtils.ConnectSQLDB(
  87. cfg.PostgreSQL.PortWrite,
  88. cfg.PostgreSQL.HostWrite,
  89. cfg.PostgreSQL.UserWrite,
  90. cfg.PostgreSQL.PasswordWrite,
  91. cfg.PostgreSQL.NameWrite,
  92. )
  93. if err != nil {
  94. return tracerr.Wrap(err)
  95. }
  96. log.Info("Wiping SQL DB...")
  97. if err := dbUtils.MigrationsDown(db.DB); err != nil {
  98. return tracerr.Wrap(err)
  99. }
  100. return nil
  101. }
  102. func cmdRun(c *cli.Context) error {
  103. cfg, err := parseCli(c)
  104. if err != nil {
  105. return tracerr.Wrap(fmt.Errorf("error parsing flags and config: %w", err))
  106. }
  107. node, err := node.NewNode(cfg.mode, cfg.node)
  108. if err != nil {
  109. return tracerr.Wrap(fmt.Errorf("error starting node: %w", err))
  110. }
  111. node.Start()
  112. stopCh := make(chan interface{})
  113. // catch ^C to send the stop signal
  114. ossig := make(chan os.Signal, 1)
  115. signal.Notify(ossig, os.Interrupt)
  116. const forceStopCount = 3
  117. go func() {
  118. n := 0
  119. for sig := range ossig {
  120. if sig == os.Interrupt {
  121. log.Info("Received Interrupt Signal")
  122. stopCh <- nil
  123. n++
  124. if n == forceStopCount {
  125. log.Fatalf("Received %v Interrupt Signals", forceStopCount)
  126. }
  127. }
  128. }
  129. }()
  130. <-stopCh
  131. node.Stop()
  132. return nil
  133. }
  134. func cmdServeAPI(c *cli.Context) error {
  135. cfgPath := c.String(flagCfg)
  136. cfg, err := config.LoadAPIServer(cfgPath)
  137. if err != nil {
  138. if err := cli.ShowAppHelp(c); err != nil {
  139. panic(err)
  140. }
  141. return tracerr.Wrap(fmt.Errorf("error parsing flags and config: %w", err))
  142. }
  143. node, err := node.NewNode(cfg.mode, cfg.node)
  144. if err != nil {
  145. return tracerr.Wrap(fmt.Errorf("error starting node: %w", err))
  146. }
  147. node.Start()
  148. stopCh := make(chan interface{})
  149. // catch ^C to send the stop signal
  150. ossig := make(chan os.Signal, 1)
  151. signal.Notify(ossig, os.Interrupt)
  152. const forceStopCount = 3
  153. go func() {
  154. n := 0
  155. for sig := range ossig {
  156. if sig == os.Interrupt {
  157. log.Info("Received Interrupt Signal")
  158. stopCh <- nil
  159. n++
  160. if n == forceStopCount {
  161. log.Fatalf("Received %v Interrupt Signals", forceStopCount)
  162. }
  163. }
  164. }
  165. }()
  166. <-stopCh
  167. node.Stop()
  168. return nil
  169. }
  170. func cmdDiscard(c *cli.Context) error {
  171. _cfg, err := parseCli(c)
  172. if err != nil {
  173. return tracerr.Wrap(fmt.Errorf("error parsing flags and config: %w", err))
  174. }
  175. cfg := _cfg.node
  176. blockNum := c.Int64(flagBlock)
  177. log.Infof("Discarding all blocks up to block %v...", blockNum)
  178. dbWrite, err := dbUtils.InitSQLDB(
  179. cfg.PostgreSQL.PortWrite,
  180. cfg.PostgreSQL.HostWrite,
  181. cfg.PostgreSQL.UserWrite,
  182. cfg.PostgreSQL.PasswordWrite,
  183. cfg.PostgreSQL.NameWrite,
  184. )
  185. if err != nil {
  186. return tracerr.Wrap(fmt.Errorf("dbUtils.InitSQLDB: %w", err))
  187. }
  188. var dbRead *sqlx.DB
  189. if cfg.PostgreSQL.HostRead == "" {
  190. dbRead = dbWrite
  191. } else if cfg.PostgreSQL.HostRead == cfg.PostgreSQL.HostWrite {
  192. return tracerr.Wrap(fmt.Errorf(
  193. "PostgreSQL.HostRead and PostgreSQL.HostWrite must be different",
  194. ))
  195. } else {
  196. dbRead, err = dbUtils.InitSQLDB(
  197. cfg.PostgreSQL.PortRead,
  198. cfg.PostgreSQL.HostRead,
  199. cfg.PostgreSQL.UserRead,
  200. cfg.PostgreSQL.PasswordRead,
  201. cfg.PostgreSQL.NameRead,
  202. )
  203. if err != nil {
  204. return tracerr.Wrap(fmt.Errorf("dbUtils.InitSQLDB: %w", err))
  205. }
  206. }
  207. historyDB := historydb.NewHistoryDB(dbRead, dbWrite, nil)
  208. if err := historyDB.Reorg(blockNum); err != nil {
  209. return tracerr.Wrap(fmt.Errorf("historyDB.Reorg: %w", err))
  210. }
  211. batchNum, err := historyDB.GetLastBatchNum()
  212. if err != nil {
  213. return tracerr.Wrap(fmt.Errorf("historyDB.GetLastBatchNum: %w", err))
  214. }
  215. l2DB := l2db.NewL2DB(
  216. dbRead, dbWrite,
  217. cfg.Coordinator.L2DB.SafetyPeriod,
  218. cfg.Coordinator.L2DB.MaxTxs,
  219. cfg.Coordinator.L2DB.MinFeeUSD,
  220. cfg.Coordinator.L2DB.TTL.Duration,
  221. nil,
  222. )
  223. if err := l2DB.Reorg(batchNum); err != nil {
  224. return tracerr.Wrap(fmt.Errorf("l2DB.Reorg: %w", err))
  225. }
  226. return nil
  227. }
  228. // Config is the configuration of the hermez node execution
  229. type Config struct {
  230. mode node.Mode
  231. node *config.Node
  232. }
  233. func parseCli(c *cli.Context) (*Config, error) {
  234. cfg, err := getConfig(c)
  235. if err != nil {
  236. if err := cli.ShowAppHelp(c); err != nil {
  237. panic(err)
  238. }
  239. return nil, tracerr.Wrap(err)
  240. }
  241. return cfg, nil
  242. }
  243. func getConfig(c *cli.Context) (*Config, error) {
  244. var cfg Config
  245. mode := c.String(flagMode)
  246. nodeCfgPath := c.String(flagCfg)
  247. var err error
  248. switch mode {
  249. case modeSync:
  250. cfg.mode = node.ModeSynchronizer
  251. cfg.node, err = config.LoadNode(nodeCfgPath)
  252. if err != nil {
  253. return nil, tracerr.Wrap(err)
  254. }
  255. case modeCoord:
  256. cfg.mode = node.ModeCoordinator
  257. cfg.node, err = config.LoadCoordinator(nodeCfgPath)
  258. if err != nil {
  259. return nil, tracerr.Wrap(err)
  260. }
  261. default:
  262. return nil, tracerr.Wrap(fmt.Errorf("invalid mode \"%v\"", mode))
  263. }
  264. return &cfg, nil
  265. }
  266. func main() {
  267. app := cli.NewApp()
  268. app.Name = "hermez-node"
  269. app.Version = "0.1.0-alpha"
  270. app.Flags = []cli.Flag{
  271. &cli.StringFlag{
  272. Name: flagMode,
  273. Usage: fmt.Sprintf("Set node `MODE` (can be \"%v\" or \"%v\")", modeSync, modeCoord),
  274. Required: true,
  275. },
  276. &cli.StringFlag{
  277. Name: flagCfg,
  278. Usage: "Node configuration `FILE`",
  279. Required: true,
  280. },
  281. }
  282. app.Commands = []*cli.Command{
  283. {
  284. Name: "importkey",
  285. Aliases: []string{},
  286. Usage: "Import ethereum private key",
  287. Action: cmdImportKey,
  288. Flags: []cli.Flag{
  289. &cli.StringFlag{
  290. Name: flagSK,
  291. Usage: "ethereum `PRIVATE_KEY` in hex",
  292. Required: true,
  293. }},
  294. },
  295. {
  296. Name: "genbjj",
  297. Aliases: []string{},
  298. Usage: "Generate a new BabyJubJub key",
  299. Action: cmdGenBJJ,
  300. },
  301. {
  302. Name: "wipesql",
  303. Aliases: []string{},
  304. Usage: "Wipe the SQL DB (HistoryDB and L2DB), " +
  305. "leaving the DB in a clean state",
  306. Action: cmdWipeSQL,
  307. Flags: []cli.Flag{
  308. &cli.BoolFlag{
  309. Name: flagYes,
  310. Usage: "automatic yes to the prompt",
  311. Required: false,
  312. }},
  313. },
  314. {
  315. Name: "run",
  316. Aliases: []string{},
  317. Usage: "Run the hermez-node in the indicated mode",
  318. Action: cmdRun,
  319. },
  320. {
  321. Name: "serveapi",
  322. Aliases: []string{},
  323. Usage: "Serve the API only",
  324. Action: cmdServeAPI,
  325. },
  326. {
  327. Name: "discard",
  328. Aliases: []string{},
  329. Usage: "Discard blocks up to a specified block number",
  330. Action: cmdDiscard,
  331. Flags: []cli.Flag{
  332. &cli.Int64Flag{
  333. Name: flagBlock,
  334. Usage: "last block number to keep",
  335. Required: false,
  336. }},
  337. },
  338. }
  339. err := app.Run(os.Args)
  340. if err != nil {
  341. fmt.Printf("\nError: %v\n", tracerr.Sprint(err))
  342. os.Exit(1)
  343. }
  344. }