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/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}