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.

306 lines
7.1 KiB

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