github.com/dominant-strategies/go-quai@v0.28.2/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 "math/rand" 22 23 "github.com/dominant-strategies/go-quai/common" 24 "github.com/dominant-strategies/go-quai/core/types" 25 "github.com/dominant-strategies/go-quai/eth/downloader" 26 "github.com/dominant-strategies/go-quai/eth/protocols/eth" 27 "github.com/dominant-strategies/go-quai/log" 28 "github.com/dominant-strategies/go-quai/p2p/enode" 29 ) 30 31 const ( 32 defaultMinSyncPeers = 3 // Amount of peers desired to start syncing 33 34 // This is the target size for the packs of transactions sent by txsyncLoop64. 35 // A pack can get larger than this if a single transactions exceeds this size. 36 txsyncPackSize = 100 * 1024 37 ) 38 39 type txsync struct { 40 p *eth.Peer 41 txs []*types.Transaction 42 } 43 44 // syncTransactions starts sending all currently pending transactions to the given peer. 45 func (h *handler) syncTransactions(p *eth.Peer) { 46 // Assemble the set of transaction to broadcast or announce to the remote 47 // peer. Fun fact, this is quite an expensive operation as it needs to sort 48 // the transactions if the sorting is not cached yet. However, with a random 49 // order, insertions could overflow the non-executable queues and get dropped. 50 51 var txs types.Transactions 52 pending, _ := h.txpool.TxPoolPending(false, nil) 53 for _, batch := range pending { 54 txs = append(txs, batch...) 55 } 56 if len(txs) == 0 { 57 return 58 } 59 // The eth/65 protocol introduces proper transaction announcements, so instead 60 // of dripping transactions across multiple peers, just send the entire list as 61 // an announcement and let the remote side decide what they need (likely nothing). 62 if p.Version() >= eth.QUAI1 { 63 hashes := make([]common.Hash, len(txs)) 64 for i, tx := range txs { 65 hashes[i] = tx.Hash() 66 } 67 p.AsyncSendPooledTransactionHashes(hashes) 68 return 69 } 70 // Out of luck, peer is running legacy protocols, drop the txs over 71 select { 72 case h.txsyncCh <- &txsync{p: p, txs: txs}: 73 case <-h.quitSync: 74 } 75 } 76 77 // txsyncLoop64 takes care of the initial transaction sync for each new 78 // connection. When a new peer appears, we relay all currently pending 79 // transactions. In order to minimise egress bandwidth usage, we send 80 // the transactions in small packs to one peer at a time. 81 func (h *handler) txsyncLoop64() { 82 defer h.wg.Done() 83 84 var ( 85 pending = make(map[enode.ID]*txsync) 86 sending = false // whether a send is active 87 pack = new(txsync) // the pack that is being sent 88 done = make(chan error, 1) // result of the send 89 ) 90 91 // send starts a sending a pack of transactions from the sync. 92 send := func(s *txsync) { 93 if s.p.Version() >= eth.QUAI1 { 94 panic("initial transaction syncer running on eth/65+") 95 } 96 // Fill pack with transactions up to the target size. 97 size := common.StorageSize(0) 98 pack.p = s.p 99 pack.txs = pack.txs[:0] 100 for i := 0; i < len(s.txs) && size < txsyncPackSize; i++ { 101 pack.txs = append(pack.txs, s.txs[i]) 102 size += s.txs[i].Size() 103 } 104 // Remove the transactions that will be sent. 105 s.txs = s.txs[:copy(s.txs, s.txs[len(pack.txs):])] 106 if len(s.txs) == 0 { 107 delete(pending, s.p.Peer.ID()) 108 } 109 // Send the pack in the background. 110 s.p.Log().Trace("Sending batch of transactions", "count", len(pack.txs), "bytes", size) 111 sending = true 112 go func() { done <- pack.p.SendTransactions(pack.txs) }() 113 } 114 // pick chooses the next pending sync. 115 pick := func() *txsync { 116 if len(pending) == 0 { 117 return nil 118 } 119 n := rand.Intn(len(pending)) + 1 120 for _, s := range pending { 121 if n--; n == 0 { 122 return s 123 } 124 } 125 return nil 126 } 127 128 for { 129 select { 130 case s := <-h.txsyncCh: 131 pending[s.p.Peer.ID()] = s 132 if !sending { 133 send(s) 134 } 135 case err := <-done: 136 sending = false 137 // Stop tracking peers that cause send failures. 138 if err != nil { 139 pack.p.Log().Debug("Transaction send failed", "err", err) 140 delete(pending, pack.p.Peer.ID()) 141 } 142 // Schedule the next send. 143 if s := pick(); s != nil { 144 send(s) 145 } 146 case <-h.quitSync: 147 return 148 } 149 } 150 } 151 152 // chainSyncer coordinates blockchain sync components. 153 type chainSyncer struct { 154 handler *handler 155 peerEventCh chan struct{} 156 doneCh chan error // non-nil when sync is running 157 } 158 159 // chainSyncOp is a scheduled sync operation. 160 type chainSyncOp struct { 161 mode downloader.SyncMode 162 peer *eth.Peer 163 entropy *big.Int 164 head common.Hash 165 } 166 167 // newChainSyncer creates a chainSyncer. 168 func newChainSyncer(handler *handler) *chainSyncer { 169 return &chainSyncer{ 170 handler: handler, 171 peerEventCh: make(chan struct{}), 172 } 173 } 174 175 // handlePeerEvent notifies the syncer about a change in the peer set. 176 // This is called for new peers and every time a peer announces a new 177 // chain head. 178 func (cs *chainSyncer) handlePeerEvent(peer *eth.Peer) bool { 179 select { 180 case cs.peerEventCh <- struct{}{}: 181 return true 182 case <-cs.handler.quitSync: 183 return false 184 } 185 } 186 187 // loop runs in its own goroutine and launches the sync when necessary. 188 func (cs *chainSyncer) loop() { 189 nodeCtx := common.NodeLocation.Context() 190 defer cs.handler.wg.Done() 191 192 cs.handler.blockFetcher.Start() 193 if nodeCtx == common.ZONE_CTX && cs.handler.core.ProcessingState() { 194 cs.handler.txFetcher.Start() 195 defer cs.handler.txFetcher.Stop() 196 } 197 defer cs.handler.blockFetcher.Stop() 198 defer cs.handler.downloader.Terminate() 199 200 for { 201 if nodeCtx == common.PRIME_CTX { 202 if op := cs.nextSyncOp(); op != nil { 203 cs.startSync(op) 204 } 205 } 206 select { 207 case <-cs.peerEventCh: 208 // Peer information changed, recheck. 209 case <-cs.doneCh: 210 cs.doneCh = nil 211 212 case <-cs.handler.quitSync: 213 // Disable all insertion on the blockchain. This needs to happen before 214 // terminating the downloader because the downloader waits for blockchain 215 // inserts, and these can take a long time to finish. 216 cs.handler.downloader.Terminate() 217 if cs.doneCh != nil { 218 <-cs.doneCh 219 } 220 return 221 } 222 } 223 } 224 225 // nextSyncOp determines whether sync is required at this time. 226 func (cs *chainSyncer) nextSyncOp() *chainSyncOp { 227 if cs.doneCh != nil { 228 return nil // Sync already running. 229 } 230 231 // Ensure we're at minimum peer count. 232 minPeers := defaultMinSyncPeers 233 234 if minPeers > cs.handler.maxPeers { 235 minPeers = cs.handler.maxPeers 236 } 237 if cs.handler.peers.len() < minPeers { 238 return nil 239 } 240 241 peer := cs.handler.peers.peerWithHighestEntropy() 242 if peer == nil { 243 return nil 244 } 245 246 mode, ourEntropy := cs.modeAndLocalHead() 247 op := peerToSyncOp(mode, peer) 248 if op.entropy.Cmp(ourEntropy) <= 0 { 249 return nil // We're in sync. 250 } 251 return op 252 } 253 254 func peerToSyncOp(mode downloader.SyncMode, p *eth.Peer) *chainSyncOp { 255 peerHead, _, peerEntropy, _ := p.Head() 256 return &chainSyncOp{mode: mode, peer: p, entropy: peerEntropy, head: peerHead} 257 } 258 259 func (cs *chainSyncer) modeAndLocalHead() (downloader.SyncMode, *big.Int) { 260 return downloader.FullSync, cs.handler.downloader.HeadEntropy() 261 } 262 263 // startSync launches doSync in a new goroutine. 264 func (cs *chainSyncer) startSync(op *chainSyncOp) { 265 cs.doneCh = make(chan error, 3) 266 go func() { cs.doneCh <- cs.handler.doSync(op) }() 267 } 268 269 // doSync synchronizes the local blockchain with a remote peer. 270 func (h *handler) doSync(op *chainSyncOp) error { 271 // Stopping the downloader here temporarily for Region and Zones 272 nodeCtx := common.NodeLocation.Context() 273 if nodeCtx == common.PRIME_CTX { 274 // Run the sync cycle, and disable fast sync if we're past the pivot block 275 err := h.downloader.Synchronise(op.peer.ID(), op.head, op.entropy, op.mode) 276 log.Info("Downloader exited", "err", err) 277 if err != nil { 278 return err 279 } 280 // If we've successfully finished a sync cycle and passed any required checkpoint, 281 // enable accepting transactions from the network. 282 head := h.core.CurrentBlock() 283 if head == nil { 284 log.Warn("doSync: head is nil", "hash", h.core.CurrentHeader().Hash(), "number", h.core.CurrentHeader().NumberArray()) 285 return nil 286 } 287 if head.NumberU64() > 0 { 288 // We've completed a sync cycle, notify all peers of new state. This path is 289 // essential in star-topology networks where a gateway node needs to notify 290 // all its out-of-date peers of the availability of a new block. This failure 291 // scenario will most often crop up in private and hackathon networks with 292 // degenerate connectivity, but it should be healthy for the mainnet too to 293 // more reliably update peers or the local TD state. 294 h.BroadcastBlock(head, false) 295 } 296 } 297 return nil 298 }