github.com/codingfuture/orig-energi3@v0.8.4/eth/backend.go (about) 1 // Copyright 2018 The Energi Core Authors 2 // Copyright 2014 The go-ethereum Authors 3 // This file is part of the Energi Core library. 4 // 5 // The Energi Core library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The Energi Core library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the Energi Core library. If not, see <http://www.gnu.org/licenses/>. 17 18 // Package eth implements the Ethereum protocol. 19 package eth 20 21 import ( 22 "errors" 23 "fmt" 24 "math/big" 25 "runtime" 26 "sync" 27 "sync/atomic" 28 29 "github.com/ethereum/go-ethereum/accounts" 30 "github.com/ethereum/go-ethereum/common" 31 "github.com/ethereum/go-ethereum/common/hexutil" 32 "github.com/ethereum/go-ethereum/consensus" 33 "github.com/ethereum/go-ethereum/consensus/clique" 34 "github.com/ethereum/go-ethereum/consensus/ethash" 35 "github.com/ethereum/go-ethereum/core" 36 "github.com/ethereum/go-ethereum/core/bloombits" 37 "github.com/ethereum/go-ethereum/core/rawdb" 38 "github.com/ethereum/go-ethereum/core/types" 39 "github.com/ethereum/go-ethereum/core/vm" 40 "github.com/ethereum/go-ethereum/eth/downloader" 41 "github.com/ethereum/go-ethereum/eth/filters" 42 "github.com/ethereum/go-ethereum/eth/gasprice" 43 "github.com/ethereum/go-ethereum/ethdb" 44 "github.com/ethereum/go-ethereum/event" 45 "github.com/ethereum/go-ethereum/internal/ethapi" 46 "github.com/ethereum/go-ethereum/log" 47 "github.com/ethereum/go-ethereum/miner" 48 "github.com/ethereum/go-ethereum/node" 49 "github.com/ethereum/go-ethereum/p2p" 50 "github.com/ethereum/go-ethereum/params" 51 "github.com/ethereum/go-ethereum/rlp" 52 "github.com/ethereum/go-ethereum/rpc" 53 54 energi_api "energi.world/core/gen3/energi/api" 55 energi "energi.world/core/gen3/energi/consensus" 56 ) 57 58 type LesServer interface { 59 Start(srvr *p2p.Server) 60 Stop() 61 Protocols() []p2p.Protocol 62 SetBloomBitsIndexer(bbIndexer *core.ChainIndexer) 63 } 64 65 // Ethereum implements the Ethereum full node service. 66 type Ethereum struct { 67 config *Config 68 chainConfig *params.ChainConfig 69 70 // Channel for shutting down the service 71 shutdownChan chan bool // Channel for shutting down the Ethereum 72 73 // Handlers 74 txPool *core.TxPool 75 blockchain *core.BlockChain 76 protocolManager *ProtocolManager 77 lesServer LesServer 78 79 // DB interfaces 80 chainDb ethdb.Database // Block chain database 81 82 eventMux *event.TypeMux 83 engine consensus.Engine 84 accountManager *accounts.Manager 85 86 bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests 87 bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports 88 89 APIBackend *EthAPIBackend 90 91 miner *miner.Miner 92 gasPrice *big.Int 93 etherbase common.Address 94 dpos DPoSMap 95 96 networkID uint64 97 netRPCService *ethapi.PublicNetAPI 98 99 lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase) 100 } 101 102 func (s *Ethereum) AddLesServer(ls LesServer) { 103 s.lesServer = ls 104 ls.SetBloomBitsIndexer(s.bloomIndexer) 105 } 106 107 // New creates a new Ethereum object (including the 108 // initialisation of the common Ethereum object) 109 func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { 110 // Ensure configuration values are compatible and sane 111 if config.SyncMode == downloader.LightSync { 112 return nil, errors.New("can't run eth.Ethereum in light sync mode, use les.LightEthereum") 113 } 114 if !config.SyncMode.IsValid() { 115 return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode) 116 } 117 if config.MinerGasPrice == nil || config.MinerGasPrice.Cmp(common.Big0) <= 0 { 118 log.Warn("Sanitizing invalid miner gas price", "provided", config.MinerGasPrice, "updated", DefaultConfig.MinerGasPrice) 119 config.MinerGasPrice = new(big.Int).Set(DefaultConfig.MinerGasPrice) 120 } 121 // Assemble the Ethereum object 122 chainDb, err := CreateDB(ctx, config, "chaindata") 123 if err != nil { 124 return nil, err 125 } 126 chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.ConstantinopleOverride) 127 if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { 128 return nil, genesisErr 129 } 130 log.Info("Initialised chain configuration", "config", chainConfig) 131 132 eth := &Ethereum{ 133 config: config, 134 chainDb: chainDb, 135 chainConfig: chainConfig, 136 eventMux: ctx.EventMux, 137 accountManager: ctx.AccountManager, 138 engine: CreateConsensusEngine(ctx, chainConfig, &config.Ethash, config.MinerNotify, config.MinerNoverify, chainDb), 139 shutdownChan: make(chan bool), 140 networkID: config.NetworkId, 141 gasPrice: config.MinerGasPrice, 142 etherbase: config.Etherbase, 143 dpos: config.MinerDPoS, 144 bloomRequests: make(chan chan *bloombits.Retrieval), 145 bloomIndexer: NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms), 146 } 147 148 log.Info("Initialising Energi protocol", "versions", ProtocolVersions, "network", config.NetworkId) 149 150 if !config.SkipBcVersionCheck { 151 bcVersion := rawdb.ReadDatabaseVersion(chainDb) 152 if bcVersion != nil && *bcVersion > core.BlockChainVersion { 153 return nil, fmt.Errorf("database version is v%d, Energi Core %s only supports v%d", *bcVersion, params.VersionWithMeta, core.BlockChainVersion) 154 } else if bcVersion != nil && *bcVersion < core.BlockChainVersion { 155 log.Warn("Upgrade blockchain database version", "from", *bcVersion, "to", core.BlockChainVersion) 156 } 157 rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion) 158 } 159 var ( 160 vmConfig = vm.Config{ 161 EnablePreimageRecording: config.EnablePreimageRecording, 162 EWASMInterpreter: config.EWASMInterpreter, 163 EVMInterpreter: config.EVMInterpreter, 164 } 165 cacheConfig = &core.CacheConfig{Disabled: config.NoPruning, TrieCleanLimit: config.TrieCleanCache, TrieDirtyLimit: config.TrieDirtyCache, TrieTimeLimit: config.TrieTimeout, TrieRapidLimit: config.TrieRapidTime} 166 ) 167 eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, eth.chainConfig, eth.engine, vmConfig, eth.shouldPreserve) 168 if err != nil { 169 return nil, err 170 } 171 // Rewind the chain in case of an incompatible config upgrade. 172 if compat, ok := genesisErr.(*params.ConfigCompatError); ok { 173 log.Warn("Rewinding chain to upgrade configuration", "err", compat) 174 eth.blockchain.SetHead(compat.RewindTo) 175 rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig) 176 } 177 eth.bloomIndexer.Start(eth.blockchain) 178 179 if config.TxPool.Journal != "" { 180 config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal) 181 } 182 config.TxPool.Protection = ctx.ResolvePath(core.DefaultTxPoolConfig.Protection) 183 184 eth.txPool = core.NewTxPool(config.TxPool, eth.chainConfig, eth.blockchain) 185 186 if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.SyncMode, config.NetworkId, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb, config.Whitelist); err != nil { 187 return nil, err 188 } 189 190 if eth.dpos == nil { 191 eth.dpos = make(DPoSMap) 192 } 193 if energi, ok := eth.engine.(*energi.Energi); ok { 194 energi.SetMinerNonceCap(config.MinerNonceCap) 195 } 196 eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine, config.MinerRecommit, config.MinerGasFloor, config.MinerGasCeil, eth.isLocalBlock) 197 eth.miner.SetExtra(makeExtraData(config.MinerExtraData)) 198 eth.miner.SetMigration(config.MinerMigration) 199 200 eth.APIBackend = &EthAPIBackend{eth, nil} 201 gpoParams := config.GPO 202 if gpoParams.Default == nil { 203 gpoParams.Default = config.MinerGasPrice 204 } 205 eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams) 206 207 eth.miner.SetEthAPIBackend(eth.APIBackend) 208 eth.miner.SetMinerAutocollateral(config.MinerAutocollateral) 209 210 return eth, nil 211 } 212 213 func makeExtraData(extra []byte) []byte { 214 if len(extra) == 0 { 215 // create default extradata 216 extra, _ = rlp.EncodeToBytes([]interface{}{ 217 uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch), 218 "energi3", 219 runtime.Version(), 220 runtime.GOOS, 221 }) 222 } 223 if uint64(len(extra)) > params.MaximumExtraDataSize { 224 log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", params.MaximumExtraDataSize) 225 extra = nil 226 } 227 return extra 228 } 229 230 // CreateDB creates the chain database. 231 func CreateDB(ctx *node.ServiceContext, config *Config, name string) (ethdb.Database, error) { 232 db, err := ctx.OpenDatabase(name, config.DatabaseCache, config.DatabaseHandles) 233 if err != nil { 234 return nil, err 235 } 236 if db, ok := db.(*ethdb.LDBDatabase); ok { 237 db.Meter("eth/db/chaindata/") 238 } 239 return db, nil 240 } 241 242 // CreateConsensusEngine creates the required type of consensus engine instance for an Ethereum service 243 func CreateConsensusEngine(ctx *node.ServiceContext, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database) consensus.Engine { 244 // If proof-of-stake is requested, set it up 245 if chainConfig.Energi != nil { 246 return energi.New(chainConfig.Energi, db) 247 } 248 // If proof-of-authority is requested, set it up 249 if chainConfig.Clique != nil { 250 return clique.New(chainConfig.Clique, db) 251 } 252 // Otherwise assume proof-of-work 253 switch config.PowMode { 254 case ethash.ModeFake: 255 log.Warn("Ethash used in fake mode") 256 return ethash.NewFaker() 257 case ethash.ModeTest: 258 log.Warn("Ethash used in test mode") 259 return ethash.NewTester(nil, noverify) 260 case ethash.ModeShared: 261 log.Warn("Ethash used in shared mode") 262 return ethash.NewShared() 263 default: 264 engine := ethash.New(ethash.Config{ 265 CacheDir: ctx.ResolvePath(config.CacheDir), 266 CachesInMem: config.CachesInMem, 267 CachesOnDisk: config.CachesOnDisk, 268 DatasetDir: config.DatasetDir, 269 DatasetsInMem: config.DatasetsInMem, 270 DatasetsOnDisk: config.DatasetsOnDisk, 271 }, notify, noverify) 272 engine.SetThreads(-1) // Disable CPU mining 273 return engine 274 } 275 } 276 277 // APIs return the collection of RPC services the ethereum package offers. 278 // NOTE, some of these services probably need to be moved to somewhere else. 279 func (s *Ethereum) APIs() []rpc.API { 280 apis := ethapi.GetAPIs(s.APIBackend) 281 282 // Append any APIs exposed explicitly by the consensus engine 283 apis = append(apis, s.engine.APIs(s.BlockChain())...) 284 285 // Append all the local APIs and return 286 apis = append(apis, []rpc.API{ 287 { 288 Namespace: "eth", 289 Version: "1.0", 290 Service: NewPublicEthereumAPI(s), 291 Public: true, 292 }, { 293 Namespace: "eth", 294 Version: "1.0", 295 Service: NewPublicMinerAPI(s), 296 Public: true, 297 }, { 298 Namespace: "eth", 299 Version: "1.0", 300 Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), 301 Public: true, 302 }, { 303 Namespace: "miner", 304 Version: "1.0", 305 Service: NewPrivateMinerAPI(s), 306 Public: false, 307 }, { 308 Namespace: "eth", 309 Version: "1.0", 310 Service: filters.NewPublicFilterAPI(s.APIBackend, false), 311 Public: true, 312 }, { 313 Namespace: "admin", 314 Version: "1.0", 315 Service: NewPrivateAdminAPI(s), 316 }, { 317 Namespace: "debug", 318 Version: "1.0", 319 Service: NewPublicDebugAPI(s), 320 Public: true, 321 }, { 322 Namespace: "debug", 323 Version: "1.0", 324 Service: NewPrivateDebugAPI(s.chainConfig, s), 325 }, { 326 Namespace: "net", 327 Version: "1.0", 328 Service: s.netRPCService, 329 Public: true, 330 }, 331 }...) 332 333 // Append Energi-specific APIs 334 apis = append(apis, []rpc.API{ 335 { 336 Namespace: "energi", 337 Version: "1.0", 338 Service: energi_api.NewBlacklistAPI(s.APIBackend), 339 Public: true, 340 }, 341 { 342 Namespace: "energi", 343 Version: "1.0", 344 Service: energi_api.NewCheckpointAPI(s.APIBackend), 345 Public: true, 346 }, 347 { 348 Namespace: "admin", 349 Version: "1.0", 350 Service: energi_api.NewCheckpointAdminAPI(s.APIBackend), 351 }, 352 { 353 Namespace: "energi", 354 Version: "1.0", 355 Service: energi_api.NewGovernanceAPI(s.APIBackend), 356 Public: true, 357 }, 358 { 359 Namespace: "energi", 360 Version: "1.0", 361 Service: energi_api.NewMigrationAPI(s.APIBackend), 362 Public: true, 363 }, 364 { 365 Namespace: "masternode", 366 Version: "1.0", 367 Service: energi_api.NewMasternodeAPI(s.APIBackend), 368 Public: true, 369 }, 370 }...) 371 372 return apis 373 } 374 375 func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) { 376 s.blockchain.ResetWithGenesisBlock(gb) 377 } 378 379 func (s *Ethereum) Etherbase() (eb common.Address, err error) { 380 s.lock.RLock() 381 etherbase := s.etherbase 382 s.lock.RUnlock() 383 384 if etherbase != (common.Address{}) { 385 return etherbase, nil 386 } 387 if wallets := s.AccountManager().Wallets(); len(wallets) > 0 { 388 if accounts := wallets[0].Accounts(); len(accounts) > 0 { 389 etherbase := accounts[0].Address 390 391 s.lock.Lock() 392 s.etherbase = etherbase 393 s.lock.Unlock() 394 395 if _, ok := s.engine.(*energi.Energi); !ok { 396 log.Info("Etherbase automatically configured", "address", etherbase) 397 } 398 return etherbase, nil 399 } 400 } 401 402 if _, ok := s.engine.(*energi.Energi); ok { 403 return common.Address{}, fmt.Errorf("local accounts are required for staking") 404 } 405 406 return common.Address{}, fmt.Errorf("etherbase must be explicitly specified") 407 } 408 409 // isLocalBlock checks whether the specified block is mined 410 // by local miner accounts. 411 // 412 // We regard two types of accounts as local miner account: etherbase 413 // and accounts specified via `txpool.locals` flag. 414 func (s *Ethereum) isLocalBlock(block *types.Block) bool { 415 author, err := s.engine.Author(block.Header()) 416 if err != nil { 417 log.Warn("Failed to retrieve block author", "number", block.NumberU64(), "hash", block.Hash(), "err", err) 418 return false 419 } 420 // Check whether the given address is etherbase. 421 s.lock.RLock() 422 etherbase := s.etherbase 423 s.lock.RUnlock() 424 if author == etherbase { 425 return true 426 } 427 // Check whether the given address is specified by `txpool.local` 428 // CLI flag. 429 for _, account := range s.config.TxPool.Locals { 430 if account == author { 431 return true 432 } 433 } 434 // TODO: DPoS + accounts 435 return false 436 } 437 438 // shouldPreserve checks whether we should preserve the given block 439 // during the chain reorg depending on whether the author of block 440 // is a local account. 441 func (s *Ethereum) shouldPreserve(block *types.Block) bool { 442 // The reason we need to disable the self-reorg preserving for clique 443 // is it can be probable to introduce a deadlock. 444 // 445 // e.g. If there are 7 available signers 446 // 447 // r1 A 448 // r2 B 449 // r3 C 450 // r4 D 451 // r5 A [X] F G 452 // r6 [X] 453 // 454 // In the round5, the inturn signer E is offline, so the worst case 455 // is A, F and G sign the block of round5 and reject the block of opponents 456 // and in the round6, the last available signer B is offline, the whole 457 // network is stuck. 458 if _, ok := s.engine.(*clique.Clique); ok { 459 return false 460 } 461 return s.isLocalBlock(block) 462 } 463 464 // SetEtherbase sets the mining reward address. 465 func (s *Ethereum) SetEtherbase(etherbase common.Address) { 466 s.lock.Lock() 467 s.etherbase = etherbase 468 s.lock.Unlock() 469 470 s.miner.SetEtherbase(etherbase) 471 } 472 473 // AddDPoS add contract for delegated PoS 474 func (s *Ethereum) AddDPoS(contract common.Address, signer common.Address) { 475 s.lock.Lock() 476 s.dpos[contract] = signer 477 s.lock.Unlock() 478 } 479 480 // RemoveDPoS remove contract from delegated PoS 481 func (s *Ethereum) RemoveDPoS(contract common.Address) { 482 s.lock.Lock() 483 delete(s.dpos, contract) 484 s.lock.Unlock() 485 } 486 487 // StartMining starts the miner with the given number of CPU threads. If mining 488 // is already running, this method adjust the number of threads allowed to use 489 // and updates the minimum price required by the transaction pool. 490 func (s *Ethereum) StartMining(threads int) error { 491 // Update the thread count within the consensus engine 492 type threaded interface { 493 SetThreads(threads int) 494 } 495 if th, ok := s.engine.(threaded); ok { 496 log.Info("Updated mining threads", "threads", threads) 497 if threads == 0 { 498 threads = -1 // Disable the miner from within 499 } 500 th.SetThreads(threads) 501 } 502 // If the miner was not running, initialize it 503 if !s.IsMining() { 504 // Propagate the initial price point to the transaction pool 505 s.lock.RLock() 506 price := s.gasPrice 507 s.lock.RUnlock() 508 s.txPool.SetGasPrice(price) 509 510 // Configure the local mining address 511 eb, err := s.Etherbase() 512 if err != nil { 513 log.Error("Cannot start mining without etherbase", "err", err) 514 return fmt.Errorf("etherbase missing: %v", err) 515 } 516 if clique, ok := s.engine.(*clique.Clique); ok { 517 wallet, err := s.accountManager.Find(accounts.Account{Address: eb}) 518 if wallet == nil || err != nil { 519 log.Error("Etherbase account unavailable locally", "err", err) 520 return fmt.Errorf("signer missing: %v", err) 521 } 522 clique.Authorize(eb, wallet.SignHash) 523 } 524 if energi, ok := s.engine.(*energi.Energi); ok { 525 energi.SetMinerCB( 526 func() []common.Address { 527 res := make([]common.Address, 0, 32) 528 for _, w := range s.accountManager.Wallets() { 529 for _, a := range w.Accounts() { 530 if w.IsUnlockedForStaking(a) { 531 res = append(res, a.Address) 532 } 533 } 534 } 535 536 // TODO: revise how locking affects performance 537 s.lock.RLock() 538 for k := range s.dpos { 539 res = append(res, k) 540 } 541 s.lock.RUnlock() 542 543 return res 544 }, 545 func(addr common.Address, hash []byte) ([]byte, error) { 546 // TODO: revise how locking affects performance 547 s.lock.RLock() 548 if signer, ok := s.dpos[addr]; ok { 549 addr = signer 550 } 551 s.lock.RUnlock() 552 553 account := accounts.Account{Address: addr} 554 wallet, err := s.accountManager.Find(account) 555 if wallet == nil || err != nil { 556 log.Error("Account unavailable locally", "err", err) 557 return nil, fmt.Errorf("signer missing: %v", err) 558 } 559 return wallet.SignHash(account, hash) 560 }, 561 func() int { 562 return s.protocolManager.peers.Len() 563 }, 564 ) 565 } 566 // If mining is started, we can disable the transaction rejection mechanism 567 // introduced to speed sync times. 568 atomic.StoreUint32(&s.protocolManager.acceptTxs, 1) 569 570 go s.miner.Start(eb) 571 } 572 return nil 573 } 574 575 // StopMining terminates the miner, both at the consensus engine level as well as 576 // at the block creation level. 577 func (s *Ethereum) StopMining() { 578 // Update the thread count within the consensus engine 579 type threaded interface { 580 SetThreads(threads int) 581 } 582 if th, ok := s.engine.(threaded); ok { 583 th.SetThreads(-1) 584 } 585 // Stop the block creating itself 586 s.miner.Stop() 587 } 588 589 func (s *Ethereum) IsMining() bool { return s.miner.Mining() } 590 func (s *Ethereum) Miner() *miner.Miner { return s.miner } 591 592 func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager } 593 func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain } 594 func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } 595 func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } 596 func (s *Ethereum) Engine() consensus.Engine { return s.engine } 597 func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } 598 func (s *Ethereum) IsListening() bool { return true } // Always listening 599 func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } 600 func (s *Ethereum) NetVersion() uint64 { return s.networkID } 601 func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } 602 603 // Protocols implements node.Service, returning all the currently configured 604 // network protocols to start. 605 func (s *Ethereum) Protocols() []p2p.Protocol { 606 if s.lesServer == nil { 607 return s.protocolManager.SubProtocols 608 } 609 return append(s.protocolManager.SubProtocols, s.lesServer.Protocols()...) 610 } 611 612 // Start implements node.Service, starting all internal goroutines needed by the 613 // Ethereum protocol implementation. 614 func (s *Ethereum) Start(srvr *p2p.Server) error { 615 // Start the bloom bits servicing goroutines 616 s.startBloomHandlers(params.BloomBitsBlocks) 617 618 // Start the RPC service 619 s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion()) 620 621 // Figure out a max peers count based on the server limits 622 maxPeers := srvr.MaxPeers 623 if s.config.LightServ > 0 { 624 if s.config.LightPeers >= srvr.MaxPeers { 625 return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, srvr.MaxPeers) 626 } 627 maxPeers -= s.config.LightPeers 628 } 629 // Start the networking layer and the light server if requested 630 s.protocolManager.Start(maxPeers) 631 if s.lesServer != nil { 632 s.lesServer.Start(srvr) 633 } 634 return nil 635 } 636 637 // Stop implements node.Service, terminating all internal goroutines used by the 638 // Ethereum protocol. 639 func (s *Ethereum) Stop() error { 640 s.bloomIndexer.Close() 641 s.blockchain.Stop() 642 s.engine.Close() 643 s.protocolManager.Stop() 644 if s.lesServer != nil { 645 s.lesServer.Stop() 646 } 647 s.txPool.Stop() 648 s.miner.Stop() 649 s.eventMux.Stop() 650 651 s.chainDb.Close() 652 close(s.shutdownChan) 653 return nil 654 }