arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
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}