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.

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