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.MinFeeUSD,
177 cfg.Coordinator.L2DB.TTL.Duration,
178 nil,
179 )
180 if err := l2DB.Reorg(batchNum); err != nil {
181 return tracerr.Wrap(fmt.Errorf("l2DB.Reorg: %w", err))
182 }
183
184 return nil
185}
186
187// Config is the configuration of the hermez node execution
188type Config struct {
189 mode node.Mode
190 node *config.Node
191}
192
193func parseCli(c *cli.Context) (*Config, error) {
194 cfg, err := getConfig(c)
195 if err != nil {
196 if err := cli.ShowAppHelp(c); err != nil {
197 panic(err)
198 }
199 return nil, tracerr.Wrap(err)
200 }
201 return cfg, nil
202}
203
204func getConfig(c *cli.Context) (*Config, error) {
205 var cfg Config
206 mode := c.String(flagMode)
207 nodeCfgPath := c.String(flagCfg)
208 if nodeCfgPath == "" {
209 return nil, tracerr.Wrap(fmt.Errorf("required flag \"%v\" not set", flagCfg))
210 }
211 var err error
212 switch mode {
213 case modeSync:
214 cfg.mode = node.ModeSynchronizer
215 cfg.node, err = config.LoadNode(nodeCfgPath)
216 if err != nil {
217 return nil, tracerr.Wrap(err)
218 }
219 case modeCoord:
220 cfg.mode = node.ModeCoordinator
221 cfg.node, err = config.LoadCoordinator(nodeCfgPath)
222 if err != nil {
223 return nil, tracerr.Wrap(err)
224 }
225 default:
226 return nil, tracerr.Wrap(fmt.Errorf("invalid mode \"%v\"", mode))
227 }
228
229 return &cfg, nil
230}
231
232func main() {
233 app := cli.NewApp()
234 app.Name = "hermez-node"
235 app.Version = "0.1.0-alpha"
236 app.Flags = []cli.Flag{
237 &cli.StringFlag{
238 Name: flagMode,
239 Usage: fmt.Sprintf("Set node `MODE` (can be \"%v\" or \"%v\")", modeSync, modeCoord),
240 Required: true,
241 },
242 &cli.StringFlag{
243 Name: flagCfg,
244 Usage: "Node configuration `FILE`",
245 Required: true,
246 },
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
301 err := app.Run(os.Args)
302 if err != nil {
303 fmt.Printf("\nError: %v\n", tracerr.Sprint(err))
304 os.Exit(1)
305 }
306}