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