| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // Copyright 2015 The go-ethereum Authors | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // This file is part of the go-ethereum library. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-23 18:35:11 +02:00
										 |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | package eth | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2015-06-09 12:03:14 +02:00
										 |  |  | 	"math/rand" | 
					
						
							| 
									
										
										
										
											2016-05-17 14:17:20 +03:00
										 |  |  | 	"sync/atomic" | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-08 19:24:56 +03:00
										 |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							| 
									
										
										
										
											2015-05-26 14:00:21 +03:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core/types" | 
					
						
							| 
									
										
										
										
											2015-10-13 12:04:25 +03:00
										 |  |  | 	"github.com/ethereum/go-ethereum/eth/downloader" | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/logger" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/logger/glog" | 
					
						
							| 
									
										
										
										
											2015-06-09 12:03:14 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/p2p/discover" | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-08 20:38:39 +03:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2015-06-16 11:58:32 +03:00
										 |  |  | 	forceSyncCycle      = 10 * time.Second // Time interval to force syncs, even if few peers are available | 
					
						
							|  |  |  | 	minDesiredPeerCount = 5                // Amount of peers desired to start syncing | 
					
						
							| 
									
										
										
										
											2015-06-09 12:03:14 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// This is the target size for the packs of transactions sent by txsyncLoop. | 
					
						
							|  |  |  | 	// A pack can get larger than this if a single transactions exceeds this size. | 
					
						
							|  |  |  | 	txsyncPackSize = 100 * 1024 | 
					
						
							| 
									
										
										
										
											2015-06-08 20:38:39 +03:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-09 12:03:14 +02:00
										 |  |  | type txsync struct { | 
					
						
							|  |  |  | 	p   *peer | 
					
						
							|  |  |  | 	txs []*types.Transaction | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // syncTransactions starts sending all currently pending transactions to the given peer. | 
					
						
							|  |  |  | func (pm *ProtocolManager) syncTransactions(p *peer) { | 
					
						
							| 
									
										
										
										
											2016-07-01 18:59:55 +03:00
										 |  |  | 	var txs types.Transactions | 
					
						
							|  |  |  | 	for _, batch := range pm.txpool.Pending() { | 
					
						
							|  |  |  | 		txs = append(txs, batch...) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-06-09 12:03:14 +02:00
										 |  |  | 	if len(txs) == 0 { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	select { | 
					
						
							|  |  |  | 	case pm.txsyncCh <- &txsync{p, txs}: | 
					
						
							|  |  |  | 	case <-pm.quitSync: | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // txsyncLoop takes care of the initial transaction sync for each new | 
					
						
							|  |  |  | // connection. When a new peer appears, we relay all currently pending | 
					
						
							|  |  |  | // transactions. In order to minimise egress bandwidth usage, we send | 
					
						
							|  |  |  | // the transactions in small packs to one peer at a time. | 
					
						
							|  |  |  | func (pm *ProtocolManager) txsyncLoop() { | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		pending = make(map[discover.NodeID]*txsync) | 
					
						
							|  |  |  | 		sending = false               // whether a send is active | 
					
						
							|  |  |  | 		pack    = new(txsync)         // the pack that is being sent | 
					
						
							|  |  |  | 		done    = make(chan error, 1) // result of the send | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// send starts a sending a pack of transactions from the sync. | 
					
						
							|  |  |  | 	send := func(s *txsync) { | 
					
						
							|  |  |  | 		// Fill pack with transactions up to the target size. | 
					
						
							|  |  |  | 		size := common.StorageSize(0) | 
					
						
							|  |  |  | 		pack.p = s.p | 
					
						
							|  |  |  | 		pack.txs = pack.txs[:0] | 
					
						
							|  |  |  | 		for i := 0; i < len(s.txs) && size < txsyncPackSize; i++ { | 
					
						
							|  |  |  | 			pack.txs = append(pack.txs, s.txs[i]) | 
					
						
							|  |  |  | 			size += s.txs[i].Size() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Remove the transactions that will be sent. | 
					
						
							|  |  |  | 		s.txs = s.txs[:copy(s.txs, s.txs[len(pack.txs):])] | 
					
						
							|  |  |  | 		if len(s.txs) == 0 { | 
					
						
							|  |  |  | 			delete(pending, s.p.ID()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Send the pack in the background. | 
					
						
							|  |  |  | 		glog.V(logger.Detail).Infof("%v: sending %d transactions (%v)", s.p.Peer, len(pack.txs), size) | 
					
						
							|  |  |  | 		sending = true | 
					
						
							| 
									
										
										
										
											2015-06-29 12:44:00 +03:00
										 |  |  | 		go func() { done <- pack.p.SendTransactions(pack.txs) }() | 
					
						
							| 
									
										
										
										
											2015-06-09 12:03:14 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// pick chooses the next pending sync. | 
					
						
							|  |  |  | 	pick := func() *txsync { | 
					
						
							|  |  |  | 		if len(pending) == 0 { | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		n := rand.Intn(len(pending)) + 1 | 
					
						
							|  |  |  | 		for _, s := range pending { | 
					
						
							|  |  |  | 			if n--; n == 0 { | 
					
						
							|  |  |  | 				return s | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case s := <-pm.txsyncCh: | 
					
						
							|  |  |  | 			pending[s.p.ID()] = s | 
					
						
							|  |  |  | 			if !sending { | 
					
						
							|  |  |  | 				send(s) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case err := <-done: | 
					
						
							|  |  |  | 			sending = false | 
					
						
							|  |  |  | 			// Stop tracking peers that cause send failures. | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				glog.V(logger.Debug).Infof("%v: tx send failed: %v", pack.p.Peer, err) | 
					
						
							|  |  |  | 				delete(pending, pack.p.ID()) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Schedule the next send. | 
					
						
							|  |  |  | 			if s := pick(); s != nil { | 
					
						
							|  |  |  | 				send(s) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case <-pm.quitSync: | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-08 19:24:56 +03:00
										 |  |  | // syncer is responsible for periodically synchronising with the network, both | 
					
						
							| 
									
										
										
										
											2015-06-16 11:58:32 +03:00
										 |  |  | // downloading hashes and blocks as well as handling the announcement handler. | 
					
						
							| 
									
										
										
										
											2015-06-08 19:24:56 +03:00
										 |  |  | func (pm *ProtocolManager) syncer() { | 
					
						
							| 
									
										
										
										
											2015-06-16 11:58:32 +03:00
										 |  |  | 	// Start and ensure cleanup of sync mechanisms | 
					
						
							|  |  |  | 	pm.fetcher.Start() | 
					
						
							|  |  |  | 	defer pm.fetcher.Stop() | 
					
						
							| 
									
										
										
										
											2015-06-18 00:04:57 +03:00
										 |  |  | 	defer pm.downloader.Terminate() | 
					
						
							| 
									
										
										
										
											2015-05-01 16:30:02 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-16 11:58:32 +03:00
										 |  |  | 	// Wait for different events to fire synchronisation operations | 
					
						
							| 
									
										
										
										
											2015-06-12 13:35:29 +03:00
										 |  |  | 	forceSync := time.Tick(forceSyncCycle) | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | 	for { | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case <-pm.newPeerCh: | 
					
						
							| 
									
										
										
										
											2015-05-18 21:33:37 +03:00
										 |  |  | 			// Make sure we have peers to select from, then sync | 
					
						
							|  |  |  | 			if pm.peers.Len() < minDesiredPeerCount { | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-05-18 21:33:37 +03:00
										 |  |  | 			go pm.synchronise(pm.peers.BestPeer()) | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-08 15:22:48 +03:00
										 |  |  | 		case <-forceSync: | 
					
						
							|  |  |  | 			// Force a sync even if not enough peers are present | 
					
						
							| 
									
										
										
										
											2015-05-18 21:33:37 +03:00
										 |  |  | 			go pm.synchronise(pm.peers.BestPeer()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 03:08:16 +02:00
										 |  |  | 		case <-pm.noMorePeers: | 
					
						
							| 
									
										
										
										
											2015-05-01 16:30:02 +02:00
										 |  |  | 			return | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-16 11:58:32 +03:00
										 |  |  | // synchronise tries to sync up our local block chain with a remote peer. | 
					
						
							| 
									
										
										
										
											2015-05-08 15:22:48 +03:00
										 |  |  | func (pm *ProtocolManager) synchronise(peer *peer) { | 
					
						
							| 
									
										
										
										
											2015-05-18 21:33:37 +03:00
										 |  |  | 	// Short circuit if no peers are available | 
					
						
							|  |  |  | 	if peer == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-25 15:14:14 +03:00
										 |  |  | 	// Make sure the peer's TD is higher than our own | 
					
						
							| 
									
										
										
										
											2016-04-05 15:22:04 +02:00
										 |  |  | 	currentBlock := pm.blockchain.CurrentBlock() | 
					
						
							|  |  |  | 	td := pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) | 
					
						
							| 
									
										
										
										
											2016-07-25 15:14:14 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	pHead, pTd := peer.Head() | 
					
						
							|  |  |  | 	if pTd.Cmp(td) <= 0 { | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-06-11 15:56:08 +03:00
										 |  |  | 	// Otherwise try to sync with the downloader | 
					
						
							| 
									
										
										
										
											2015-10-13 12:04:25 +03:00
										 |  |  | 	mode := downloader.FullSync | 
					
						
							| 
									
										
										
										
											2016-05-17 14:17:20 +03:00
										 |  |  | 	if atomic.LoadUint32(&pm.fastSync) == 1 { | 
					
						
							| 
									
										
										
										
											2015-10-13 12:04:25 +03:00
										 |  |  | 		mode = downloader.FastSync | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-25 15:14:14 +03:00
										 |  |  | 	if err := pm.downloader.Synchronise(peer.id, pHead, pTd, mode); err != nil { | 
					
						
							| 
									
										
										
										
											2015-10-28 16:41:01 +02:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-06-02 15:54:07 +03:00
										 |  |  | 	atomic.StoreUint32(&pm.synced, 1) // Mark initial sync done | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-13 12:04:25 +03:00
										 |  |  | 	// If fast sync was enabled, and we synced up, disable it | 
					
						
							| 
									
										
										
										
											2016-05-17 14:17:20 +03:00
										 |  |  | 	if atomic.LoadUint32(&pm.fastSync) == 1 { | 
					
						
							| 
									
										
										
										
											2015-10-28 16:41:01 +02:00
										 |  |  | 		// Disable fast sync if we indeed have something in our chain | 
					
						
							| 
									
										
										
										
											2015-10-13 12:04:25 +03:00
										 |  |  | 		if pm.blockchain.CurrentBlock().NumberU64() > 0 { | 
					
						
							|  |  |  | 			glog.V(logger.Info).Infof("fast sync complete, auto disabling") | 
					
						
							| 
									
										
										
										
											2016-05-17 14:17:20 +03:00
										 |  |  | 			atomic.StoreUint32(&pm.fastSync, 0) | 
					
						
							| 
									
										
										
										
											2015-10-13 12:04:25 +03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | } |