github.com/ebakus/go-ebakus@v1.0.5-0.20200520105415-dbccef9ec421/eth/backend.go (about) 1 // Copyright 2019 The ebakus/go-ebakus Authors 2 // This file is part of the ebakus/go-ebakus library. 3 // 4 // The ebakus/go-ebakus 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 ebakus/go-ebakus 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 ebakus/go-ebakus library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package eth implements the Ebakus protocol. 18 package eth 19 20 import ( 21 "errors" 22 "fmt" 23 "runtime" 24 "sync" 25 "sync/atomic" 26 27 "github.com/ebakus/ebakusdb" 28 "github.com/ebakus/go-ebakus/consensus/dpos" 29 30 "github.com/ebakus/go-ebakus/accounts" 31 "github.com/ebakus/go-ebakus/accounts/abi/bind" 32 "github.com/ebakus/go-ebakus/common" 33 "github.com/ebakus/go-ebakus/common/hexutil" 34 "github.com/ebakus/go-ebakus/consensus" 35 "github.com/ebakus/go-ebakus/core" 36 "github.com/ebakus/go-ebakus/core/bloombits" 37 "github.com/ebakus/go-ebakus/core/rawdb" 38 "github.com/ebakus/go-ebakus/core/types" 39 "github.com/ebakus/go-ebakus/core/vm" 40 "github.com/ebakus/go-ebakus/eth/downloader" 41 "github.com/ebakus/go-ebakus/eth/filters" 42 "github.com/ebakus/go-ebakus/eth/gasprice" 43 "github.com/ebakus/go-ebakus/ethdb" 44 "github.com/ebakus/go-ebakus/event" 45 "github.com/ebakus/go-ebakus/internal/ethapi" 46 "github.com/ebakus/go-ebakus/log" 47 "github.com/ebakus/go-ebakus/miner" 48 "github.com/ebakus/go-ebakus/node" 49 "github.com/ebakus/go-ebakus/p2p" 50 "github.com/ebakus/go-ebakus/p2p/enr" 51 "github.com/ebakus/go-ebakus/params" 52 "github.com/ebakus/go-ebakus/rlp" 53 "github.com/ebakus/go-ebakus/rpc" 54 ) 55 56 type LesServer interface { 57 Start(srvr *p2p.Server) 58 Stop() 59 APIs() []rpc.API 60 Protocols() []p2p.Protocol 61 SetBloomBitsIndexer(bbIndexer *core.ChainIndexer) 62 SetContractBackend(bind.ContractBackend) 63 } 64 65 // Ebakus implements the Ebakus full node service. 66 type Ebakus struct { 67 config *Config 68 69 // Channel for shutting down the service 70 shutdownChan chan bool 71 72 // Handlers 73 txPool *core.TxPool 74 blockchain *core.BlockChain 75 protocolManager *ProtocolManager 76 lesServer LesServer 77 78 // DB interfaces 79 chainDb ethdb.Database // Block chain database 80 stateDb *ebakusdb.DB // State 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 *float64 93 etherbase common.Address 94 95 networkID uint64 96 netRPCService *ethapi.PublicNetAPI 97 98 lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase) 99 } 100 101 func (s *Ebakus) AddLesServer(ls LesServer) { 102 s.lesServer = ls 103 ls.SetBloomBitsIndexer(s.bloomIndexer) 104 } 105 106 // SetClient sets a rpc client which connecting to our local node. 107 func (s *Ebakus) SetContractBackend(backend bind.ContractBackend) { 108 // Pass the rpc client to les server if it is enabled. 109 if s.lesServer != nil { 110 s.lesServer.SetContractBackend(backend) 111 } 112 } 113 114 // New creates a new Ebakus object (including the 115 // initialisation of the common Ebakus object) 116 func New(ctx *node.ServiceContext, config *Config) (*Ebakus, error) { 117 // Ensure configuration values are compatible and sane 118 if config.SyncMode == downloader.LightSync { 119 return nil, errors.New("can't run eth.Ebakus in light sync mode, use les.LightEbakus") 120 } 121 if !config.SyncMode.IsValid() { 122 return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode) 123 } 124 if config.Miner.GasPrice < 0.0 { 125 log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", DefaultConfig.Miner.GasPrice) 126 config.Miner.GasPrice = DefaultConfig.Miner.GasPrice 127 } 128 if config.NoPruning && config.TrieDirtyCache > 0 { 129 config.TrieCleanCache += config.TrieDirtyCache 130 config.TrieDirtyCache = 0 131 } 132 log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024) 133 134 // Assemble the Ebakus object 135 chainDb, err := ctx.OpenDatabaseWithFreezer("chaindata", config.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezer, "eth/db/chaindata/") 136 if err != nil { 137 return nil, err 138 } 139 140 stateDb, err := CreateEbakusDB(ctx, config, "state.db") 141 if err != nil { 142 return nil, err 143 } 144 145 chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, stateDb, config.Genesis) 146 if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { 147 return nil, genesisErr 148 } 149 150 log.Info("Initialised chain configuration", "config", chainConfig) 151 152 engine := CreateConsensusEngine(ctx, &config.DPOS, chainConfig, chainDb, stateDb, config.Genesis) 153 154 eth := &Ebakus{ 155 config: config, 156 chainDb: chainDb, 157 stateDb: stateDb, 158 eventMux: ctx.EventMux, 159 accountManager: ctx.AccountManager, 160 engine: engine, 161 shutdownChan: make(chan bool), 162 networkID: config.NetworkId, 163 gasPrice: &config.Miner.GasPrice, 164 etherbase: config.Miner.Etherbase, 165 bloomRequests: make(chan chan *bloombits.Retrieval), 166 bloomIndexer: NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms), 167 } 168 169 bcVersion := rawdb.ReadDatabaseVersion(chainDb) 170 var dbVer = "<nil>" 171 if bcVersion != nil { 172 dbVer = fmt.Sprintf("%d", *bcVersion) 173 } 174 log.Info("Initialising Ebakus protocol", "versions", ProtocolVersions, "network", config.NetworkId, "dbversion", dbVer) 175 176 if !config.SkipBcVersionCheck { 177 if bcVersion != nil && *bcVersion > core.BlockChainVersion { 178 return nil, fmt.Errorf("database version is v%d, Ebakus %s only supports v%d", *bcVersion, params.VersionWithMeta, core.BlockChainVersion) 179 } else if bcVersion == nil || *bcVersion < core.BlockChainVersion { 180 log.Warn("Upgrade blockchain database version", "from", dbVer, "to", core.BlockChainVersion) 181 rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion) 182 } 183 } 184 var ( 185 vmConfig = vm.Config{ 186 EnablePreimageRecording: config.EnablePreimageRecording, 187 EWASMInterpreter: config.EWASMInterpreter, 188 EVMInterpreter: config.EVMInterpreter, 189 } 190 cacheConfig = &core.CacheConfig{ 191 TrieCleanLimit: config.TrieCleanCache, 192 TrieCleanNoPrefetch: config.NoPrefetch, 193 TrieDirtyLimit: config.TrieDirtyCache, 194 TrieDirtyDisabled: config.NoPruning, 195 TrieTimeLimit: config.TrieTimeout, 196 } 197 ) 198 eth.blockchain, err = core.NewBlockChain(chainDb, stateDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve) 199 if err != nil { 200 return nil, err 201 } 202 // Rewind the chain in case of an incompatible config upgrade. 203 if compat, ok := genesisErr.(*params.ConfigCompatError); ok { 204 log.Warn("Rewinding chain to upgrade configuration", "err", compat) 205 eth.blockchain.SetHead(compat.RewindTo) 206 rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig) 207 } 208 eth.bloomIndexer.Start(eth.blockchain) 209 210 if chainConfig.DPOS != nil { 211 engine.(*dpos.DPOS).SetBlockchain(eth.blockchain) 212 } 213 214 if config.TxPool.Journal != "" { 215 config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal) 216 } 217 eth.txPool = core.NewTxPool(config.TxPool, chainConfig, eth.blockchain) 218 219 // Permit the downloader to use the trie cache allowance during fast sync 220 cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit 221 checkpoint := config.Checkpoint 222 if eth.protocolManager, err = NewProtocolManager(chainConfig, checkpoint, config.SyncMode, config.NetworkId, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb, cacheLimit, config.Whitelist); err != nil { 223 return nil, err 224 } 225 eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock) 226 227 eth.APIBackend = &EthAPIBackend{ctx.ExtRPCEnabled(), eth, nil} 228 gpoParams := config.GPO 229 if gpoParams.Default == nil { 230 gpoParams.Default = &config.Miner.GasPrice 231 } 232 eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams) 233 234 return eth, nil 235 } 236 237 func makeExtraData(extra []byte) []byte { 238 if len(extra) == 0 { 239 // create default extradata 240 extra, _ = rlp.EncodeToBytes([]interface{}{ 241 uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch), 242 "ebakus", 243 runtime.Version(), 244 runtime.GOOS, 245 }) 246 } 247 if uint64(len(extra)) > params.MaximumExtraDataSize { 248 log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", params.MaximumExtraDataSize) 249 extra = nil 250 } 251 return extra 252 } 253 254 // CreateEbakusDB creates the ebakus state db 255 func CreateEbakusDB(ctx *node.ServiceContext, config *Config, name string) (*ebakusdb.DB, error) { 256 db, err := ctx.OpenEbakusDatabase(name, config.DatabaseCache, config.DatabaseHandles) 257 if err != nil { 258 return nil, err 259 } 260 261 return db, nil 262 } 263 264 // CreateConsensusEngine creates the required type of consensus engine instance for an Ebakus service 265 func CreateConsensusEngine(ctx *node.ServiceContext, config *params.DPOSConfig, chainConfig *params.ChainConfig, db ethdb.Database, ebakusDb *ebakusdb.DB, genesis *core.Genesis) consensus.Engine { 266 return dpos.New(chainConfig.DPOS, db, ebakusDb, genesis) 267 } 268 269 // APIs return the collection of RPC services the ebakus package offers. 270 // NOTE, some of these services probably need to be moved to somewhere else. 271 func (s *Ebakus) APIs() []rpc.API { 272 apis := ethapi.GetAPIs(s.APIBackend) 273 274 // Append any APIs exposed explicitly by the les server 275 if s.lesServer != nil { 276 apis = append(apis, s.lesServer.APIs()...) 277 } 278 // Append any APIs exposed explicitly by the consensus engine 279 apis = append(apis, s.engine.APIs(s.BlockChain())...) 280 281 // Append any APIs exposed explicitly by the les server 282 if s.lesServer != nil { 283 apis = append(apis, s.lesServer.APIs()...) 284 } 285 286 // Append all the local APIs and return 287 return append(apis, []rpc.API{ 288 { 289 Namespace: "eth", 290 Version: "1.0", 291 Service: NewPublicEbakusAPI(s), 292 Public: true, 293 }, { 294 Namespace: "eth", 295 Version: "1.0", 296 Service: NewPublicMinerAPI(s), 297 Public: true, 298 }, { 299 Namespace: "eth", 300 Version: "1.0", 301 Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), 302 Public: true, 303 }, { 304 Namespace: "miner", 305 Version: "1.0", 306 Service: NewPrivateMinerAPI(s), 307 Public: false, 308 }, { 309 Namespace: "eth", 310 Version: "1.0", 311 Service: filters.NewPublicFilterAPI(s.APIBackend, false), 312 Public: true, 313 }, { 314 Namespace: "admin", 315 Version: "1.0", 316 Service: NewPrivateAdminAPI(s), 317 }, { 318 Namespace: "debug", 319 Version: "1.0", 320 Service: NewPublicDebugAPI(s), 321 Public: true, 322 }, { 323 Namespace: "debug", 324 Version: "1.0", 325 Service: NewPrivateDebugAPI(s), 326 }, { 327 Namespace: "net", 328 Version: "1.0", 329 Service: s.netRPCService, 330 Public: true, 331 }, 332 }...) 333 } 334 335 func (s *Ebakus) ResetWithGenesisBlock(gb *types.Block) { 336 s.blockchain.ResetWithGenesisBlock(gb) 337 } 338 339 func (s *Ebakus) Etherbase() (eb common.Address, err error) { 340 s.lock.RLock() 341 etherbase := s.etherbase 342 s.lock.RUnlock() 343 344 if etherbase != (common.Address{}) { 345 return etherbase, nil 346 } 347 if wallets := s.AccountManager().Wallets(); len(wallets) > 0 { 348 if accounts := wallets[0].Accounts(); len(accounts) > 0 { 349 etherbase := accounts[0].Address 350 351 s.lock.Lock() 352 s.etherbase = etherbase 353 s.lock.Unlock() 354 355 log.Info("Etherbase automatically configured", "address", etherbase) 356 return etherbase, nil 357 } 358 } 359 return common.Address{}, fmt.Errorf("etherbase must be explicitly specified") 360 } 361 362 // isLocalBlock checks whether the specified block is mined 363 // by local miner accounts. 364 // 365 // We regard two types of accounts as local miner account: etherbase 366 // and accounts specified via `txpool.locals` flag. 367 func (s *Ebakus) isLocalBlock(block *types.Block) bool { 368 author, err := s.engine.Author(block.Header()) 369 if err != nil { 370 log.Warn("Failed to retrieve block author", "number", block.NumberU64(), "hash", block.Hash(), "err", err) 371 return false 372 } 373 // Check whether the given address is etherbase. 374 s.lock.RLock() 375 etherbase := s.etherbase 376 s.lock.RUnlock() 377 if author == etherbase { 378 return true 379 } 380 // Check whether the given address is specified by `txpool.local` 381 // CLI flag. 382 for _, account := range s.config.TxPool.Locals { 383 if account == author { 384 return true 385 } 386 } 387 return false 388 } 389 390 // shouldPreserve checks whether we should preserve the given block 391 // during the chain reorg depending on whether the author of block 392 // is a local account. 393 func (s *Ebakus) shouldPreserve(block *types.Block) bool { 394 // The reason we need to disable the self-reorg preserving for clique 395 // is it can be probable to introduce a deadlock. 396 // 397 // e.g. If there are 7 available signers 398 // 399 // r1 A 400 // r2 B 401 // r3 C 402 // r4 D 403 // r5 A [X] F G 404 // r6 [X] 405 // 406 // In the round5, the inturn signer E is offline, so the worst case 407 // is A, F and G sign the block of round5 and reject the block of opponents 408 // and in the round6, the last available signer B is offline, the whole 409 // network is stuck. 410 return s.isLocalBlock(block) 411 } 412 413 // SetEtherbase sets the mining reward address. 414 func (s *Ebakus) SetEtherbase(etherbase common.Address) { 415 s.lock.Lock() 416 s.etherbase = etherbase 417 s.lock.Unlock() 418 419 s.miner.SetEtherbase(etherbase) 420 } 421 422 // StartMining starts the miner with the given number of CPU threads. If mining 423 // is already running, this method adjust the number of threads allowed to use 424 // and updates the minimum price required by the transaction pool. 425 func (s *Ebakus) StartMining(threads int) error { 426 // Update the thread count within the consensus engine 427 type threaded interface { 428 SetThreads(threads int) 429 } 430 if th, ok := s.engine.(threaded); ok { 431 log.Info("Updated mining threads", "threads", threads) 432 if threads == 0 { 433 threads = -1 // Disable the miner from within 434 } 435 th.SetThreads(threads) 436 } 437 // If the miner was not running, initialize it 438 if !s.IsMining() { 439 // Propagate the initial price point to the transaction pool 440 s.lock.RLock() 441 price := s.gasPrice 442 s.lock.RUnlock() 443 s.txPool.SetGasPrice(*price) 444 445 // Configure the local mining address 446 eb, err := s.Etherbase() 447 if err != nil { 448 log.Error("Cannot start mining without etherbase", "err", err) 449 return fmt.Errorf("etherbase missing: %v", err) 450 } 451 452 // TODO: Ebakus: we might want to remove the introduced threads from this func 453 if dpos, ok := s.engine.(*dpos.DPOS); ok { 454 wallet, err := s.accountManager.Find(accounts.Account{Address: eb}) 455 if wallet == nil || err != nil { 456 log.Error("Etherbase account unavailable locally", "err", err) 457 return fmt.Errorf("signer missing: %v", err) 458 } 459 dpos.Authorize(eb, wallet.SignData) 460 } 461 462 // If mining is started, we can disable the transaction rejection mechanism 463 // introduced to speed sync times. 464 atomic.StoreUint32(&s.protocolManager.acceptTxs, 1) 465 466 go s.miner.Start(eb) 467 } 468 return nil 469 } 470 471 // StopMining terminates the miner, both at the consensus engine level as well as 472 // at the block creation level. 473 func (s *Ebakus) StopMining() { 474 // Update the thread count within the consensus engine 475 type threaded interface { 476 SetThreads(threads int) 477 } 478 if th, ok := s.engine.(threaded); ok { 479 th.SetThreads(-1) 480 } 481 // Stop the block creating itself 482 s.miner.Stop() 483 } 484 485 func (s *Ebakus) IsMining() bool { return s.miner.Mining() } 486 func (s *Ebakus) Miner() *miner.Miner { return s.miner } 487 488 func (s *Ebakus) AccountManager() *accounts.Manager { return s.accountManager } 489 func (s *Ebakus) BlockChain() *core.BlockChain { return s.blockchain } 490 func (s *Ebakus) TxPool() *core.TxPool { return s.txPool } 491 func (s *Ebakus) EventMux() *event.TypeMux { return s.eventMux } 492 func (s *Ebakus) Engine() consensus.Engine { return s.engine } 493 func (s *Ebakus) ChainDb() ethdb.Database { return s.chainDb } 494 func (s *Ebakus) EbakusDb() *ebakusdb.DB { return s.stateDb } 495 func (s *Ebakus) IsListening() bool { return true } // Always listening 496 func (s *Ebakus) EthVersion() int { return int(ProtocolVersions[0]) } 497 func (s *Ebakus) NetVersion() uint64 { return s.networkID } 498 func (s *Ebakus) Downloader() *downloader.Downloader { return s.protocolManager.downloader } 499 func (s *Ebakus) Synced() bool { return atomic.LoadUint32(&s.protocolManager.acceptTxs) == 1 } 500 func (s *Ebakus) ArchiveMode() bool { return s.config.NoPruning } 501 502 // Protocols implements node.Service, returning all the currently configured 503 // network protocols to start. 504 func (s *Ebakus) Protocols() []p2p.Protocol { 505 protos := make([]p2p.Protocol, len(ProtocolVersions)) 506 for i, vsn := range ProtocolVersions { 507 protos[i] = s.protocolManager.makeProtocol(vsn) 508 protos[i].Attributes = []enr.Entry{s.currentEthEntry()} 509 } 510 if s.lesServer != nil { 511 protos = append(protos, s.lesServer.Protocols()...) 512 } 513 return protos 514 } 515 516 // Start implements node.Service, starting all internal goroutines needed by the 517 // Ebakus protocol implementation. 518 func (s *Ebakus) Start(srvr *p2p.Server) error { 519 s.startEthEntryUpdate(srvr.LocalNode()) 520 521 // Start the bloom bits servicing goroutines 522 s.startBloomHandlers(params.BloomBitsBlocks) 523 524 // Start the RPC service 525 s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion()) 526 527 // Figure out a max peers count based on the server limits 528 maxPeers := srvr.MaxPeers 529 if s.config.LightServ > 0 { 530 if s.config.LightPeers >= srvr.MaxPeers { 531 return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, srvr.MaxPeers) 532 } 533 maxPeers -= s.config.LightPeers 534 } 535 // Start the networking layer and the light server if requested 536 s.protocolManager.Start(maxPeers) 537 if s.lesServer != nil { 538 s.lesServer.Start(srvr) 539 } 540 return nil 541 } 542 543 // Stop implements node.Service, terminating all internal goroutines used by the 544 // Ebakus protocol. 545 func (s *Ebakus) Stop() error { 546 s.bloomIndexer.Close() 547 s.blockchain.Stop() 548 s.engine.Close() 549 s.protocolManager.Stop() 550 if s.lesServer != nil { 551 s.lesServer.Stop() 552 } 553 s.txPool.Stop() 554 s.miner.Stop() 555 s.eventMux.Stop() 556 557 s.chainDb.Close() 558 close(s.shutdownChan) 559 560 if err := s.stateDb.Close(); err != nil { 561 log.Error("Failed to close ebakus state database", "database", s.stateDb.GetPath(), "error", err) 562 } else { 563 log.Info("Ebakus state database closed", "database", s.stateDb.GetPath()) 564 } 565 566 return nil 567 }