github.com/ethereum/go-ethereum@v1.16.1/eth/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 eth implements the Ethereum protocol. 18 package eth 19 20 import ( 21 "context" 22 "encoding/json" 23 "fmt" 24 "math" 25 "math/big" 26 "runtime" 27 "sync" 28 "time" 29 30 "github.com/ethereum/go-ethereum/accounts" 31 "github.com/ethereum/go-ethereum/common" 32 "github.com/ethereum/go-ethereum/common/hexutil" 33 "github.com/ethereum/go-ethereum/consensus" 34 "github.com/ethereum/go-ethereum/core" 35 "github.com/ethereum/go-ethereum/core/filtermaps" 36 "github.com/ethereum/go-ethereum/core/rawdb" 37 "github.com/ethereum/go-ethereum/core/state/pruner" 38 "github.com/ethereum/go-ethereum/core/txpool" 39 "github.com/ethereum/go-ethereum/core/txpool/blobpool" 40 "github.com/ethereum/go-ethereum/core/txpool/legacypool" 41 "github.com/ethereum/go-ethereum/core/txpool/locals" 42 "github.com/ethereum/go-ethereum/core/types" 43 "github.com/ethereum/go-ethereum/core/vm" 44 "github.com/ethereum/go-ethereum/eth/downloader" 45 "github.com/ethereum/go-ethereum/eth/ethconfig" 46 "github.com/ethereum/go-ethereum/eth/gasprice" 47 "github.com/ethereum/go-ethereum/eth/protocols/eth" 48 "github.com/ethereum/go-ethereum/eth/protocols/snap" 49 "github.com/ethereum/go-ethereum/eth/tracers" 50 "github.com/ethereum/go-ethereum/ethdb" 51 "github.com/ethereum/go-ethereum/event" 52 "github.com/ethereum/go-ethereum/internal/ethapi" 53 "github.com/ethereum/go-ethereum/internal/shutdowncheck" 54 "github.com/ethereum/go-ethereum/internal/version" 55 "github.com/ethereum/go-ethereum/log" 56 "github.com/ethereum/go-ethereum/miner" 57 "github.com/ethereum/go-ethereum/node" 58 "github.com/ethereum/go-ethereum/p2p" 59 "github.com/ethereum/go-ethereum/p2p/dnsdisc" 60 "github.com/ethereum/go-ethereum/p2p/enode" 61 "github.com/ethereum/go-ethereum/params" 62 "github.com/ethereum/go-ethereum/rlp" 63 "github.com/ethereum/go-ethereum/rpc" 64 gethversion "github.com/ethereum/go-ethereum/version" 65 ) 66 67 const ( 68 // This is the fairness knob for the discovery mixer. When looking for peers, we'll 69 // wait this long for a single source of candidates before moving on and trying other 70 // sources. If this timeout expires, the source will be skipped in this round, but it 71 // will continue to fetch in the background and will have a chance with a new timeout 72 // in the next rounds, giving it overall more time but a proportionally smaller share. 73 // We expect a normal source to produce ~10 candidates per second. 74 discmixTimeout = 100 * time.Millisecond 75 76 // discoveryPrefetchBuffer is the number of peers to pre-fetch from a discovery 77 // source. It is useful to avoid the negative effects of potential longer timeouts 78 // in the discovery, keeping dial progress while waiting for the next batch of 79 // candidates. 80 discoveryPrefetchBuffer = 32 81 82 // maxParallelENRRequests is the maximum number of parallel ENR requests that can be 83 // performed by a disc/v4 source. 84 maxParallelENRRequests = 16 85 ) 86 87 // Config contains the configuration options of the ETH protocol. 88 // Deprecated: use ethconfig.Config instead. 89 type Config = ethconfig.Config 90 91 // Ethereum implements the Ethereum full node service. 92 type Ethereum struct { 93 // core protocol objects 94 config *ethconfig.Config 95 txPool *txpool.TxPool 96 blobTxPool *blobpool.BlobPool 97 localTxTracker *locals.TxTracker 98 blockchain *core.BlockChain 99 100 handler *handler 101 discmix *enode.FairMix 102 dropper *dropper 103 104 // DB interfaces 105 chainDb ethdb.Database // Block chain database 106 107 eventMux *event.TypeMux 108 engine consensus.Engine 109 accountManager *accounts.Manager 110 111 filterMaps *filtermaps.FilterMaps 112 closeFilterMaps chan chan struct{} 113 114 APIBackend *EthAPIBackend 115 116 miner *miner.Miner 117 gasPrice *big.Int 118 119 networkID uint64 120 netRPCService *ethapi.NetAPI 121 122 p2pServer *p2p.Server 123 124 lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase) 125 126 shutdownTracker *shutdowncheck.ShutdownTracker // Tracks if and when the node has shutdown ungracefully 127 } 128 129 // New creates a new Ethereum object (including the initialisation of the common Ethereum object), 130 // whose lifecycle will be managed by the provided node. 131 func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { 132 // Ensure configuration values are compatible and sane 133 if !config.SyncMode.IsValid() { 134 return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode) 135 } 136 if !config.HistoryMode.IsValid() { 137 return nil, fmt.Errorf("invalid history mode %d", config.HistoryMode) 138 } 139 if config.Miner.GasPrice == nil || config.Miner.GasPrice.Sign() <= 0 { 140 log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", ethconfig.Defaults.Miner.GasPrice) 141 config.Miner.GasPrice = new(big.Int).Set(ethconfig.Defaults.Miner.GasPrice) 142 } 143 if config.NoPruning && config.TrieDirtyCache > 0 && config.StateScheme == rawdb.HashScheme { 144 if config.SnapshotCache > 0 { 145 config.TrieCleanCache += config.TrieDirtyCache * 3 / 5 146 config.SnapshotCache += config.TrieDirtyCache * 2 / 5 147 } else { 148 config.TrieCleanCache += config.TrieDirtyCache 149 } 150 config.TrieDirtyCache = 0 151 } 152 log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024) 153 154 dbOptions := node.DatabaseOptions{ 155 Cache: config.DatabaseCache, 156 Handles: config.DatabaseHandles, 157 AncientsDirectory: config.DatabaseFreezer, 158 EraDirectory: config.DatabaseEra, 159 MetricsNamespace: "eth/db/chaindata/", 160 } 161 chainDb, err := stack.OpenDatabaseWithOptions("chaindata", dbOptions) 162 if err != nil { 163 return nil, err 164 } 165 scheme, err := rawdb.ParseStateScheme(config.StateScheme, chainDb) 166 if err != nil { 167 return nil, err 168 } 169 // Try to recover offline state pruning only in hash-based. 170 if scheme == rawdb.HashScheme { 171 if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb); err != nil { 172 log.Error("Failed to recover state", "error", err) 173 } 174 } 175 176 // Here we determine genesis hash and active ChainConfig. 177 // We need these to figure out the consensus parameters and to set up history pruning. 178 chainConfig, _, err := core.LoadChainConfig(chainDb, config.Genesis) 179 if err != nil { 180 return nil, err 181 } 182 engine, err := ethconfig.CreateConsensusEngine(chainConfig, chainDb) 183 if err != nil { 184 return nil, err 185 } 186 // Set networkID to chainID by default. 187 networkID := config.NetworkId 188 if networkID == 0 { 189 networkID = chainConfig.ChainID.Uint64() 190 } 191 192 // Assemble the Ethereum object. 193 eth := &Ethereum{ 194 config: config, 195 chainDb: chainDb, 196 eventMux: stack.EventMux(), 197 accountManager: stack.AccountManager(), 198 engine: engine, 199 networkID: networkID, 200 gasPrice: config.Miner.GasPrice, 201 p2pServer: stack.Server(), 202 discmix: enode.NewFairMix(discmixTimeout), 203 shutdownTracker: shutdowncheck.NewShutdownTracker(chainDb), 204 } 205 bcVersion := rawdb.ReadDatabaseVersion(chainDb) 206 var dbVer = "<nil>" 207 if bcVersion != nil { 208 dbVer = fmt.Sprintf("%d", *bcVersion) 209 } 210 log.Info("Initialising Ethereum protocol", "network", networkID, "dbversion", dbVer) 211 212 // Create BlockChain object. 213 if !config.SkipBcVersionCheck { 214 if bcVersion != nil && *bcVersion > core.BlockChainVersion { 215 return nil, fmt.Errorf("database version is v%d, Geth %s only supports v%d", *bcVersion, version.WithMeta, core.BlockChainVersion) 216 } else if bcVersion == nil || *bcVersion < core.BlockChainVersion { 217 if bcVersion != nil { // only print warning on upgrade, not on init 218 log.Warn("Upgrade blockchain database version", "from", dbVer, "to", core.BlockChainVersion) 219 } 220 rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion) 221 } 222 } 223 var ( 224 options = &core.BlockChainConfig{ 225 TrieCleanLimit: config.TrieCleanCache, 226 NoPrefetch: config.NoPrefetch, 227 TrieDirtyLimit: config.TrieDirtyCache, 228 ArchiveMode: config.NoPruning, 229 TrieTimeLimit: config.TrieTimeout, 230 SnapshotLimit: config.SnapshotCache, 231 Preimages: config.Preimages, 232 StateHistory: config.StateHistory, 233 StateScheme: scheme, 234 ChainHistoryMode: config.HistoryMode, 235 TxLookupLimit: int64(min(config.TransactionHistory, math.MaxInt64)), 236 VmConfig: vm.Config{ 237 EnablePreimageRecording: config.EnablePreimageRecording, 238 }, 239 } 240 ) 241 242 if config.VMTrace != "" { 243 traceConfig := json.RawMessage("{}") 244 if config.VMTraceJsonConfig != "" { 245 traceConfig = json.RawMessage(config.VMTraceJsonConfig) 246 } 247 t, err := tracers.LiveDirectory.New(config.VMTrace, traceConfig) 248 if err != nil { 249 return nil, fmt.Errorf("failed to create tracer %s: %v", config.VMTrace, err) 250 } 251 options.VmConfig.Tracer = t 252 } 253 // Override the chain config with provided settings. 254 var overrides core.ChainOverrides 255 if config.OverrideOsaka != nil { 256 overrides.OverrideOsaka = config.OverrideOsaka 257 } 258 if config.OverrideVerkle != nil { 259 overrides.OverrideVerkle = config.OverrideVerkle 260 } 261 options.Overrides = &overrides 262 263 eth.blockchain, err = core.NewBlockChain(chainDb, config.Genesis, eth.engine, options) 264 if err != nil { 265 return nil, err 266 } 267 268 // Initialize filtermaps log index. 269 fmConfig := filtermaps.Config{ 270 History: config.LogHistory, 271 Disabled: config.LogNoHistory, 272 ExportFileName: config.LogExportCheckpoints, 273 HashScheme: scheme == rawdb.HashScheme, 274 } 275 chainView := eth.newChainView(eth.blockchain.CurrentBlock()) 276 historyCutoff, _ := eth.blockchain.HistoryPruningCutoff() 277 var finalBlock uint64 278 if fb := eth.blockchain.CurrentFinalBlock(); fb != nil { 279 finalBlock = fb.Number.Uint64() 280 } 281 filterMaps, err := filtermaps.NewFilterMaps(chainDb, chainView, historyCutoff, finalBlock, filtermaps.DefaultParams, fmConfig) 282 if err != nil { 283 return nil, err 284 } 285 eth.filterMaps = filterMaps 286 eth.closeFilterMaps = make(chan chan struct{}) 287 288 // TxPool 289 if config.TxPool.Journal != "" { 290 config.TxPool.Journal = stack.ResolvePath(config.TxPool.Journal) 291 } 292 legacyPool := legacypool.New(config.TxPool, eth.blockchain) 293 294 if config.BlobPool.Datadir != "" { 295 config.BlobPool.Datadir = stack.ResolvePath(config.BlobPool.Datadir) 296 } 297 eth.blobTxPool = blobpool.New(config.BlobPool, eth.blockchain, legacyPool.HasPendingAuth) 298 299 eth.txPool, err = txpool.New(config.TxPool.PriceLimit, eth.blockchain, []txpool.SubPool{legacyPool, eth.blobTxPool}) 300 if err != nil { 301 return nil, err 302 } 303 304 if !config.TxPool.NoLocals { 305 rejournal := config.TxPool.Rejournal 306 if rejournal < time.Second { 307 log.Warn("Sanitizing invalid txpool journal time", "provided", rejournal, "updated", time.Second) 308 rejournal = time.Second 309 } 310 eth.localTxTracker = locals.New(config.TxPool.Journal, rejournal, eth.blockchain.Config(), eth.txPool) 311 stack.RegisterLifecycle(eth.localTxTracker) 312 } 313 314 // Permit the downloader to use the trie cache allowance during fast sync 315 cacheLimit := options.TrieCleanLimit + options.TrieDirtyLimit + options.SnapshotLimit 316 if eth.handler, err = newHandler(&handlerConfig{ 317 NodeID: eth.p2pServer.Self().ID(), 318 Database: chainDb, 319 Chain: eth.blockchain, 320 TxPool: eth.txPool, 321 Network: networkID, 322 Sync: config.SyncMode, 323 BloomCache: uint64(cacheLimit), 324 EventMux: eth.eventMux, 325 RequiredBlocks: config.RequiredBlocks, 326 }); err != nil { 327 return nil, err 328 } 329 330 eth.dropper = newDropper(eth.p2pServer.MaxDialedConns(), eth.p2pServer.MaxInboundConns()) 331 332 eth.miner = miner.New(eth, config.Miner, eth.engine) 333 eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData)) 334 eth.miner.SetPrioAddresses(config.TxPool.Locals) 335 336 eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil} 337 if eth.APIBackend.allowUnprotectedTxs { 338 log.Info("Unprotected transactions allowed") 339 } 340 eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, config.GPO, config.Miner.GasPrice) 341 342 // Start the RPC service 343 eth.netRPCService = ethapi.NewNetAPI(eth.p2pServer, networkID) 344 345 // Register the backend on the node 346 stack.RegisterAPIs(eth.APIs()) 347 stack.RegisterProtocols(eth.Protocols()) 348 stack.RegisterLifecycle(eth) 349 350 // Successful startup; push a marker and check previous unclean shutdowns. 351 eth.shutdownTracker.MarkStartup() 352 353 return eth, nil 354 } 355 356 func makeExtraData(extra []byte) []byte { 357 if len(extra) == 0 { 358 // create default extradata 359 extra, _ = rlp.EncodeToBytes([]interface{}{ 360 uint(gethversion.Major<<16 | gethversion.Minor<<8 | gethversion.Patch), 361 "geth", 362 runtime.Version(), 363 runtime.GOOS, 364 }) 365 } 366 if uint64(len(extra)) > params.MaximumExtraDataSize { 367 log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", params.MaximumExtraDataSize) 368 extra = nil 369 } 370 return extra 371 } 372 373 // APIs return the collection of RPC services the ethereum package offers. 374 // NOTE, some of these services probably need to be moved to somewhere else. 375 func (s *Ethereum) APIs() []rpc.API { 376 apis := ethapi.GetAPIs(s.APIBackend) 377 378 // Append all the local APIs and return 379 return append(apis, []rpc.API{ 380 { 381 Namespace: "miner", 382 Service: NewMinerAPI(s), 383 }, { 384 Namespace: "eth", 385 Service: downloader.NewDownloaderAPI(s.handler.downloader, s.blockchain, s.eventMux), 386 }, { 387 Namespace: "admin", 388 Service: NewAdminAPI(s), 389 }, { 390 Namespace: "debug", 391 Service: NewDebugAPI(s), 392 }, { 393 Namespace: "net", 394 Service: s.netRPCService, 395 }, 396 }...) 397 } 398 399 func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) { 400 s.blockchain.ResetWithGenesisBlock(gb) 401 } 402 403 func (s *Ethereum) Miner() *miner.Miner { return s.miner } 404 405 func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager } 406 func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain } 407 func (s *Ethereum) TxPool() *txpool.TxPool { return s.txPool } 408 func (s *Ethereum) BlobTxPool() *blobpool.BlobPool { return s.blobTxPool } 409 func (s *Ethereum) Engine() consensus.Engine { return s.engine } 410 func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } 411 func (s *Ethereum) IsListening() bool { return true } // Always listening 412 func (s *Ethereum) Downloader() *downloader.Downloader { return s.handler.downloader } 413 func (s *Ethereum) Synced() bool { return s.handler.synced.Load() } 414 func (s *Ethereum) SetSynced() { s.handler.enableSyncedFeatures() } 415 func (s *Ethereum) ArchiveMode() bool { return s.config.NoPruning } 416 417 // Protocols returns all the currently configured 418 // network protocols to start. 419 func (s *Ethereum) Protocols() []p2p.Protocol { 420 protos := eth.MakeProtocols((*ethHandler)(s.handler), s.networkID, s.discmix) 421 if s.config.SnapshotCache > 0 { 422 protos = append(protos, snap.MakeProtocols((*snapHandler)(s.handler))...) 423 } 424 return protos 425 } 426 427 // Start implements node.Lifecycle, starting all internal goroutines needed by the 428 // Ethereum protocol implementation. 429 func (s *Ethereum) Start() error { 430 if err := s.setupDiscovery(); err != nil { 431 return err 432 } 433 434 // Regularly update shutdown marker 435 s.shutdownTracker.Start() 436 437 // Start the networking layer 438 s.handler.Start(s.p2pServer.MaxPeers) 439 440 // Start the connection manager 441 s.dropper.Start(s.p2pServer, func() bool { return !s.Synced() }) 442 443 // start log indexer 444 s.filterMaps.Start() 445 go s.updateFilterMapsHeads() 446 return nil 447 } 448 449 func (s *Ethereum) newChainView(head *types.Header) *filtermaps.ChainView { 450 if head == nil { 451 return nil 452 } 453 return filtermaps.NewChainView(s.blockchain, head.Number.Uint64(), head.Hash()) 454 } 455 456 func (s *Ethereum) updateFilterMapsHeads() { 457 headEventCh := make(chan core.ChainEvent, 10) 458 blockProcCh := make(chan bool, 10) 459 sub := s.blockchain.SubscribeChainEvent(headEventCh) 460 sub2 := s.blockchain.SubscribeBlockProcessingEvent(blockProcCh) 461 defer func() { 462 sub.Unsubscribe() 463 sub2.Unsubscribe() 464 for { 465 select { 466 case <-headEventCh: 467 case <-blockProcCh: 468 default: 469 return 470 } 471 } 472 }() 473 474 var head *types.Header 475 setHead := func(newHead *types.Header) { 476 if newHead == nil { 477 return 478 } 479 if head == nil || newHead.Hash() != head.Hash() { 480 head = newHead 481 chainView := s.newChainView(head) 482 historyCutoff, _ := s.blockchain.HistoryPruningCutoff() 483 var finalBlock uint64 484 if fb := s.blockchain.CurrentFinalBlock(); fb != nil { 485 finalBlock = fb.Number.Uint64() 486 } 487 s.filterMaps.SetTarget(chainView, historyCutoff, finalBlock) 488 } 489 } 490 setHead(s.blockchain.CurrentBlock()) 491 492 for { 493 select { 494 case ev := <-headEventCh: 495 setHead(ev.Header) 496 case blockProc := <-blockProcCh: 497 s.filterMaps.SetBlockProcessing(blockProc) 498 case <-time.After(time.Second * 10): 499 setHead(s.blockchain.CurrentBlock()) 500 case ch := <-s.closeFilterMaps: 501 close(ch) 502 return 503 } 504 } 505 } 506 507 func (s *Ethereum) setupDiscovery() error { 508 eth.StartENRUpdater(s.blockchain, s.p2pServer.LocalNode()) 509 510 // Add eth nodes from DNS. 511 dnsclient := dnsdisc.NewClient(dnsdisc.Config{}) 512 if len(s.config.EthDiscoveryURLs) > 0 { 513 iter, err := dnsclient.NewIterator(s.config.EthDiscoveryURLs...) 514 if err != nil { 515 return err 516 } 517 s.discmix.AddSource(iter) 518 } 519 520 // Add snap nodes from DNS. 521 if len(s.config.SnapDiscoveryURLs) > 0 { 522 iter, err := dnsclient.NewIterator(s.config.SnapDiscoveryURLs...) 523 if err != nil { 524 return err 525 } 526 s.discmix.AddSource(iter) 527 } 528 529 // Add DHT nodes from discv4. 530 if s.p2pServer.DiscoveryV4() != nil { 531 iter := s.p2pServer.DiscoveryV4().RandomNodes() 532 resolverFunc := func(ctx context.Context, enr *enode.Node) *enode.Node { 533 // RequestENR does not yet support context. It will simply time out. 534 // If the ENR can't be resolved, RequestENR will return nil. We don't 535 // care about the specific error here, so we ignore it. 536 nn, _ := s.p2pServer.DiscoveryV4().RequestENR(enr) 537 return nn 538 } 539 iter = enode.AsyncFilter(iter, resolverFunc, maxParallelENRRequests) 540 iter = enode.Filter(iter, eth.NewNodeFilter(s.blockchain)) 541 iter = enode.NewBufferIter(iter, discoveryPrefetchBuffer) 542 s.discmix.AddSource(iter) 543 } 544 545 // Add DHT nodes from discv5. 546 if s.p2pServer.DiscoveryV5() != nil { 547 filter := eth.NewNodeFilter(s.blockchain) 548 iter := enode.Filter(s.p2pServer.DiscoveryV5().RandomNodes(), filter) 549 iter = enode.NewBufferIter(iter, discoveryPrefetchBuffer) 550 s.discmix.AddSource(iter) 551 } 552 553 return nil 554 } 555 556 // Stop implements node.Lifecycle, terminating all internal goroutines used by the 557 // Ethereum protocol. 558 func (s *Ethereum) Stop() error { 559 // Stop all the peer-related stuff first. 560 s.discmix.Close() 561 s.dropper.Stop() 562 s.handler.Stop() 563 564 // Then stop everything else. 565 ch := make(chan struct{}) 566 s.closeFilterMaps <- ch 567 <-ch 568 s.filterMaps.Stop() 569 s.txPool.Close() 570 s.blockchain.Stop() 571 s.engine.Close() 572 573 // Clean shutdown marker as the last thing before closing db 574 s.shutdownTracker.Stop() 575 576 s.chainDb.Close() 577 s.eventMux.Stop() 578 579 return nil 580 } 581 582 // SyncMode retrieves the current sync mode, either explicitly set, or derived 583 // from the chain status. 584 func (s *Ethereum) SyncMode() ethconfig.SyncMode { 585 // If we're in snap sync mode, return that directly 586 if s.handler.snapSync.Load() { 587 return ethconfig.SnapSync 588 } 589 // We are probably in full sync, but we might have rewound to before the 590 // snap sync pivot, check if we should re-enable snap sync. 591 head := s.blockchain.CurrentBlock() 592 if pivot := rawdb.ReadLastPivotNumber(s.chainDb); pivot != nil { 593 if head.Number.Uint64() < *pivot { 594 return ethconfig.SnapSync 595 } 596 } 597 // We are in a full sync, but the associated head state is missing. To complete 598 // the head state, forcefully rerun the snap sync. Note it doesn't mean the 599 // persistent state is corrupted, just mismatch with the head block. 600 if !s.blockchain.HasState(head.Root) { 601 log.Info("Reenabled snap sync as chain is stateless") 602 return ethconfig.SnapSync 603 } 604 // Nope, we're really full syncing 605 return ethconfig.FullSync 606 }