github.com/bloxroute-labs/bor@v0.1.4/les/sync.go (about) 1 // Copyright 2016 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 les 18 19 import ( 20 "context" 21 "errors" 22 "time" 23 24 "github.com/maticnetwork/bor/common" 25 "github.com/maticnetwork/bor/core/rawdb" 26 "github.com/maticnetwork/bor/eth/downloader" 27 "github.com/maticnetwork/bor/light" 28 "github.com/maticnetwork/bor/log" 29 ) 30 31 var errInvalidCheckpoint = errors.New("invalid advertised checkpoint") 32 33 const ( 34 // lightSync starts syncing from the current highest block. 35 // If the chain is empty, syncing the entire header chain. 36 lightSync = iota 37 38 // legacyCheckpointSync starts syncing from a hardcoded checkpoint. 39 legacyCheckpointSync 40 41 // checkpointSync starts syncing from a checkpoint signed by trusted 42 // signer or hardcoded checkpoint for compatibility. 43 checkpointSync 44 ) 45 46 // syncer is responsible for periodically synchronising with the network, both 47 // downloading hashes and blocks as well as handling the announcement handler. 48 func (pm *ProtocolManager) syncer() { 49 // Start and ensure cleanup of sync mechanisms 50 //pm.fetcher.Start() 51 //defer pm.fetcher.Stop() 52 defer pm.downloader.Terminate() 53 54 // Wait for different events to fire synchronisation operations 55 //forceSync := time.Tick(forceSyncCycle) 56 for { 57 select { 58 case <-pm.newPeerCh: 59 /* // Make sure we have peers to select from, then sync 60 if pm.peers.Len() < minDesiredPeerCount { 61 break 62 } 63 go pm.synchronise(pm.peers.BestPeer()) 64 */ 65 /*case <-forceSync: 66 // Force a sync even if not enough peers are present 67 go pm.synchronise(pm.peers.BestPeer()) 68 */ 69 case <-pm.noMorePeers: 70 return 71 } 72 } 73 } 74 75 // validateCheckpoint verifies the advertised checkpoint by peer is valid or not. 76 // 77 // Each network has several hard-coded checkpoint signer addresses. Only the 78 // checkpoint issued by the specified signer is considered valid. 79 // 80 // In addition to the checkpoint registered in the registrar contract, there are 81 // several legacy hardcoded checkpoints in our codebase. These checkpoints are 82 // also considered as valid. 83 func (pm *ProtocolManager) validateCheckpoint(peer *peer) error { 84 ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) 85 defer cancel() 86 87 // Fetch the block header corresponding to the checkpoint registration. 88 cp := peer.checkpoint 89 header, err := light.GetUntrustedHeaderByNumber(ctx, pm.odr, peer.checkpointNumber, peer.id) 90 if err != nil { 91 return err 92 } 93 // Fetch block logs associated with the block header. 94 logs, err := light.GetUntrustedBlockLogs(ctx, pm.odr, header) 95 if err != nil { 96 return err 97 } 98 events := pm.reg.contract.LookupCheckpointEvents(logs, cp.SectionIndex, cp.Hash()) 99 if len(events) == 0 { 100 return errInvalidCheckpoint 101 } 102 var ( 103 index = events[0].Index 104 hash = events[0].CheckpointHash 105 signatures [][]byte 106 ) 107 for _, event := range events { 108 signatures = append(signatures, append(event.R[:], append(event.S[:], event.V)...)) 109 } 110 valid, signers := pm.reg.verifySigners(index, hash, signatures) 111 if !valid { 112 return errInvalidCheckpoint 113 } 114 log.Warn("Verified advertised checkpoint", "peer", peer.id, "signers", len(signers)) 115 return nil 116 } 117 118 // synchronise tries to sync up our local chain with a remote peer. 119 func (pm *ProtocolManager) synchronise(peer *peer) { 120 // Short circuit if the peer is nil. 121 if peer == nil { 122 return 123 } 124 // Make sure the peer's TD is higher than our own. 125 latest := pm.blockchain.CurrentHeader() 126 currentTd := rawdb.ReadTd(pm.chainDb, latest.Hash(), latest.Number.Uint64()) 127 if currentTd != nil && peer.headBlockInfo().Td.Cmp(currentTd) < 0 { 128 return 129 } 130 // Recap the checkpoint. 131 // 132 // The light client may be connected to several different versions of the server. 133 // (1) Old version server which can not provide stable checkpoint in the handshake packet. 134 // => Use hardcoded checkpoint or empty checkpoint 135 // (2) New version server but simple checkpoint syncing is not enabled(e.g. mainnet, new testnet or private network) 136 // => Use hardcoded checkpoint or empty checkpoint 137 // (3) New version server but the provided stable checkpoint is even lower than the hardcoded one. 138 // => Use hardcoded checkpoint 139 // (4) New version server with valid and higher stable checkpoint 140 // => Use provided checkpoint 141 var checkpoint = &peer.checkpoint 142 var hardcoded bool 143 if pm.checkpoint != nil && pm.checkpoint.SectionIndex >= peer.checkpoint.SectionIndex { 144 checkpoint = pm.checkpoint // Use the hardcoded one. 145 hardcoded = true 146 } 147 // Determine whether we should run checkpoint syncing or normal light syncing. 148 // 149 // Here has four situations that we will disable the checkpoint syncing: 150 // 151 // 1. The checkpoint is empty 152 // 2. The latest head block of the local chain is above the checkpoint. 153 // 3. The checkpoint is hardcoded(recap with local hardcoded checkpoint) 154 // 4. For some networks the checkpoint syncing is not activated. 155 mode := checkpointSync 156 switch { 157 case checkpoint.Empty(): 158 mode = lightSync 159 log.Debug("Disable checkpoint syncing", "reason", "empty checkpoint") 160 case latest.Number.Uint64() >= (checkpoint.SectionIndex+1)*pm.iConfig.ChtSize-1: 161 mode = lightSync 162 log.Debug("Disable checkpoint syncing", "reason", "local chain beyond the checkpoint") 163 case hardcoded: 164 mode = legacyCheckpointSync 165 log.Debug("Disable checkpoint syncing", "reason", "checkpoint is hardcoded") 166 case pm.reg == nil || !pm.reg.isRunning(): 167 mode = legacyCheckpointSync 168 log.Debug("Disable checkpoint syncing", "reason", "checkpoint syncing is not activated") 169 } 170 // Notify testing framework if syncing has completed(for testing purpose). 171 defer func() { 172 if pm.reg != nil && pm.reg.syncDoneHook != nil { 173 pm.reg.syncDoneHook() 174 } 175 }() 176 start := time.Now() 177 if mode == checkpointSync || mode == legacyCheckpointSync { 178 // Validate the advertised checkpoint 179 if mode == legacyCheckpointSync { 180 checkpoint = pm.checkpoint 181 } else if mode == checkpointSync { 182 if err := pm.validateCheckpoint(peer); err != nil { 183 log.Debug("Failed to validate checkpoint", "reason", err) 184 pm.removePeer(peer.id) 185 return 186 } 187 pm.blockchain.(*light.LightChain).AddTrustedCheckpoint(checkpoint) 188 } 189 log.Debug("Checkpoint syncing start", "peer", peer.id, "checkpoint", checkpoint.SectionIndex) 190 191 // Fetch the start point block header. 192 // 193 // For the ethash consensus engine, the start header is the block header 194 // of the checkpoint. 195 // 196 // For the clique consensus engine, the start header is the block header 197 // of the latest epoch covered by checkpoint. 198 ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) 199 defer cancel() 200 if !checkpoint.Empty() && !pm.blockchain.(*light.LightChain).SyncCheckpoint(ctx, checkpoint) { 201 log.Debug("Sync checkpoint failed") 202 pm.removePeer(peer.id) 203 return 204 } 205 } 206 // Fetch the remaining block headers based on the current chain header. 207 if err := pm.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), downloader.LightSync); err != nil { 208 log.Debug("Synchronise failed", "reason", err) 209 return 210 } 211 log.Debug("Synchronise finished", "elapsed", common.PrettyDuration(time.Since(start))) 212 }