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