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 = &params.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  }