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.

326 lines
7.8 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/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 cmdDiscard(c *cli.Context) error {
  135. _cfg, err := parseCli(c)
  136. if err != nil {
  137. return tracerr.Wrap(fmt.Errorf("error parsing flags and config: %w", err))
  138. }
  139. cfg := _cfg.node
  140. blockNum := c.Int64(flagBlock)
  141. log.Infof("Discarding all blocks up to block %v...", blockNum)
  142. dbWrite, err := dbUtils.InitSQLDB(
  143. cfg.PostgreSQL.PortWrite,
  144. cfg.PostgreSQL.HostWrite,
  145. cfg.PostgreSQL.UserWrite,
  146. cfg.PostgreSQL.PasswordWrite,
  147. cfg.PostgreSQL.NameWrite,
  148. )
  149. if err != nil {
  150. return tracerr.Wrap(fmt.Errorf("dbUtils.InitSQLDB: %w", err))
  151. }
  152. var dbRead *sqlx.DB
  153. if cfg.PostgreSQL.HostRead == "" {
  154. dbRead = dbWrite
  155. } else if cfg.PostgreSQL.HostRead == cfg.PostgreSQL.HostWrite {
  156. return tracerr.Wrap(fmt.Errorf(
  157. "PostgreSQL.HostRead and PostgreSQL.HostWrite must be different",
  158. ))
  159. } else {
  160. dbRead, err = dbUtils.InitSQLDB(
  161. cfg.PostgreSQL.PortRead,
  162. cfg.PostgreSQL.HostRead,
  163. cfg.PostgreSQL.UserRead,
  164. cfg.PostgreSQL.PasswordRead,
  165. cfg.PostgreSQL.NameRead,
  166. )
  167. if err != nil {
  168. return tracerr.Wrap(fmt.Errorf("dbUtils.InitSQLDB: %w", err))
  169. }
  170. }
  171. historyDB := historydb.NewHistoryDB(dbRead, dbWrite, nil)
  172. if err := historyDB.Reorg(blockNum); err != nil {
  173. return tracerr.Wrap(fmt.Errorf("historyDB.Reorg: %w", err))
  174. }
  175. batchNum, err := historyDB.GetLastBatchNum()
  176. if err != nil {
  177. return tracerr.Wrap(fmt.Errorf("historyDB.GetLastBatchNum: %w", err))
  178. }
  179. l2DB := l2db.NewL2DB(
  180. dbRead, dbWrite,
  181. cfg.Coordinator.L2DB.SafetyPeriod,
  182. cfg.Coordinator.L2DB.MaxTxs,
  183. cfg.Coordinator.L2DB.MinFeeUSD,
  184. cfg.Coordinator.L2DB.TTL.Duration,
  185. nil,
  186. )
  187. if err := l2DB.Reorg(batchNum); err != nil {
  188. return tracerr.Wrap(fmt.Errorf("l2DB.Reorg: %w", err))
  189. }
  190. return nil
  191. }
  192. // Config is the configuration of the hermez node execution
  193. type Config struct {
  194. mode node.Mode
  195. node *config.Node
  196. }
  197. func parseCli(c *cli.Context) (*Config, error) {
  198. cfg, err := getConfig(c)
  199. if err != nil {
  200. if err := cli.ShowAppHelp(c); err != nil {
  201. panic(err)
  202. }
  203. return nil, tracerr.Wrap(err)
  204. }
  205. return cfg, nil
  206. }
  207. func getConfig(c *cli.Context) (*Config, error) {
  208. var cfg Config
  209. mode := c.String(flagMode)
  210. nodeCfgPath := c.String(flagCfg)
  211. if nodeCfgPath == "" {
  212. return nil, tracerr.Wrap(fmt.Errorf("required flag \"%v\" not set", flagCfg))
  213. }
  214. var err error
  215. switch mode {
  216. case modeSync:
  217. cfg.mode = node.ModeSynchronizer
  218. cfg.node, err = config.LoadNode(nodeCfgPath)
  219. if err != nil {
  220. return nil, tracerr.Wrap(err)
  221. }
  222. case modeCoord:
  223. cfg.mode = node.ModeCoordinator
  224. cfg.node, err = config.LoadCoordinator(nodeCfgPath)
  225. if err != nil {
  226. return nil, tracerr.Wrap(err)
  227. }
  228. default:
  229. return nil, tracerr.Wrap(fmt.Errorf("invalid mode \"%v\"", mode))
  230. }
  231. return &cfg, nil
  232. }
  233. func main() {
  234. app := cli.NewApp()
  235. app.Name = "hermez-node"
  236. app.Version = "0.1.0-alpha"
  237. app.Flags = []cli.Flag{
  238. &cli.StringFlag{
  239. Name: flagMode,
  240. Usage: fmt.Sprintf("Set node `MODE` (can be \"%v\" or \"%v\")", modeSync, modeCoord),
  241. Required: true,
  242. },
  243. &cli.StringFlag{
  244. Name: flagCfg,
  245. Usage: "Node configuration `FILE`",
  246. Required: true,
  247. },
  248. }
  249. app.Commands = []*cli.Command{
  250. {
  251. Name: "importkey",
  252. Aliases: []string{},
  253. Usage: "Import ethereum private key",
  254. Action: cmdImportKey,
  255. Flags: []cli.Flag{
  256. &cli.StringFlag{
  257. Name: flagSK,
  258. Usage: "ethereum `PRIVATE_KEY` in hex",
  259. Required: true,
  260. }},
  261. },
  262. {
  263. Name: "genbjj",
  264. Aliases: []string{},
  265. Usage: "Generate a new BabyJubJub key",
  266. Action: cmdGenBJJ,
  267. },
  268. {
  269. Name: "wipesql",
  270. Aliases: []string{},
  271. Usage: "Wipe the SQL DB (HistoryDB and L2DB), " +
  272. "leaving the DB in a clean state",
  273. Action: cmdWipeSQL,
  274. Flags: []cli.Flag{
  275. &cli.BoolFlag{
  276. Name: flagYes,
  277. Usage: "automatic yes to the prompt",
  278. Required: false,
  279. }},
  280. },
  281. {
  282. Name: "run",
  283. Aliases: []string{},
  284. Usage: "Run the hermez-node in the indicated mode",
  285. Action: cmdRun,
  286. },
  287. {
  288. Name: "discard",
  289. Aliases: []string{},
  290. Usage: "Discard blocks up to a specified block number",
  291. Action: cmdDiscard,
  292. Flags: []cli.Flag{
  293. &cli.Int64Flag{
  294. Name: flagBlock,
  295. Usage: "last block number to keep",
  296. Required: false,
  297. }},
  298. },
  299. }
  300. err := app.Run(os.Args)
  301. if err != nil {
  302. fmt.Printf("\nError: %v\n", tracerr.Sprint(err))
  303. os.Exit(1)
  304. }
  305. }