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