github.com/shrimpyuk/bor@v0.2.15-0.20220224151350-fb4ec6020bae/eth/sync.go (about) 1 // Copyright 2015 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 18 19 import ( 20 "math/big" 21 "sync/atomic" 22 "time" 23 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/core/rawdb" 26 "github.com/ethereum/go-ethereum/core/types" 27 "github.com/ethereum/go-ethereum/eth/downloader" 28 "github.com/ethereum/go-ethereum/eth/protocols/eth" 29 "github.com/ethereum/go-ethereum/log" 30 ) 31 32 const ( 33 forceSyncCycle = 10 * time.Second // Time interval to force syncs, even if few peers are available 34 defaultMinSyncPeers = 5 // Amount of peers desired to start syncing 35 ) 36 37 // syncTransactions starts sending all currently pending transactions to the given peer. 38 func (h *handler) syncTransactions(p *eth.Peer) { 39 // Assemble the set of transaction to broadcast or announce to the remote 40 // peer. Fun fact, this is quite an expensive operation as it needs to sort 41 // the transactions if the sorting is not cached yet. However, with a random 42 // order, insertions could overflow the non-executable queues and get dropped. 43 // 44 // TODO(karalabe): Figure out if we could get away with random order somehow 45 var txs types.Transactions 46 pending := h.txpool.Pending(false) 47 for _, batch := range pending { 48 txs = append(txs, batch...) 49 } 50 if len(txs) == 0 { 51 return 52 } 53 // The eth/65 protocol introduces proper transaction announcements, so instead 54 // of dripping transactions across multiple peers, just send the entire list as 55 // an announcement and let the remote side decide what they need (likely nothing). 56 hashes := make([]common.Hash, len(txs)) 57 for i, tx := range txs { 58 hashes[i] = tx.Hash() 59 } 60 p.AsyncSendPooledTransactionHashes(hashes) 61 } 62 63 // chainSyncer coordinates blockchain sync components. 64 type chainSyncer struct { 65 handler *handler 66 force *time.Timer 67 forced bool // true when force timer fired 68 peerEventCh chan struct{} 69 doneCh chan error // non-nil when sync is running 70 } 71 72 // chainSyncOp is a scheduled sync operation. 73 type chainSyncOp struct { 74 mode downloader.SyncMode 75 peer *eth.Peer 76 td *big.Int 77 head common.Hash 78 } 79 80 // newChainSyncer creates a chainSyncer. 81 func newChainSyncer(handler *handler) *chainSyncer { 82 return &chainSyncer{ 83 handler: handler, 84 peerEventCh: make(chan struct{}), 85 } 86 } 87 88 // handlePeerEvent notifies the syncer about a change in the peer set. 89 // This is called for new peers and every time a peer announces a new 90 // chain head. 91 func (cs *chainSyncer) handlePeerEvent(peer *eth.Peer) bool { 92 select { 93 case cs.peerEventCh <- struct{}{}: 94 return true 95 case <-cs.handler.quitSync: 96 return false 97 } 98 } 99 100 // loop runs in its own goroutine and launches the sync when necessary. 101 func (cs *chainSyncer) loop() { 102 defer cs.handler.wg.Done() 103 104 cs.handler.blockFetcher.Start() 105 cs.handler.txFetcher.Start() 106 defer cs.handler.blockFetcher.Stop() 107 defer cs.handler.txFetcher.Stop() 108 defer cs.handler.downloader.Terminate() 109 110 // The force timer lowers the peer count threshold down to one when it fires. 111 // This ensures we'll always start sync even if there aren't enough peers. 112 cs.force = time.NewTimer(forceSyncCycle) 113 defer cs.force.Stop() 114 115 for { 116 if op := cs.nextSyncOp(); op != nil { 117 cs.startSync(op) 118 } 119 select { 120 case <-cs.peerEventCh: 121 // Peer information changed, recheck. 122 case <-cs.doneCh: 123 cs.doneCh = nil 124 cs.force.Reset(forceSyncCycle) 125 cs.forced = false 126 case <-cs.force.C: 127 cs.forced = true 128 129 case <-cs.handler.quitSync: 130 // Disable all insertion on the blockchain. This needs to happen before 131 // terminating the downloader because the downloader waits for blockchain 132 // inserts, and these can take a long time to finish. 133 cs.handler.chain.StopInsert() 134 cs.handler.downloader.Terminate() 135 if cs.doneCh != nil { 136 <-cs.doneCh 137 } 138 return 139 } 140 } 141 } 142 143 // nextSyncOp determines whether sync is required at this time. 144 func (cs *chainSyncer) nextSyncOp() *chainSyncOp { 145 if cs.doneCh != nil { 146 return nil // Sync already running. 147 } 148 149 // Ensure we're at minimum peer count. 150 minPeers := defaultMinSyncPeers 151 if cs.forced { 152 minPeers = 1 153 } else if minPeers > cs.handler.maxPeers { 154 minPeers = cs.handler.maxPeers 155 } 156 if cs.handler.peers.len() < minPeers { 157 return nil 158 } 159 // We have enough peers, check TD 160 peer := cs.handler.peers.peerWithHighestTD() 161 if peer == nil { 162 return nil 163 } 164 mode, ourTD := cs.modeAndLocalHead() 165 if mode == downloader.FastSync && atomic.LoadUint32(&cs.handler.snapSync) == 1 { 166 // Fast sync via the snap protocol 167 mode = downloader.SnapSync 168 } 169 op := peerToSyncOp(mode, peer) 170 if op.td.Cmp(ourTD) <= 0 { 171 return nil // We're in sync. 172 } 173 return op 174 } 175 176 func peerToSyncOp(mode downloader.SyncMode, p *eth.Peer) *chainSyncOp { 177 peerHead, peerTD := p.Head() 178 return &chainSyncOp{mode: mode, peer: p, td: peerTD, head: peerHead} 179 } 180 181 func (cs *chainSyncer) modeAndLocalHead() (downloader.SyncMode, *big.Int) { 182 // If we're in fast sync mode, return that directly 183 if atomic.LoadUint32(&cs.handler.fastSync) == 1 { 184 block := cs.handler.chain.CurrentFastBlock() 185 td := cs.handler.chain.GetTd(block.Hash(), block.NumberU64()) 186 return downloader.FastSync, td 187 } 188 // We are probably in full sync, but we might have rewound to before the 189 // fast sync pivot, check if we should reenable 190 if pivot := rawdb.ReadLastPivotNumber(cs.handler.database); pivot != nil { 191 if head := cs.handler.chain.CurrentBlock(); head.NumberU64() < *pivot { 192 block := cs.handler.chain.CurrentFastBlock() 193 td := cs.handler.chain.GetTd(block.Hash(), block.NumberU64()) 194 return downloader.FastSync, td 195 } 196 } 197 // Nope, we're really full syncing 198 head := cs.handler.chain.CurrentBlock() 199 td := cs.handler.chain.GetTd(head.Hash(), head.NumberU64()) 200 return downloader.FullSync, td 201 } 202 203 // startSync launches doSync in a new goroutine. 204 func (cs *chainSyncer) startSync(op *chainSyncOp) { 205 cs.doneCh = make(chan error, 1) 206 go func() { cs.doneCh <- cs.handler.doSync(op) }() 207 } 208 209 // doSync synchronizes the local blockchain with a remote peer. 210 func (h *handler) doSync(op *chainSyncOp) error { 211 if op.mode == downloader.FastSync || op.mode == downloader.SnapSync { 212 // Before launch the fast sync, we have to ensure user uses the same 213 // txlookup limit. 214 // The main concern here is: during the fast sync Geth won't index the 215 // block(generate tx indices) before the HEAD-limit. But if user changes 216 // the limit in the next fast sync(e.g. user kill Geth manually and 217 // restart) then it will be hard for Geth to figure out the oldest block 218 // has been indexed. So here for the user-experience wise, it's non-optimal 219 // that user can't change limit during the fast sync. If changed, Geth 220 // will just blindly use the original one. 221 limit := h.chain.TxLookupLimit() 222 if stored := rawdb.ReadFastTxLookupLimit(h.database); stored == nil { 223 rawdb.WriteFastTxLookupLimit(h.database, limit) 224 } else if *stored != limit { 225 h.chain.SetTxLookupLimit(*stored) 226 log.Warn("Update txLookup limit", "provided", limit, "updated", *stored) 227 } 228 } 229 // Run the sync cycle, and disable fast sync if we're past the pivot block 230 err := h.downloader.Synchronise(op.peer.ID(), op.head, op.td, op.mode) 231 if err != nil { 232 return err 233 } 234 if atomic.LoadUint32(&h.fastSync) == 1 { 235 log.Info("Fast sync complete, auto disabling") 236 atomic.StoreUint32(&h.fastSync, 0) 237 } 238 if atomic.LoadUint32(&h.snapSync) == 1 { 239 log.Info("Snap sync complete, auto disabling") 240 atomic.StoreUint32(&h.snapSync, 0) 241 } 242 // If we've successfully finished a sync cycle and passed any required checkpoint, 243 // enable accepting transactions from the network. 244 head := h.chain.CurrentBlock() 245 if head.NumberU64() >= h.checkpointNumber { 246 // Checkpoint passed, sanity check the timestamp to have a fallback mechanism 247 // for non-checkpointed (number = 0) private networks. 248 if head.Time() >= uint64(time.Now().AddDate(0, -1, 0).Unix()) { 249 atomic.StoreUint32(&h.acceptTxs, 1) 250 } 251 } 252 if head.NumberU64() > 0 { 253 // We've completed a sync cycle, notify all peers of new state. This path is 254 // essential in star-topology networks where a gateway node needs to notify 255 // all its out-of-date peers of the availability of a new block. This failure 256 // scenario will most often crop up in private and hackathon networks with 257 // degenerate connectivity, but it should be healthy for the mainnet too to 258 // more reliably update peers or the local TD state. 259 h.BroadcastBlock(head, false) 260 } 261 return nil 262 }