github.com/kaleido-io/go-ethereum@v1.9.7/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/ethereum/go-ethereum/common"
    25  	"github.com/ethereum/go-ethereum/core/rawdb"
    26  	"github.com/ethereum/go-ethereum/eth/downloader"
    27  	"github.com/ethereum/go-ethereum/light"
    28  	"github.com/ethereum/go-ethereum/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 *peer) 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  	cp := peer.checkpoint
    60  	header, err := light.GetUntrustedHeaderByNumber(ctx, h.backend.odr, peer.checkpointNumber, peer.id)
    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, cp.SectionIndex, cp.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 *peer) {
    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.headBlockInfo().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  }