github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/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 "sync/atomic" 23 "time" 24 25 "github.com/kisexp/xdchain/common" 26 "github.com/kisexp/xdchain/core/rawdb" 27 "github.com/kisexp/xdchain/core/types" 28 "github.com/kisexp/xdchain/eth/downloader" 29 "github.com/kisexp/xdchain/log" 30 "github.com/kisexp/xdchain/p2p/enode" 31 "github.com/kisexp/xdchain/permission/core" 32 ) 33 34 const ( 35 forceSyncCycle = 10 * time.Second // Time interval to force syncs, even if few peers are available 36 defaultMinSyncPeers = 5 // Amount of peers desired to start syncing 37 38 // This is the target size for the packs of transactions sent by txsyncLoop64. 39 // A pack can get larger than this if a single transactions exceeds this size. 40 txsyncPackSize = 100 * 1024 41 ) 42 43 type txsync struct { 44 p *peer 45 txs []*types.Transaction 46 } 47 48 // syncTransactions starts sending all currently pending transactions to the given peer. 49 func (pm *ProtocolManager) syncTransactions(p *peer) { 50 // Assemble the set of transaction to broadcast or announce to the remote 51 // peer. Fun fact, this is quite an expensive operation as it needs to sort 52 // the transactions if the sorting is not cached yet. However, with a random 53 // order, insertions could overflow the non-executable queues and get dropped. 54 // 55 // TODO(karalabe): Figure out if we could get away with random order somehow 56 var txs types.Transactions 57 pending, _ := pm.txpool.Pending() 58 for _, batch := range pending { 59 txs = append(txs, batch...) 60 } 61 if len(txs) == 0 { 62 return 63 } 64 // The eth/65 protocol introduces proper transaction announcements, so instead 65 // of dripping transactions across multiple peers, just send the entire list as 66 // an announcement and let the remote side decide what they need (likely nothing). 67 if p.version >= eth65 { 68 hashes := make([]common.Hash, len(txs)) 69 for i, tx := range txs { 70 hashes[i] = tx.Hash() 71 } 72 p.AsyncSendPooledTransactionHashes(hashes) 73 return 74 } 75 // Out of luck, peer is running legacy protocols, drop the txs over 76 select { 77 case pm.txsyncCh <- &txsync{p: p, txs: txs}: 78 case <-pm.quitSync: 79 } 80 } 81 82 // txsyncLoop64 takes care of the initial transaction sync for each new 83 // connection. When a new peer appears, we relay all currently pending 84 // transactions. In order to minimise egress bandwidth usage, we send 85 // the transactions in small packs to one peer at a time. 86 func (pm *ProtocolManager) txsyncLoop64() { 87 defer pm.wg.Done() 88 89 var ( 90 pending = make(map[enode.ID]*txsync) 91 sending = false // whether a send is active 92 pack = new(txsync) // the pack that is being sent 93 done = make(chan error, 1) // result of the send 94 ) 95 96 // send starts a sending a pack of transactions from the sync. 97 send := func(s *txsync) { 98 if s.p.version >= eth65 { 99 panic("initial transaction syncer running on eth/65+") 100 } 101 // Fill pack with transactions up to the target size. 102 size := common.StorageSize(0) 103 pack.p = s.p 104 pack.txs = pack.txs[:0] 105 for i := 0; i < len(s.txs) && size < txsyncPackSize; i++ { 106 pack.txs = append(pack.txs, s.txs[i]) 107 size += s.txs[i].Size() 108 } 109 // Remove the transactions that will be sent. 110 s.txs = s.txs[:copy(s.txs, s.txs[len(pack.txs):])] 111 if len(s.txs) == 0 { 112 delete(pending, s.p.ID()) 113 } 114 // Send the pack in the background. 115 s.p.Log().Trace("Sending batch of transactions", "count", len(pack.txs), "bytes", size) 116 sending = true 117 go func() { done <- pack.p.SendTransactions64(pack.txs) }() 118 } 119 120 // pick chooses the next pending sync. 121 pick := func() *txsync { 122 if len(pending) == 0 { 123 return nil 124 } 125 n := rand.Intn(len(pending)) + 1 126 for _, s := range pending { 127 if n--; n == 0 { 128 return s 129 } 130 } 131 return nil 132 } 133 134 for { 135 select { 136 case s := <-pm.txsyncCh: 137 pending[s.p.ID()] = s 138 if !sending { 139 send(s) 140 } 141 case err := <-done: 142 sending = false 143 // Stop tracking peers that cause send failures. 144 if err != nil { 145 pack.p.Log().Debug("Transaction send failed", "err", err) 146 delete(pending, pack.p.ID()) 147 } 148 // Schedule the next send. 149 if s := pick(); s != nil { 150 send(s) 151 } 152 case <-pm.quitSync: 153 return 154 } 155 } 156 } 157 158 // chainSyncer coordinates blockchain sync components. 159 type chainSyncer struct { 160 pm *ProtocolManager 161 force *time.Timer 162 forced bool // true when force timer fired 163 peerEventCh chan struct{} 164 doneCh chan error // non-nil when sync is running 165 } 166 167 // chainSyncOp is a scheduled sync operation. 168 type chainSyncOp struct { 169 mode downloader.SyncMode 170 peer *peer 171 td *big.Int 172 head common.Hash 173 } 174 175 // newChainSyncer creates a chainSyncer. 176 func newChainSyncer(pm *ProtocolManager) *chainSyncer { 177 return &chainSyncer{ 178 pm: pm, 179 peerEventCh: make(chan struct{}), 180 } 181 } 182 183 // handlePeerEvent notifies the syncer about a change in the peer set. 184 // This is called for new peers and every time a peer announces a new 185 // chain head. 186 func (cs *chainSyncer) handlePeerEvent(p *peer) bool { 187 select { 188 case cs.peerEventCh <- struct{}{}: 189 return true 190 case <-cs.pm.quitSync: 191 return false 192 } 193 } 194 195 // loop runs in its own goroutine and launches the sync when necessary. 196 func (cs *chainSyncer) loop() { 197 defer cs.pm.wg.Done() 198 199 cs.pm.blockFetcher.Start() 200 cs.pm.txFetcher.Start() 201 defer cs.pm.blockFetcher.Stop() 202 defer cs.pm.txFetcher.Stop() 203 204 // The force timer lowers the peer count threshold down to one when it fires. 205 // This ensures we'll always start sync even if there aren't enough peers. 206 cs.force = time.NewTimer(forceSyncCycle) 207 defer cs.force.Stop() 208 209 for { 210 if op := cs.nextSyncOp(); op != nil { 211 if !cs.pm.raftMode { 212 cs.startSync(op) 213 } 214 } 215 216 select { 217 case <-cs.peerEventCh: 218 // Peer information changed, recheck. 219 case <-cs.doneCh: 220 cs.doneCh = nil 221 cs.force.Reset(forceSyncCycle) 222 cs.forced = false 223 case <-cs.force.C: 224 cs.forced = true 225 226 case <-cs.pm.quitSync: 227 // Disable all insertion on the blockchain. This needs to happen before 228 // terminating the downloader because the downloader waits for blockchain 229 // inserts, and these can take a long time to finish. 230 cs.pm.blockchain.StopInsert() 231 cs.pm.downloader.Terminate() 232 if cs.doneCh != nil { 233 // Wait for the current sync to end. 234 <-cs.doneCh 235 } 236 return 237 } 238 } 239 } 240 241 // nextSyncOp determines whether sync is required at this time. 242 func (cs *chainSyncer) nextSyncOp() *chainSyncOp { 243 if cs.doneCh != nil { 244 return nil // Sync already running. 245 } 246 247 // Ensure we're at minimum peer count. 248 minPeers := defaultMinSyncPeers 249 if cs.forced { 250 minPeers = 1 251 } else if minPeers > cs.pm.maxPeers { 252 minPeers = cs.pm.maxPeers 253 } 254 if cs.pm.peers.Len() < minPeers { 255 return nil 256 } 257 258 // We have enough peers, check TD. 259 peer := cs.pm.peers.BestPeer() 260 if peer == nil { 261 return nil 262 } 263 mode, ourTD := cs.modeAndLocalHead() 264 op := peerToSyncOp(mode, peer) 265 if op.td.Cmp(ourTD) <= 0 { 266 // Quorum 267 // added for permissions changes to indicate node sync up has started 268 // if peer's TD is smaller than ours, no sync will happen 269 core.SetSyncStatus() 270 return nil // We're in sync. 271 } 272 if mode == downloader.FastSync { 273 // Make sure the peer's total difficulty we are synchronizing is higher. 274 if cs.pm.blockchain.GetTdByHash(cs.pm.blockchain.CurrentFastBlock().Hash()).Cmp(ourTD) >= 0 { 275 // Quorum never use FastSync, no need to execute SetSyncStatus 276 return nil 277 } 278 } 279 return op 280 } 281 282 func peerToSyncOp(mode downloader.SyncMode, p *peer) *chainSyncOp { 283 peerHead, peerTD := p.Head() 284 return &chainSyncOp{mode: mode, peer: p, td: peerTD, head: peerHead} 285 } 286 287 func (cs *chainSyncer) modeAndLocalHead() (downloader.SyncMode, *big.Int) { 288 // If we're in fast sync mode, return that directly 289 if atomic.LoadUint32(&cs.pm.fastSync) == 1 { 290 block := cs.pm.blockchain.CurrentFastBlock() 291 td := cs.pm.blockchain.GetTdByHash(block.Hash()) 292 return downloader.FastSync, td 293 } 294 // We are probably in full sync, but we might have rewound to before the 295 // fast sync pivot, check if we should reenable 296 if pivot := rawdb.ReadLastPivotNumber(cs.pm.chaindb); pivot != nil { 297 if head := cs.pm.blockchain.CurrentBlock(); head.NumberU64() < *pivot { 298 block := cs.pm.blockchain.CurrentFastBlock() 299 td := cs.pm.blockchain.GetTdByHash(block.Hash()) 300 return downloader.FastSync, td 301 } 302 } 303 // Nope, we're really full syncing 304 head := cs.pm.blockchain.CurrentHeader() 305 td := cs.pm.blockchain.GetTd(head.Hash(), head.Number.Uint64()) 306 return downloader.FullSync, td 307 } 308 309 // startSync launches doSync in a new goroutine. 310 func (cs *chainSyncer) startSync(op *chainSyncOp) { 311 cs.doneCh = make(chan error, 1) 312 go func() { cs.doneCh <- cs.pm.doSync(op) }() 313 } 314 315 // doSync synchronizes the local blockchain with a remote peer. 316 func (pm *ProtocolManager) doSync(op *chainSyncOp) error { 317 if op.mode == downloader.FastSync { 318 // Before launch the fast sync, we have to ensure user uses the same 319 // txlookup limit. 320 // The main concern here is: during the fast sync Geth won't index the 321 // block(generate tx indices) before the HEAD-limit. But if user changes 322 // the limit in the next fast sync(e.g. user kill Geth manually and 323 // restart) then it will be hard for Geth to figure out the oldest block 324 // has been indexed. So here for the user-experience wise, it's non-optimal 325 // that user can't change limit during the fast sync. If changed, Geth 326 // will just blindly use the original one. 327 limit := pm.blockchain.TxLookupLimit() 328 if stored := rawdb.ReadFastTxLookupLimit(pm.chaindb); stored == nil { 329 rawdb.WriteFastTxLookupLimit(pm.chaindb, limit) 330 } else if *stored != limit { 331 pm.blockchain.SetTxLookupLimit(*stored) 332 log.Warn("Update txLookup limit", "provided", limit, "updated", *stored) 333 } 334 } 335 // Run the sync cycle, and disable fast sync if we're past the pivot block 336 err := pm.downloader.Synchronise(op.peer.id, op.head, op.td, op.mode) 337 if err != nil { 338 return err 339 } 340 if atomic.LoadUint32(&pm.fastSync) == 1 { 341 log.Info("Fast sync complete, auto disabling") 342 atomic.StoreUint32(&pm.fastSync, 0) 343 } 344 345 // If we've successfully finished a sync cycle and passed any required checkpoint, 346 // enable accepting transactions from the network. 347 head := pm.blockchain.CurrentBlock() 348 if head.NumberU64() >= pm.checkpointNumber { 349 // Checkpoint passed, sanity check the timestamp to have a fallback mechanism 350 // for non-checkpointed (number = 0) private networks. 351 if head.Time() >= uint64(time.Now().AddDate(0, -1, 0).Unix()) { 352 atomic.StoreUint32(&pm.acceptTxs, 1) 353 } 354 } 355 356 if head.NumberU64() > 0 { 357 // We've completed a sync cycle, notify all peers of new state. This path is 358 // essential in star-topology networks where a gateway node needs to notify 359 // all its out-of-date peers of the availability of a new block. This failure 360 // scenario will most often crop up in private and hackathon networks with 361 // degenerate connectivity, but it should be healthy for the mainnet too to 362 // more reliably update peers or the local TD state. 363 pm.BroadcastBlock(head, false) 364 } 365 366 return nil 367 }