github.com/theQRL/go-zond@v0.2.1/zond/backend.go (about) 1 // Copyright 2014 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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-ethereum 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-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package zond implements the Zond protocol. 18 package zond 19 20 import ( 21 "fmt" 22 "math/big" 23 "runtime" 24 "sync" 25 26 "github.com/theQRL/go-zond/accounts" 27 "github.com/theQRL/go-zond/common" 28 "github.com/theQRL/go-zond/common/hexutil" 29 "github.com/theQRL/go-zond/consensus" 30 "github.com/theQRL/go-zond/core" 31 "github.com/theQRL/go-zond/core/bloombits" 32 "github.com/theQRL/go-zond/core/rawdb" 33 "github.com/theQRL/go-zond/core/state/pruner" 34 "github.com/theQRL/go-zond/core/txpool" 35 "github.com/theQRL/go-zond/core/txpool/legacypool" 36 "github.com/theQRL/go-zond/core/types" 37 "github.com/theQRL/go-zond/core/vm" 38 "github.com/theQRL/go-zond/event" 39 "github.com/theQRL/go-zond/internal/shutdowncheck" 40 "github.com/theQRL/go-zond/internal/zondapi" 41 "github.com/theQRL/go-zond/log" 42 "github.com/theQRL/go-zond/miner" 43 "github.com/theQRL/go-zond/node" 44 "github.com/theQRL/go-zond/p2p" 45 "github.com/theQRL/go-zond/p2p/dnsdisc" 46 "github.com/theQRL/go-zond/p2p/enode" 47 "github.com/theQRL/go-zond/params" 48 "github.com/theQRL/go-zond/rlp" 49 "github.com/theQRL/go-zond/rpc" 50 "github.com/theQRL/go-zond/zond/downloader" 51 "github.com/theQRL/go-zond/zond/gasprice" 52 "github.com/theQRL/go-zond/zond/protocols/snap" 53 "github.com/theQRL/go-zond/zond/protocols/zond" 54 "github.com/theQRL/go-zond/zond/zondconfig" 55 "github.com/theQRL/go-zond/zonddb" 56 ) 57 58 // Zond implements the Zond full node service. 59 type Zond struct { 60 config *zondconfig.Config 61 62 // Handlers 63 txPool *txpool.TxPool 64 65 blockchain *core.BlockChain 66 handler *handler 67 zondDialCandidates enode.Iterator 68 snapDialCandidates enode.Iterator 69 70 // DB interfaces 71 chainDb zonddb.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 *ZondAPIBackend 82 83 miner *miner.Miner 84 gasPrice *big.Int 85 86 networkID uint64 87 netRPCService *zondapi.NetAPI 88 89 p2pServer *p2p.Server 90 91 lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase) 92 93 shutdownTracker *shutdowncheck.ShutdownTracker // Tracks if and when the node has shutdown ungracefully 94 } 95 96 // New creates a new Zond object (including the initialisation of the common Zond object), 97 // whose lifecycle will be managed by the provided node. 98 func New(stack *node.Node, config *zondconfig.Config) (*Zond, error) { 99 // Ensure configuration values are compatible and sane 100 if !config.SyncMode.IsValid() { 101 return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode) 102 } 103 if config.Miner.GasPrice == nil || config.Miner.GasPrice.Sign() <= 0 { 104 log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", zondconfig.Defaults.Miner.GasPrice) 105 config.Miner.GasPrice = new(big.Int).Set(zondconfig.Defaults.Miner.GasPrice) 106 } 107 if config.NoPruning && config.TrieDirtyCache > 0 { 108 if config.SnapshotCache > 0 { 109 config.TrieCleanCache += config.TrieDirtyCache * 3 / 5 110 config.SnapshotCache += config.TrieDirtyCache * 2 / 5 111 } else { 112 config.TrieCleanCache += config.TrieDirtyCache 113 } 114 config.TrieDirtyCache = 0 115 } 116 log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024) 117 118 // Assemble the Zond object 119 chainDb, err := stack.OpenDatabaseWithFreezer("chaindata", config.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezer, "eth/db/chaindata/", false) 120 if err != nil { 121 return nil, err 122 } 123 // Try to recover offline state pruning only in hash-based. 124 if config.StateScheme == rawdb.HashScheme { 125 if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb); err != nil { 126 log.Error("Failed to recover state", "error", err) 127 } 128 } 129 chainConfig, err := core.LoadChainConfig(chainDb, config.Genesis) 130 if err != nil { 131 return nil, err 132 } 133 engine := zondconfig.CreateConsensusEngine() 134 networkID := config.NetworkId 135 if networkID == 0 { 136 networkID = chainConfig.ChainID.Uint64() 137 } 138 zond := &Zond{ 139 config: config, 140 chainDb: chainDb, 141 eventMux: stack.EventMux(), 142 accountManager: stack.AccountManager(), 143 engine: engine, 144 closeBloomHandler: make(chan struct{}), 145 networkID: networkID, 146 gasPrice: config.Miner.GasPrice, 147 bloomRequests: make(chan chan *bloombits.Retrieval), 148 bloomIndexer: core.NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms), 149 p2pServer: stack.Server(), 150 shutdownTracker: shutdowncheck.NewShutdownTracker(chainDb), 151 } 152 bcVersion := rawdb.ReadDatabaseVersion(chainDb) 153 var dbVer = "<nil>" 154 if bcVersion != nil { 155 dbVer = fmt.Sprintf("%d", *bcVersion) 156 } 157 log.Info("Initialising Zond protocol", "network", networkID, "dbversion", dbVer) 158 159 if !config.SkipBcVersionCheck { 160 if bcVersion != nil && *bcVersion > core.BlockChainVersion { 161 return nil, fmt.Errorf("database version is v%d, Gzond %s only supports v%d", *bcVersion, params.VersionWithMeta, core.BlockChainVersion) 162 } else if bcVersion == nil || *bcVersion < core.BlockChainVersion { 163 if bcVersion != nil { // only print warning on upgrade, not on init 164 log.Warn("Upgrade blockchain database version", "from", dbVer, "to", core.BlockChainVersion) 165 } 166 rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion) 167 } 168 } 169 var ( 170 vmConfig = vm.Config{ 171 EnablePreimageRecording: config.EnablePreimageRecording, 172 } 173 cacheConfig = &core.CacheConfig{ 174 TrieCleanLimit: config.TrieCleanCache, 175 TrieCleanNoPrefetch: config.NoPrefetch, 176 TrieDirtyLimit: config.TrieDirtyCache, 177 TrieDirtyDisabled: config.NoPruning, 178 TrieTimeLimit: config.TrieTimeout, 179 SnapshotLimit: config.SnapshotCache, 180 Preimages: config.Preimages, 181 StateHistory: config.StateHistory, 182 StateScheme: config.StateScheme, 183 } 184 ) 185 zond.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, config.Genesis, zond.engine, vmConfig, &config.TransactionHistory) 186 if err != nil { 187 return nil, err 188 } 189 zond.bloomIndexer.Start(zond.blockchain) 190 191 if config.TxPool.Journal != "" { 192 config.TxPool.Journal = stack.ResolvePath(config.TxPool.Journal) 193 } 194 legacyPool := legacypool.New(config.TxPool, zond.blockchain) 195 zond.txPool, err = txpool.New(new(big.Int).SetUint64(config.TxPool.PriceLimit), zond.blockchain, []txpool.SubPool{legacyPool}) 196 if err != nil { 197 return nil, err 198 } 199 // Permit the downloader to use the trie cache allowance during fast sync 200 cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit + cacheConfig.SnapshotLimit 201 if zond.handler, err = newHandler(&handlerConfig{ 202 NodeID: zond.p2pServer.Self().ID(), 203 Database: chainDb, 204 Chain: zond.blockchain, 205 TxPool: zond.txPool, 206 Network: networkID, 207 Sync: config.SyncMode, 208 BloomCache: uint64(cacheLimit), 209 EventMux: zond.eventMux, 210 RequiredBlocks: config.RequiredBlocks, 211 }); err != nil { 212 return nil, err 213 } 214 215 zond.miner = miner.New(zond, config.Miner, zond.engine) 216 zond.miner.SetExtra(makeExtraData(config.Miner.ExtraData)) 217 218 zond.APIBackend = &ZondAPIBackend{stack.Config().ExtRPCEnabled(), zond, nil} 219 220 gpoParams := config.GPO 221 if gpoParams.Default == nil { 222 gpoParams.Default = config.Miner.GasPrice 223 } 224 zond.APIBackend.gpo = gasprice.NewOracle(zond.APIBackend, gpoParams) 225 226 // Setup DNS discovery iterators. 227 dnsclient := dnsdisc.NewClient(dnsdisc.Config{}) 228 zond.zondDialCandidates, err = dnsclient.NewIterator(zond.config.ZondDiscoveryURLs...) 229 if err != nil { 230 return nil, err 231 } 232 zond.snapDialCandidates, err = dnsclient.NewIterator(zond.config.SnapDiscoveryURLs...) 233 if err != nil { 234 return nil, err 235 } 236 237 // Start the RPC service 238 zond.netRPCService = zondapi.NewNetAPI(zond.p2pServer, networkID) 239 240 // Register the backend on the node 241 stack.RegisterAPIs(zond.APIs()) 242 stack.RegisterProtocols(zond.Protocols()) 243 stack.RegisterLifecycle(zond) 244 245 // Successful startup; push a marker and check previous unclean shutdowns. 246 zond.shutdownTracker.MarkStartup() 247 248 return zond, nil 249 } 250 251 func makeExtraData(extra []byte) []byte { 252 if len(extra) == 0 { 253 // create default extradata 254 extra, _ = rlp.EncodeToBytes([]interface{}{ 255 uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch), 256 "gzond", 257 runtime.Version(), 258 runtime.GOOS, 259 }) 260 } 261 if uint64(len(extra)) > params.MaximumExtraDataSize { 262 log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", params.MaximumExtraDataSize) 263 extra = nil 264 } 265 return extra 266 } 267 268 // APIs return the collection of RPC services the zond package offers. 269 // NOTE, some of these services probably need to be moved to somewhere else. 270 func (s *Zond) APIs() []rpc.API { 271 apis := zondapi.GetAPIs(s.APIBackend) 272 273 // Append any APIs exposed explicitly by the consensus engine 274 apis = append(apis, s.engine.APIs(s.BlockChain())...) 275 276 // Append all the local APIs and return 277 return append(apis, []rpc.API{ 278 { 279 Namespace: "miner", 280 Service: NewMinerAPI(s), 281 }, { 282 Namespace: "zond", 283 Service: downloader.NewDownloaderAPI(s.handler.downloader, s.eventMux), 284 }, { 285 Namespace: "admin", 286 Service: NewAdminAPI(s), 287 }, { 288 Namespace: "debug", 289 Service: NewDebugAPI(s), 290 }, { 291 Namespace: "net", 292 Service: s.netRPCService, 293 }, 294 }...) 295 } 296 297 func (s *Zond) ResetWithGenesisBlock(gb *types.Block) { 298 s.blockchain.ResetWithGenesisBlock(gb) 299 } 300 301 func (s *Zond) Miner() *miner.Miner { return s.miner } 302 303 func (s *Zond) AccountManager() *accounts.Manager { return s.accountManager } 304 func (s *Zond) BlockChain() *core.BlockChain { return s.blockchain } 305 func (s *Zond) TxPool() *txpool.TxPool { return s.txPool } 306 func (s *Zond) EventMux() *event.TypeMux { return s.eventMux } 307 func (s *Zond) Engine() consensus.Engine { return s.engine } 308 func (s *Zond) ChainDb() zonddb.Database { return s.chainDb } 309 func (s *Zond) IsListening() bool { return true } // Always listening 310 func (s *Zond) Downloader() *downloader.Downloader { return s.handler.downloader } 311 func (s *Zond) Synced() bool { return s.handler.synced.Load() } 312 func (s *Zond) SetSynced() { s.handler.enableSyncedFeatures() } 313 func (s *Zond) ArchiveMode() bool { return s.config.NoPruning } 314 func (s *Zond) BloomIndexer() *core.ChainIndexer { return s.bloomIndexer } 315 316 // Protocols returns all the currently configured 317 // network protocols to start. 318 func (s *Zond) Protocols() []p2p.Protocol { 319 protos := zond.MakeProtocols((*zondHandler)(s.handler), s.networkID, s.zondDialCandidates) 320 if s.config.SnapshotCache > 0 { 321 protos = append(protos, snap.MakeProtocols((*snapHandler)(s.handler), s.snapDialCandidates)...) 322 } 323 return protos 324 } 325 326 // Start implements node.Lifecycle, starting all internal goroutines needed by the 327 // Zond protocol implementation. 328 func (s *Zond) Start() error { 329 zond.StartENRUpdater(s.blockchain, s.p2pServer.LocalNode()) 330 331 // Start the bloom bits servicing goroutines 332 s.startBloomHandlers(params.BloomBitsBlocks) 333 334 // Regularly update shutdown marker 335 s.shutdownTracker.Start() 336 337 // Figure out a max peers count based on the server limits 338 maxPeers := s.p2pServer.MaxPeers 339 340 // Start the networking layer if requested 341 s.handler.Start(maxPeers) 342 return nil 343 } 344 345 // Stop implements node.Lifecycle, terminating all internal goroutines used by the 346 // Zond protocol. 347 func (s *Zond) Stop() error { 348 // Stop all the peer-related stuff first. 349 s.zondDialCandidates.Close() 350 s.snapDialCandidates.Close() 351 s.handler.Stop() 352 353 // Then stop everything else. 354 s.bloomIndexer.Close() 355 close(s.closeBloomHandler) 356 s.txPool.Close() 357 s.blockchain.Stop() 358 s.engine.Close() 359 360 // Clean shutdown marker as the last thing before closing db 361 s.shutdownTracker.Stop() 362 363 s.chainDb.Close() 364 s.eventMux.Stop() 365 366 return nil 367 } 368 369 // SyncMode retrieves the current sync mode, either explicitly set, or derived 370 // from the chain status. 371 func (s *Zond) SyncMode() downloader.SyncMode { 372 // If we're in snap sync mode, return that directly 373 if s.handler.snapSync.Load() { 374 return downloader.SnapSync 375 } 376 // We are probably in full sync, but we might have rewound to before the 377 // snap sync pivot, check if we should re-enable snap sync. 378 head := s.blockchain.CurrentBlock() 379 if pivot := rawdb.ReadLastPivotNumber(s.chainDb); pivot != nil { 380 if head.Number.Uint64() < *pivot { 381 return downloader.SnapSync 382 } 383 } 384 // We are in a full sync, but the associated head state is missing. To complete 385 // the head state, forcefully rerun the snap sync. Note it doesn't mean the 386 // persistent state is corrupted, just mismatch with the head block. 387 if !s.blockchain.HasState(head.Root) { 388 log.Info("Reenabled snap sync as chain is stateless") 389 return downloader.SnapSync 390 } 391 // Nope, we're really full syncing 392 return downloader.FullSync 393 }