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  }