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