github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/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/kisexp/xdchain/common" 25 "github.com/kisexp/xdchain/core/rawdb" 26 "github.com/kisexp/xdchain/eth/downloader" 27 "github.com/kisexp/xdchain/light" 28 "github.com/kisexp/xdchain/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 // validateCheckpoint verifies the advertised checkpoint by peer is valid or not. 47 // 48 // Each network has several hard-coded checkpoint signer addresses. Only the 49 // checkpoint issued by the specified signer is considered valid. 50 // 51 // In addition to the checkpoint registered in the registrar contract, there are 52 // several legacy hardcoded checkpoints in our codebase. These checkpoints are 53 // also considered as valid. 54 func (h *clientHandler) validateCheckpoint(peer *serverPeer) error { 55 ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) 56 defer cancel() 57 58 // Fetch the block header corresponding to the checkpoint registration. 59 wrapPeer := &peerConnection{handler: h, peer: peer} 60 header, err := wrapPeer.RetrieveSingleHeaderByNumber(ctx, peer.checkpointNumber) 61 if err != nil { 62 return err 63 } 64 // Fetch block logs associated with the block header. 65 logs, err := light.GetUntrustedBlockLogs(ctx, h.backend.odr, header) 66 if err != nil { 67 return err 68 } 69 events := h.backend.oracle.Contract().LookupCheckpointEvents(logs, peer.checkpoint.SectionIndex, peer.checkpoint.Hash()) 70 if len(events) == 0 { 71 return errInvalidCheckpoint 72 } 73 var ( 74 index = events[0].Index 75 hash = events[0].CheckpointHash 76 signatures [][]byte 77 ) 78 for _, event := range events { 79 signatures = append(signatures, append(event.R[:], append(event.S[:], event.V)...)) 80 } 81 valid, signers := h.backend.oracle.VerifySigners(index, hash, signatures) 82 if !valid { 83 return errInvalidCheckpoint 84 } 85 log.Warn("Verified advertised checkpoint", "peer", peer.id, "signers", len(signers)) 86 return nil 87 } 88 89 // synchronise tries to sync up our local chain with a remote peer. 90 func (h *clientHandler) synchronise(peer *serverPeer) { 91 // Short circuit if the peer is nil. 92 if peer == nil { 93 return 94 } 95 // Make sure the peer's TD is higher than our own. 96 latest := h.backend.blockchain.CurrentHeader() 97 currentTd := rawdb.ReadTd(h.backend.chainDb, latest.Hash(), latest.Number.Uint64()) 98 if currentTd != nil && peer.Td().Cmp(currentTd) < 0 { 99 return 100 } 101 // Recap the checkpoint. 102 // 103 // The light client may be connected to several different versions of the server. 104 // (1) Old version server which can not provide stable checkpoint in the handshake packet. 105 // => Use hardcoded checkpoint or empty checkpoint 106 // (2) New version server but simple checkpoint syncing is not enabled(e.g. mainnet, new testnet or private network) 107 // => Use hardcoded checkpoint or empty checkpoint 108 // (3) New version server but the provided stable checkpoint is even lower than the hardcoded one. 109 // => Use hardcoded checkpoint 110 // (4) New version server with valid and higher stable checkpoint 111 // => Use provided checkpoint 112 var checkpoint = &peer.checkpoint 113 var hardcoded bool 114 if h.checkpoint != nil && h.checkpoint.SectionIndex >= peer.checkpoint.SectionIndex { 115 checkpoint = h.checkpoint // Use the hardcoded one. 116 hardcoded = true 117 } 118 // Determine whether we should run checkpoint syncing or normal light syncing. 119 // 120 // Here has four situations that we will disable the checkpoint syncing: 121 // 122 // 1. The checkpoint is empty 123 // 2. The latest head block of the local chain is above the checkpoint. 124 // 3. The checkpoint is hardcoded(recap with local hardcoded checkpoint) 125 // 4. For some networks the checkpoint syncing is not activated. 126 mode := checkpointSync 127 switch { 128 case checkpoint.Empty(): 129 mode = lightSync 130 log.Debug("Disable checkpoint syncing", "reason", "empty checkpoint") 131 case latest.Number.Uint64() >= (checkpoint.SectionIndex+1)*h.backend.iConfig.ChtSize-1: 132 mode = lightSync 133 log.Debug("Disable checkpoint syncing", "reason", "local chain beyond the checkpoint") 134 case hardcoded: 135 mode = legacyCheckpointSync 136 log.Debug("Disable checkpoint syncing", "reason", "checkpoint is hardcoded") 137 case h.backend.oracle == nil || !h.backend.oracle.IsRunning(): 138 if h.checkpoint == nil { 139 mode = lightSync // Downgrade to light sync unfortunately. 140 } else { 141 checkpoint = h.checkpoint 142 mode = legacyCheckpointSync 143 } 144 log.Debug("Disable checkpoint syncing", "reason", "checkpoint syncing is not activated") 145 } 146 // Notify testing framework if syncing has completed(for testing purpose). 147 defer func() { 148 if h.syncDone != nil { 149 h.syncDone() 150 } 151 }() 152 start := time.Now() 153 if mode == checkpointSync || mode == legacyCheckpointSync { 154 // Validate the advertised checkpoint 155 if mode == checkpointSync { 156 if err := h.validateCheckpoint(peer); err != nil { 157 log.Debug("Failed to validate checkpoint", "reason", err) 158 h.removePeer(peer.id) 159 return 160 } 161 h.backend.blockchain.AddTrustedCheckpoint(checkpoint) 162 } 163 log.Debug("Checkpoint syncing start", "peer", peer.id, "checkpoint", checkpoint.SectionIndex) 164 165 // Fetch the start point block header. 166 // 167 // For the ethash consensus engine, the start header is the block header 168 // of the checkpoint. 169 // 170 // For the clique consensus engine, the start header is the block header 171 // of the latest epoch covered by checkpoint. 172 ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) 173 defer cancel() 174 if !checkpoint.Empty() && !h.backend.blockchain.SyncCheckpoint(ctx, checkpoint) { 175 log.Debug("Sync checkpoint failed") 176 h.removePeer(peer.id) 177 return 178 } 179 } 180 // Fetch the remaining block headers based on the current chain header. 181 if err := h.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), downloader.LightSync); err != nil { 182 log.Debug("Synchronise failed", "reason", err) 183 return 184 } 185 log.Debug("Synchronise finished", "elapsed", common.PrettyDuration(time.Since(start))) 186 }