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