diff options
Diffstat (limited to 'eth/sync.go')
-rw-r--r-- | eth/sync.go | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/eth/sync.go b/eth/sync.go new file mode 100644 index 000000000..9e8b21a7c --- /dev/null +++ b/eth/sync.go @@ -0,0 +1,108 @@ +package eth + +import ( + "math" + "time" + + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +// Sync contains all synchronisation code for the eth protocol + +func (pm *ProtocolManager) update() { + // itimer is used to determine when to start ignoring `minDesiredPeerCount` + itimer := time.NewTimer(peerCountTimeout) + // btimer is used for picking of blocks from the downloader + btimer := time.Tick(blockProcTimer) + + for { + select { + case <-pm.newPeerCh: + // Meet the `minDesiredPeerCount` before we select our best peer + if len(pm.peers) < minDesiredPeerCount { + break + } + + // Find the best peer + peer := getBestPeer(pm.peers) + if peer == nil { + glog.V(logger.Debug).Infoln("Sync attempt cancelled. No peers available") + } + + itimer.Stop() + go pm.synchronise(peer) + case <-itimer.C: + // The timer will make sure that the downloader keeps an active state + // in which it attempts to always check the network for highest td peers + // Either select the peer or restart the timer if no peers could + // be selected. + if peer := getBestPeer(pm.peers); peer != nil { + go pm.synchronise(peer) + } else { + itimer.Reset(5 * time.Second) + } + case <-btimer: + go pm.processBlocks() + case <-pm.quitSync: + return + } + } +} + +// processBlocks will attempt to reconstruct a chain by checking the first item and check if it's +// a known parent. The first block in the chain may be unknown during downloading. When the +// downloader isn't downloading blocks will be dropped with an unknown parent until either it +// has depleted the list or found a known parent. +func (pm *ProtocolManager) processBlocks() error { + pm.wg.Add(1) + defer pm.wg.Done() + + blocks := pm.downloader.TakeBlocks() + if len(blocks) == 0 { + return nil + } + defer pm.downloader.Done() + + glog.V(logger.Debug).Infof("Inserting chain with %d blocks (#%v - #%v)\n", len(blocks), blocks[0].Number(), blocks[len(blocks)-1].Number()) + + for len(blocks) != 0 && !pm.quit { + max := int(math.Min(float64(len(blocks)), float64(blockProcAmount))) + _, err := pm.chainman.InsertChain(blocks[:max]) + if err != nil { + return err + } + blocks = blocks[max:] + } + return nil +} + +func (pm *ProtocolManager) synchronise(peer *peer) { + // Make sure the peer's TD is higher than our own. If not drop. + if peer.td.Cmp(pm.chainman.Td()) <= 0 { + return + } + // Check downloader if it's busy so it doesn't show the sync message + // for every attempty + if pm.downloader.IsBusy() { + return + } + + // FIXME if we have the hash in our chain and the TD of the peer is + // much higher than ours, something is wrong with us or the peer. + // Check if the hash is on our own chain + if pm.chainman.HasBlock(peer.recentHash) { + return + } + + // Get the hashes from the peer (synchronously) + err := pm.downloader.Synchronise(peer.id, peer.recentHash) + if err != nil && err == downloader.ErrBadPeer { + glog.V(logger.Debug).Infoln("removed peer from peer set due to bad action") + pm.removePeer(peer) + } else if err != nil { + // handle error + glog.V(logger.Detail).Infoln("error downloading:", err) + } +} |