github.com/amazechain/amc@v0.1.3/internal/node/node.go (about) 1 // Copyright 2022 The AmazeChain Authors 2 // This file is part of the AmazeChain library. 3 // 4 // The AmazeChain library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The AmazeChain library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the AmazeChain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package node 18 19 import ( 20 "context" 21 "crypto/rand" 22 "fmt" 23 "github.com/amazechain/amc/common/hexutil" 24 "github.com/amazechain/amc/contracts/deposit" 25 "github.com/amazechain/amc/contracts/deposit/AMT" 26 fujideposit "github.com/amazechain/amc/contracts/deposit/FUJI" 27 nftdeposit "github.com/amazechain/amc/contracts/deposit/NFT" 28 "github.com/amazechain/amc/internal/debug" 29 "github.com/amazechain/amc/internal/metrics/prometheus" 30 "github.com/amazechain/amc/internal/p2p" 31 amcsync "github.com/amazechain/amc/internal/sync" 32 initialsync "github.com/amazechain/amc/internal/sync/initial-sync" 33 "github.com/amazechain/amc/internal/tracers" 34 "github.com/gofrs/flock" 35 "github.com/ledgerwatch/erigon-lib/common/cmp" 36 "github.com/pkg/errors" 37 "github.com/urfave/cli/v2" 38 "hash/crc32" 39 "net" 40 "path" 41 "runtime" 42 "strings" 43 44 "github.com/amazechain/amc/internal" 45 "github.com/amazechain/amc/internal/api" 46 47 "github.com/amazechain/amc/modules" 48 "github.com/c2h5oh/datasize" 49 "github.com/ledgerwatch/erigon-lib/kv" 50 "github.com/ledgerwatch/erigon-lib/kv/mdbx" 51 "github.com/ledgerwatch/erigon-lib/kv/memdb" 52 log2 "github.com/ledgerwatch/log/v3" 53 "golang.org/x/sync/semaphore" 54 55 "github.com/amazechain/amc/log" 56 "os" 57 "path/filepath" 58 "strconv" 59 60 "github.com/amazechain/amc/accounts" 61 "github.com/amazechain/amc/accounts/keystore" 62 "github.com/amazechain/amc/common" 63 "github.com/amazechain/amc/common/block" 64 "github.com/amazechain/amc/common/transaction" 65 "github.com/amazechain/amc/common/types" 66 "github.com/amazechain/amc/conf" 67 "sync" 68 69 "github.com/amazechain/amc/internal/consensus" 70 "github.com/amazechain/amc/internal/consensus/apoa" 71 "github.com/amazechain/amc/internal/consensus/apos" 72 "github.com/amazechain/amc/internal/miner" 73 "github.com/amazechain/amc/internal/txspool" 74 "github.com/amazechain/amc/modules/rawdb" 75 "github.com/amazechain/amc/modules/rpc/jsonrpc" 76 "github.com/amazechain/amc/params" 77 "github.com/amazechain/amc/utils" 78 "go.uber.org/zap" 79 ) 80 81 const datadirJWTKey = "jwtsecret" // Path within the datadir to the node's jwt secret 82 83 type Node struct { 84 cliCtx *cli.Context 85 ctx context.Context 86 cancel context.CancelFunc 87 config *conf.Config 88 genesisBlock block.IBlock 89 etherbase types.Address 90 91 lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase) 92 startStopLock sync.Mutex // Start/Stop are protected by an additional lock 93 state int // Tracks state of node lifecycle 94 shutDown chan struct{} // Channel to wait for termination notifications 95 dirLock *flock.Flock // prevents concurrent use of instance directory 96 97 // s 98 miner *miner.Miner 99 blockChain common.IBlockChain 100 engine consensus.Engine 101 db kv.RwDB 102 txspool common.ITxsPool 103 depositContract *deposit.Deposit 104 p2p p2p.P2P 105 sync *amcsync.Service 106 is *initialsync.Service 107 accman *accounts.Manager 108 109 api *api.API 110 rpcAPIs []jsonrpc.API 111 112 http *httpServer 113 ipc *ipcServer 114 ws *httpServer 115 httpAuth *httpServer // 116 wsAuth *httpServer // 117 inprocHandler *jsonrpc.Server 118 119 keyDir string // key store directory 120 keyDirTemp bool // If true, key directory will be removed by Stop 121 122 } 123 124 const ( 125 initializingState = iota 126 runningState 127 closedState 128 ) 129 130 func NewNode(cliCtx *cli.Context, cfg *conf.Config) (*Node, error) { 131 132 ctx, cancel := context.WithCancel(cliCtx.Context) 133 134 var ( 135 genesisBlock block.IBlock 136 node Node 137 engine consensus.Engine 138 depositContract *deposit.Deposit 139 genesisHash types.Hash 140 genesisConfig *conf.Genesis 141 chainConfig *params.ChainConfig 142 chainKv kv.RwDB 143 err error 144 ) 145 146 // 147 chainKv, err = OpenDatabase(cfg, nil, kv.ChainDB.String()) 148 if nil != err { 149 return nil, err 150 } 151 152 if err := chainKv.View(ctx, func(tx kv.Tx) error { 153 // 154 genesisHash, err = rawdb.ReadCanonicalHash(tx, 0) 155 // 156 if genesisHash == (types.Hash{}) && err != nil { 157 //return fmt.Errorf("GenesisHash is missing err:%w", err) 158 return internal.ErrGenesisNoConfig 159 } 160 if genesisHash == (types.Hash{}) && err == nil { 161 //needs WriteGenesisBlock 162 return nil 163 } 164 // 165 chainConfig, err = rawdb.ReadChainConfig(tx, genesisHash) 166 if err != nil { 167 return err 168 } 169 // 170 if genesisBlock, err = rawdb.ReadBlockByHash(tx, genesisHash); genesisBlock == nil { 171 return fmt.Errorf("genesisBlock is missing err:%w", err) 172 } 173 174 return nil 175 }); err != nil { 176 return nil, err 177 } 178 179 if genesisHash == (types.Hash{}) { 180 genesisHash = *params.GenesisHashByChainName(cfg.NodeCfg.Chain) 181 genesisConfig = internal.GenesisByChainName(cfg.NodeCfg.Chain) 182 chainConfig = params.ChainConfigByChainName(cfg.NodeCfg.Chain) 183 if err := chainKv.Update(ctx, func(tx kv.RwTx) error { 184 var genesisErr error 185 genesisBlock, genesisErr = WriteGenesisBlock(tx, genesisConfig) 186 if nil != genesisErr { 187 return genesisErr 188 } 189 return nil 190 }); err != nil { 191 return nil, err 192 } 193 } 194 195 // update ChainConfig everytime 196 if cfg.NodeCfg.Chain != "private" { 197 if err := chainKv.Update(ctx, func(tx kv.RwTx) error { 198 genesisHash = *params.GenesisHashByChainName(cfg.NodeCfg.Chain) 199 genesisConfig = internal.GenesisByChainName(cfg.NodeCfg.Chain) 200 if err := WriteChainConfig(tx, genesisHash, genesisConfig); err != nil { 201 return err 202 } 203 return nil 204 }); err != nil { 205 return nil, err 206 } 207 } 208 209 // Acquire the instance directory lock. 210 if err := node.openDataDir(cfg); err != nil { 211 return nil, err 212 } 213 214 cfg.ChainCfg = chainConfig 215 216 p2p, err := p2p.NewService(ctx, genesisBlock.Hash(), cfg.P2PCfg, cfg.NodeCfg) 217 if err != nil { 218 return nil, err 219 } 220 221 switch cfg.ChainCfg.Consensus { 222 case params.CliqueConsensus: 223 engine = apoa.New(cfg.ChainCfg.Clique, chainKv) 224 case params.AposConsensu: 225 engine = apos.New(cfg.ChainCfg.Apos, chainKv, cfg.ChainCfg) 226 default: 227 return nil, fmt.Errorf("invalid engine name %s", cfg.ChainCfg.Consensus) 228 } 229 230 bc, _ := internal.NewBlockChain(ctx, genesisBlock, engine, chainKv, p2p, cfg.ChainCfg) 231 232 if cfg.ChainCfg.Apos != nil { 233 depositContracts := make(map[types.Address]deposit.DepositContract, 0) 234 if depositContractAddress := cfg.ChainCfg.Apos.DepositContract; depositContractAddress != "" { 235 var addr types.Address 236 if !addr.DecodeString(depositContractAddress) { 237 panic(fmt.Sprintf("cannot decode DepositContract address: %s", depositContractAddress)) 238 } 239 depositContracts[addr] = new(amtdeposit.Contract) 240 } 241 if depositNFTContractAddress := cfg.ChainCfg.Apos.DepositNFTContract; depositNFTContractAddress != "" { 242 var addr types.Address 243 if !addr.DecodeString(depositNFTContractAddress) { 244 panic(fmt.Sprintf("cannot decode DepositNFTContract address: %s", depositNFTContractAddress)) 245 } 246 depositContracts[addr] = new(nftdeposit.Contract) 247 } 248 249 if depositFUJIContractAddress := cfg.ChainCfg.Apos.DepositFUJIContract; depositFUJIContractAddress != "" { 250 var addr types.Address 251 if !addr.DecodeString(depositFUJIContractAddress) { 252 panic(fmt.Sprintf("cannot decode DepositFUJIContract address: %s", depositFUJIContractAddress)) 253 } 254 depositContracts[addr] = new(fujideposit.Contract) 255 } 256 depositContract = deposit.NewDeposit(ctx, bc, chainKv, depositContracts) 257 } 258 259 pool, _ := txspool.NewTxsPool(ctx, bc, depositContract) 260 261 is := initialsync.NewService(ctx, &initialsync.Config{ 262 Chain: bc, 263 P2P: p2p, 264 }) 265 266 syncServer := amcsync.NewService( 267 ctx, 268 amcsync.WithP2P(p2p), 269 amcsync.WithChainService(bc), 270 amcsync.WithInitialSync(is), 271 ) 272 273 //todo 274 var txs []*transaction.Transaction 275 pending := pool.Pending(false) 276 for _, batch := range pending { 277 txs = append(txs, batch...) 278 } 279 var bloom *types.Bloom 280 if len(txs) > 0 { 281 bloom, _ = types.NewBloom(uint64(len(txs))) 282 for _, tx := range txs { 283 hash := tx.Hash() 284 bloom.Add(hash.Bytes()) 285 } 286 } 287 288 miner := miner.NewMiner(ctx, cfg, bc, engine, pool, nil) 289 290 keyDir, isEphem, err := getKeyStoreDir(&cfg.NodeCfg) 291 if err != nil { 292 return nil, err 293 } 294 // Creates an empty AccountManager with no backends. Callers (e.g. cmd/amc) 295 // are required to add the backends later on. 296 accman := accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: cfg.NodeCfg.InsecureUnlockAllowed}) 297 298 log.Info("new node", "GenesisHash", genesisBlock.Hash(), "CurrentBlockNr", bc.CurrentBlock().Number64().Uint64()) 299 300 node = Node{ 301 cliCtx: cliCtx, 302 ctx: ctx, 303 cancel: cancel, 304 config: cfg, 305 miner: miner, 306 genesisBlock: genesisBlock, 307 blockChain: bc, 308 db: chainKv, 309 shutDown: make(chan struct{}), 310 txspool: pool, 311 engine: engine, 312 depositContract: depositContract, 313 314 inprocHandler: jsonrpc.NewServer(), 315 http: newHTTPServer(), 316 ws: newHTTPServer(), 317 wsAuth: newHTTPServer(), 318 httpAuth: newHTTPServer(), 319 ipc: newIPCServer(&cfg.NodeCfg), 320 etherbase: types.HexToAddress(cfg.Miner.Etherbase), 321 322 accman: accman, 323 keyDir: keyDir, 324 keyDirTemp: isEphem, 325 326 p2p: p2p, 327 sync: syncServer, 328 is: is, 329 } 330 331 // Apply flags. 332 //SetNodeConfig(ctx, &cfg) 333 // Node doesn't by default populate account manager backends 334 if err = setAccountManagerBackends(&node, &cfg.NodeCfg); err != nil { 335 log.Errorf("Failed to set account manager backends: %v", err) 336 } 337 338 gpoParams := cfg.GPO 339 if gpoParams.Default == nil { 340 gpoParams.Default = cfg.Miner.GasPrice 341 } 342 343 // 344 log.Info("") 345 log.Info(strings.Repeat("-", 153)) 346 for _, line := range strings.Split(cfg.ChainCfg.Description(), "\n") { 347 log.Info(line) 348 } 349 log.Info(strings.Repeat("-", 153)) 350 log.Info("") 351 352 node.api = api.NewAPI(bc, chainKv, engine, pool, node.AccountManager(), cfg.ChainCfg) 353 node.api.SetGpo(api.NewOracle(bc, miner, cfg.ChainCfg, gpoParams)) 354 return &node, nil 355 } 356 357 func (n *Node) Start() error { 358 n.startStopLock.Lock() 359 defer n.startStopLock.Unlock() 360 361 n.lock.Lock() 362 switch n.state { 363 case runningState: 364 n.lock.Unlock() 365 return ErrNodeRunning 366 case closedState: 367 n.lock.Unlock() 368 return ErrNodeStopped 369 } 370 n.state = runningState 371 n.lock.Unlock() 372 373 if err := n.blockChain.Start(); err != nil { 374 log.Errorf("failed setup blockChain service, err: %v", err) 375 return err 376 } 377 378 if n.config.NodeCfg.Miner { 379 380 // Configure the local mining address 381 eb, err := n.Etherbase() 382 if err != nil { 383 log.Error("Cannot start mining without etherbase", "err", err) 384 return fmt.Errorf("etherbase missing: %v", err) 385 } 386 387 if poa, ok := n.engine.(*apoa.Apoa); ok { 388 wallet, err := n.accman.Find(accounts.Account{Address: eb}) 389 if wallet == nil || err != nil { 390 log.Error("Etherbase account unavailable locally", "err", err) 391 return fmt.Errorf("signer missing: %v", err) 392 } 393 poa.Authorize(eb, wallet.SignData) 394 } else if pos, ok := n.engine.(*apos.APos); ok { 395 wallet, err := n.accman.Find(accounts.Account{Address: eb}) 396 if wallet == nil || err != nil { 397 log.Error("Etherbase account unavailable locally", "err", err) 398 return fmt.Errorf("signer missing: %v", err) 399 } 400 pos.Authorize(eb, wallet.SignData) 401 } 402 403 n.miner.SetCoinbase(eb) 404 n.miner.Start() 405 } 406 407 if pos, ok := n.engine.(*apos.APos); ok { 408 pos.SetBlockChain(n.blockChain) 409 } 410 411 n.rpcAPIs = append(n.rpcAPIs, n.engine.APIs(n.blockChain)...) 412 n.rpcAPIs = append(n.rpcAPIs, n.api.Apis()...) 413 n.rpcAPIs = append(n.rpcAPIs, tracers.APIs(n.api)...) 414 n.rpcAPIs = append(n.rpcAPIs, debug.APIs()...) 415 416 if err := n.startRPC(); err != nil { 417 log.Error("failed start jsonrpc service", zap.Error(err)) 418 return err 419 } 420 421 //n.p2p.AddConnectionHandler() 422 n.p2p.Start() 423 n.sync.Start() 424 425 n.SetupMetrics(n.config.MetricsCfg) 426 427 if n.depositContract != nil { 428 n.depositContract.Start() 429 } 430 431 go n.is.Start() 432 433 log.Debug("node setup success!") 434 435 return nil 436 } 437 438 // getAPIs return two sets of APIs, both the ones that do not require 439 // authentication, and the complete set 440 func (n *Node) getAPIs() (unauthenticated, all []jsonrpc.API) { 441 for _, api := range n.rpcAPIs { 442 if !api.Authenticated { 443 unauthenticated = append(unauthenticated, api) 444 } 445 } 446 return unauthenticated, n.rpcAPIs 447 } 448 449 func (n *Node) startInProc() error { 450 for _, api := range n.rpcAPIs { 451 if err := n.inprocHandler.RegisterName(api.Namespace, api.Service); err != nil { 452 return err 453 } 454 } 455 return nil 456 } 457 458 func (n *Node) stopInProc() { 459 n.inprocHandler.Stop() 460 } 461 462 func (n *Node) openDataDir(cfg *conf.Config) error { 463 if cfg.NodeCfg.DataDir == "" { 464 return nil // ephemeral 465 } 466 467 if err := os.MkdirAll(cfg.NodeCfg.DataDir, 0700); err != nil { 468 return err 469 } 470 // Lock the instance directory to prevent concurrent use by another instance as well as 471 // accidental use of the instance directory as a database. 472 n.dirLock = flock.New(filepath.Join(cfg.NodeCfg.DataDir, "LOCK")) 473 474 if locked, err := n.dirLock.TryLock(); err != nil { 475 return err 476 } else if !locked { 477 return ErrDatadirUsed 478 } 479 return nil 480 } 481 482 func (n *Node) closeDataDir() { 483 // Release instance directory lock. 484 if n.dirLock != nil && n.dirLock.Locked() { 485 n.dirLock.Unlock() 486 n.dirLock = nil 487 } 488 } 489 490 // obtainJWTSecret loads the jwt-secret, either from the provided config, 491 // or from the default location. If neither of those are present, it generates 492 // a new secret and stores to the default location. 493 func (n *Node) obtainJWTSecret(cliParam string) ([]byte, error) { 494 fileName := cliParam 495 if len(fileName) == 0 { 496 // no path provided, use default 497 fileName = path.Join(n.config.NodeCfg.DataDir, datadirJWTKey) 498 } 499 // try reading from file 500 if data, err := os.ReadFile(fileName); err == nil { 501 jwtSecret, err := hexutil.Decode(strings.TrimSpace(string(data))) 502 if err != nil { 503 return nil, errors.Wrap(err, fmt.Sprintf("failed to decode hex (%s) string", strings.TrimSpace(string(data)))) 504 } 505 if len(jwtSecret) == 32 { 506 log.Info("Loaded JWT secret file", "path", fileName, "crc32", fmt.Sprintf("%#x", crc32.ChecksumIEEE(jwtSecret))) 507 return jwtSecret, nil 508 } 509 log.Error("Invalid JWT secret", "path", fileName, "length", len(jwtSecret)) 510 return nil, errors.New("invalid JWT secret") 511 } 512 // Need to generate one 513 jwtSecret := make([]byte, 32) 514 rand.Read(jwtSecret) 515 516 if err := os.WriteFile(fileName, []byte(hexutil.Encode(jwtSecret)), 0600); err != nil { 517 return nil, err 518 } 519 log.Info("Generated JWT secret", "path", fileName) 520 return jwtSecret, nil 521 } 522 523 func (n *Node) startRPC() error { 524 525 openAPIs, allAPIs := n.getAPIs() 526 527 if err := n.startInProc(); err != nil { 528 return err 529 } 530 531 if n.ipc.endpoint != "" { 532 //if err := n.ipc.start(n.rpcAPIs); err != nil { 533 // return err 534 //} 535 } 536 if n.config.NodeCfg.HTTP { 537 //todo []string{"eth", "web3", "debug", "net", "apoa", "txpool", "apos"} 538 config := httpConfig{ 539 CorsAllowedOrigins: utils.SplitAndTrim(n.config.NodeCfg.HTTPCors), 540 Vhosts: []string{"*"}, 541 Modules: utils.SplitAndTrim(n.config.NodeCfg.HTTPApi), 542 prefix: "", 543 } 544 port, _ := strconv.Atoi(n.config.NodeCfg.HTTPPort) 545 if err := n.http.setListenAddr(n.config.NodeCfg.HTTPHost, port); err != nil { 546 return err 547 } 548 if err := n.http.enableRPC(n.rpcAPIs, config); err != nil { 549 return err 550 } 551 if err := n.http.start(); err != nil { 552 return err 553 } 554 } 555 556 // Configure WebSocket. 557 if n.config.NodeCfg.WS { 558 port, _ := strconv.Atoi(n.config.NodeCfg.WSPort) 559 if err := n.ws.setListenAddr(n.config.NodeCfg.WSHost, port); err != nil { 560 return err 561 } 562 //todo 563 config := wsConfig{ 564 Modules: utils.SplitAndTrim(n.config.NodeCfg.WSApi), 565 Origins: utils.SplitAndTrim(n.config.NodeCfg.WSOrigins), 566 prefix: "", 567 jwtSecret: []byte{}, 568 } 569 if err := n.ws.enableWS(n.rpcAPIs, config); err != nil { 570 return err 571 } 572 if err := n.ws.start(); err != nil { 573 return err 574 } 575 } 576 577 // Configure authenticated API 578 if len(openAPIs) != len(allAPIs) && n.config.NodeCfg.AuthRPC { 579 jwtSecret, err := n.obtainJWTSecret(n.config.NodeCfg.JWTSecret) 580 if err != nil { 581 return err 582 } 583 config := httpConfig{ 584 CorsAllowedOrigins: utils.SplitAndTrim(n.config.NodeCfg.HTTPCors), 585 Vhosts: []string{"*"}, 586 Modules: []string{"admin", "apos"}, 587 prefix: "", 588 jwtSecret: jwtSecret, 589 } 590 591 if err := n.httpAuth.setListenAddr(n.config.NodeCfg.AuthAddr, n.config.NodeCfg.AuthPort); err != nil { 592 return err 593 } 594 if err := n.httpAuth.enableRPC(n.rpcAPIs, config); err != nil { 595 return err 596 } 597 if err := n.httpAuth.start(); err != nil { 598 return err 599 } 600 } 601 602 return nil 603 } 604 605 func (n *Node) stopRPC() { 606 n.http.stop() 607 n.ws.stop() 608 n.ipc.stop() 609 n.stopInProc() 610 } 611 612 // InstanceDir retrieves the instance directory used by the protocol stack. 613 func (n *Node) InstanceDir() string { 614 return n.config.NodeCfg.DataDir 615 } 616 617 func (n *Node) Close() error { 618 n.startStopLock.Lock() 619 defer n.startStopLock.Unlock() 620 621 n.lock.Lock() 622 state := n.state 623 n.lock.Unlock() 624 switch state { 625 case initializingState: 626 // The node was never started. 627 return n.doClose(nil) 628 case runningState: 629 // The node was started, release resources acquired by Start(). 630 var errs []error 631 if err := n.stopServices(); err != nil { 632 errs = append(errs, err...) 633 } 634 return n.doClose(errs) 635 case closedState: 636 return ErrNodeStopped 637 default: 638 panic(fmt.Sprintf("node is in unknown state %d", state)) 639 } 640 } 641 642 // stopServices terminates running services, RPC and p2p networking. 643 // It is the inverse of Start. 644 func (n *Node) stopServices() []error { 645 var errs []error 646 n.stopRPC() 647 648 n.miner.Close() 649 650 if err := n.blockChain.Close(); err != nil { 651 errs = append(errs, err) 652 } 653 654 if err := n.engine.Close(); err != nil { 655 errs = append(errs, err) 656 } 657 658 if err := n.txspool.Stop(); err != nil { 659 errs = append(errs, err) 660 } 661 662 if err := n.depositContract.Stop(); err != nil { 663 errs = append(errs, err) 664 } 665 666 if err := n.is.Stop(); err != nil { 667 errs = append(errs, err) 668 } 669 670 if err := n.p2p.Stop(); err != nil { 671 errs = append(errs, err) 672 } 673 674 if err := n.sync.Stop(); err != nil { 675 errs = append(errs, err) 676 } 677 678 return errs 679 } 680 681 // doClose releases resources acquired by New(), collecting errors. 682 func (n *Node) doClose(errs []error) error { 683 // Close databases. This needs the lock because it needs to 684 // synchronize with OpenDatabase*. 685 n.lock.Lock() 686 n.state = closedState 687 n.db.Close() 688 n.lock.Unlock() 689 690 if err := n.accman.Close(); err != nil { 691 errs = append(errs, err) 692 } 693 if n.keyDirTemp { 694 if err := os.RemoveAll(n.keyDir); err != nil { 695 errs = append(errs, err) 696 } 697 } 698 699 // Release instance directory lock. 700 n.closeDataDir() 701 702 // Unblock n.Wait. 703 close(n.shutDown) 704 705 // Report any errors that might have occurred. 706 switch len(errs) { 707 case 0: 708 return nil 709 case 1: 710 return errs[0] 711 default: 712 return fmt.Errorf("%v", errs) 713 } 714 } 715 716 func (n *Node) Wait() { 717 <-n.shutDown 718 } 719 720 // AccountManager retrieves the account manager used by the protocol stack. 721 func (n *Node) AccountManager() *accounts.Manager { 722 return n.accman 723 } 724 725 // AccountManager retrieves the account manager used by the protocol stack. 726 func (n *Node) BlockChain() common.IBlockChain { 727 return n.blockChain 728 } 729 730 func (n *Node) Database() kv.RwDB { 731 return n.db 732 } 733 734 // getKeyStoreDir retrieves the key directory and will create 735 // and ephemeral one if necessary. 736 func getKeyStoreDir(conf *conf.NodeConfig) (string, bool, error) { 737 keydir, err := conf.KeyDirConfig() 738 if err != nil { 739 return "", false, err 740 } 741 isEphemeral := false 742 if keydir == "" { 743 // There is no datadir. 744 keydir, err = os.MkdirTemp("", "go-ethereum-keystore") 745 isEphemeral = true 746 } 747 748 if err != nil { 749 return "", false, err 750 } 751 if err := os.MkdirAll(keydir, 0700); err != nil { 752 return "", false, err 753 } 754 755 return keydir, isEphemeral, nil 756 } 757 758 func setAccountManagerBackends(stack *Node, conf *conf.NodeConfig) error { 759 am := stack.AccountManager() 760 keydir := stack.KeyStoreDir() 761 scryptN := keystore.StandardScryptN 762 scryptP := keystore.StandardScryptP 763 if conf.UseLightweightKDF { 764 scryptN = keystore.LightScryptN 765 scryptP = keystore.LightScryptP 766 } 767 768 // For now, we're using EITHER external signer OR local signers. 769 // If/when we implement some form of lockfile for USB and keystore wallets, 770 // we can have both, but it's very confusing for the user to see the same 771 // accounts in both externally and locally, plus very racey. 772 am.AddBackend(keystore.NewKeyStore(keydir, scryptN, scryptP)) 773 774 return nil 775 } 776 777 // KeyStoreDir retrieves the key directory 778 func (n *Node) KeyStoreDir() string { 779 return n.keyDir 780 } 781 782 func (n *Node) SetupMetrics(config conf.MetricsConfig) { 783 if config.Enable { 784 if config.HTTP != "" { 785 address := net.JoinHostPort(config.HTTP, fmt.Sprintf("%d", config.Port)) 786 log.Info("Enabling stand-alone metrics HTTP endpoint", "address", address) 787 prometheus.Setup(address, log.Root()) 788 } else if config.Port != 0 { 789 log.Warn(fmt.Sprintf("--%s specified without --%s, metrics server will not start.", "metrics.port", "metrics.addr")) 790 } 791 } 792 793 } 794 795 func (s *Node) Etherbase() (eb types.Address, err error) { 796 s.lock.RLock() 797 etherbase := s.etherbase 798 s.lock.RUnlock() 799 800 if etherbase != (types.Address{}) { 801 return etherbase, nil 802 } 803 if wallets := s.AccountManager().Wallets(); len(wallets) > 0 { 804 if accounts := wallets[0].Accounts(); len(accounts) > 0 { 805 etherbase := accounts[0].Address 806 807 s.lock.Lock() 808 s.etherbase = etherbase 809 s.lock.Unlock() 810 811 log.Info("Etherbase automatically configured", "address", etherbase) 812 return etherbase, nil 813 } 814 } 815 return types.Address{}, fmt.Errorf("etherbase must be explicitly specified") 816 } 817 818 func OpenDatabase(cfg *conf.Config, logger log2.Logger, name string) (kv.RwDB, error) { 819 var chainKv kv.RwDB 820 if cfg.NodeCfg.DataDir == "" { 821 chainKv = memdb.New("") 822 } 823 var err error 824 825 dbPath := filepath.Join(cfg.NodeCfg.DataDir, name) 826 827 var openFunc func(exclusive bool) (kv.RwDB, error) 828 log.Info("Opening Database", "label", name, "path", dbPath) 829 openFunc = func(exclusive bool) (kv.RwDB, error) { 830 //if config.Http.DBReadConcurrency > 0 { 831 // roTxLimit = int64(config.Http.DBReadConcurrency) 832 //} 833 roTxsLimiter := semaphore.NewWeighted(int64(cmp.Max(32, runtime.GOMAXPROCS(-1)*8))) // 1 less than max to allow unlocking to happen 834 opts := mdbx.NewMDBX(logger). 835 WriteMergeThreshold(4 * 8192). 836 Path(dbPath).Label(kv.ChainDB). 837 DBVerbosity(kv.DBVerbosityLvl(2)).RoTxsLimiter(roTxsLimiter) 838 if exclusive { 839 opts = opts.Exclusive() 840 } 841 842 modules.AmcInit() 843 kv.ChaindataTablesCfg = modules.AmcTableCfg 844 845 opts = opts.MapSize(8 * datasize.TB) 846 return opts.Open() 847 } 848 chainKv, err = openFunc(false) 849 if err != nil { 850 return nil, err 851 } 852 853 if err = chainKv.Update(context.Background(), func(tx kv.RwTx) (err error) { 854 return params.SetAmcVersion(tx, params.VersionKeyCreated) 855 }); err != nil { 856 return nil, err 857 } 858 return chainKv, nil 859 } 860 861 func WriteGenesisBlock(db kv.RwTx, genesis *conf.Genesis) (*block.Block, error) { 862 if genesis == nil { 863 return nil, internal.ErrGenesisNoConfig 864 } 865 866 g := &internal.GenesisBlock{ 867 "", 868 genesis, 869 //config, 870 } 871 log.Info("Writing genesis block") 872 genBlock, _, err := g.Write(db) 873 if nil != err { 874 return nil, err 875 } 876 877 return genBlock, nil 878 879 } 880 881 func WriteChainConfig(db kv.RwTx, genesisHash types.Hash, genesis *conf.Genesis) error { 882 if err := rawdb.WriteChainConfig(db, genesisHash, genesis.Config); err != nil { 883 log.Error("cannot get chain config from db", "err", err) 884 return err 885 } 886 return nil 887 } 888 889 func SplitTagsFlag(tagsFlag string) map[string]string { 890 tags := strings.Split(tagsFlag, ",") 891 tagsMap := map[string]string{} 892 893 for _, t := range tags { 894 if t != "" { 895 kv := strings.Split(t, "=") 896 897 if len(kv) == 2 { 898 tagsMap[kv[0]] = kv[1] 899 } 900 } 901 } 902 903 return tagsMap 904 } 905 906 func (n *Node) Miner() common.IMiner { 907 return n.miner 908 } 909 910 func (n *Node) Engine() consensus.Engine { 911 return n.engine 912 } 913 914 func (n *Node) ChainDb() kv.RwDB { 915 return n.db 916 }