github.com/bloxroute-labs/bor@v0.1.4/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/maticnetwork/bor/common"
    25  	"github.com/maticnetwork/bor/core/rawdb"
    26  	"github.com/maticnetwork/bor/eth/downloader"
    27  	"github.com/maticnetwork/bor/light"
    28  	"github.com/maticnetwork/bor/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  // syncer is responsible for periodically synchronising with the network, both
    47  // downloading hashes and blocks as well as handling the announcement handler.
    48  func (pm *ProtocolManager) syncer() {
    49  	// Start and ensure cleanup of sync mechanisms
    50  	//pm.fetcher.Start()
    51  	//defer pm.fetcher.Stop()
    52  	defer pm.downloader.Terminate()
    53  
    54  	// Wait for different events to fire synchronisation operations
    55  	//forceSync := time.Tick(forceSyncCycle)
    56  	for {
    57  		select {
    58  		case <-pm.newPeerCh:
    59  			/*			// Make sure we have peers to select from, then sync
    60  						if pm.peers.Len() < minDesiredPeerCount {
    61  							break
    62  						}
    63  						go pm.synchronise(pm.peers.BestPeer())
    64  			*/
    65  		/*case <-forceSync:
    66  		// Force a sync even if not enough peers are present
    67  		go pm.synchronise(pm.peers.BestPeer())
    68  		*/
    69  		case <-pm.noMorePeers:
    70  			return
    71  		}
    72  	}
    73  }
    74  
    75  // validateCheckpoint verifies the advertised checkpoint by peer is valid or not.
    76  //
    77  // Each network has several hard-coded checkpoint signer addresses. Only the
    78  // checkpoint issued by the specified signer is considered valid.
    79  //
    80  // In addition to the checkpoint registered in the registrar contract, there are
    81  // several legacy hardcoded checkpoints in our codebase. These checkpoints are
    82  // also considered as valid.
    83  func (pm *ProtocolManager) validateCheckpoint(peer *peer) error {
    84  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
    85  	defer cancel()
    86  
    87  	// Fetch the block header corresponding to the checkpoint registration.
    88  	cp := peer.checkpoint
    89  	header, err := light.GetUntrustedHeaderByNumber(ctx, pm.odr, peer.checkpointNumber, peer.id)
    90  	if err != nil {
    91  		return err
    92  	}
    93  	// Fetch block logs associated with the block header.
    94  	logs, err := light.GetUntrustedBlockLogs(ctx, pm.odr, header)
    95  	if err != nil {
    96  		return err
    97  	}
    98  	events := pm.reg.contract.LookupCheckpointEvents(logs, cp.SectionIndex, cp.Hash())
    99  	if len(events) == 0 {
   100  		return errInvalidCheckpoint
   101  	}
   102  	var (
   103  		index      = events[0].Index
   104  		hash       = events[0].CheckpointHash
   105  		signatures [][]byte
   106  	)
   107  	for _, event := range events {
   108  		signatures = append(signatures, append(event.R[:], append(event.S[:], event.V)...))
   109  	}
   110  	valid, signers := pm.reg.verifySigners(index, hash, signatures)
   111  	if !valid {
   112  		return errInvalidCheckpoint
   113  	}
   114  	log.Warn("Verified advertised checkpoint", "peer", peer.id, "signers", len(signers))
   115  	return nil
   116  }
   117  
   118  // synchronise tries to sync up our local chain with a remote peer.
   119  func (pm *ProtocolManager) synchronise(peer *peer) {
   120  	// Short circuit if the peer is nil.
   121  	if peer == nil {
   122  		return
   123  	}
   124  	// Make sure the peer's TD is higher than our own.
   125  	latest := pm.blockchain.CurrentHeader()
   126  	currentTd := rawdb.ReadTd(pm.chainDb, latest.Hash(), latest.Number.Uint64())
   127  	if currentTd != nil && peer.headBlockInfo().Td.Cmp(currentTd) < 0 {
   128  		return
   129  	}
   130  	// Recap the checkpoint.
   131  	//
   132  	// The light client may be connected to several different versions of the server.
   133  	// (1) Old version server which can not provide stable checkpoint in the handshake packet.
   134  	//     => Use hardcoded checkpoint or empty checkpoint
   135  	// (2) New version server but simple checkpoint syncing is not enabled(e.g. mainnet, new testnet or private network)
   136  	//     => Use hardcoded checkpoint or empty checkpoint
   137  	// (3) New version server but the provided stable checkpoint is even lower than the hardcoded one.
   138  	//     => Use hardcoded checkpoint
   139  	// (4) New version server with valid and higher stable checkpoint
   140  	//     => Use provided checkpoint
   141  	var checkpoint = &peer.checkpoint
   142  	var hardcoded bool
   143  	if pm.checkpoint != nil && pm.checkpoint.SectionIndex >= peer.checkpoint.SectionIndex {
   144  		checkpoint = pm.checkpoint // Use the hardcoded one.
   145  		hardcoded = true
   146  	}
   147  	// Determine whether we should run checkpoint syncing or normal light syncing.
   148  	//
   149  	// Here has four situations that we will disable the checkpoint syncing:
   150  	//
   151  	// 1. The checkpoint is empty
   152  	// 2. The latest head block of the local chain is above the checkpoint.
   153  	// 3. The checkpoint is hardcoded(recap with local hardcoded checkpoint)
   154  	// 4. For some networks the checkpoint syncing is not activated.
   155  	mode := checkpointSync
   156  	switch {
   157  	case checkpoint.Empty():
   158  		mode = lightSync
   159  		log.Debug("Disable checkpoint syncing", "reason", "empty checkpoint")
   160  	case latest.Number.Uint64() >= (checkpoint.SectionIndex+1)*pm.iConfig.ChtSize-1:
   161  		mode = lightSync
   162  		log.Debug("Disable checkpoint syncing", "reason", "local chain beyond the checkpoint")
   163  	case hardcoded:
   164  		mode = legacyCheckpointSync
   165  		log.Debug("Disable checkpoint syncing", "reason", "checkpoint is hardcoded")
   166  	case pm.reg == nil || !pm.reg.isRunning():
   167  		mode = legacyCheckpointSync
   168  		log.Debug("Disable checkpoint syncing", "reason", "checkpoint syncing is not activated")
   169  	}
   170  	// Notify testing framework if syncing has completed(for testing purpose).
   171  	defer func() {
   172  		if pm.reg != nil && pm.reg.syncDoneHook != nil {
   173  			pm.reg.syncDoneHook()
   174  		}
   175  	}()
   176  	start := time.Now()
   177  	if mode == checkpointSync || mode == legacyCheckpointSync {
   178  		// Validate the advertised checkpoint
   179  		if mode == legacyCheckpointSync {
   180  			checkpoint = pm.checkpoint
   181  		} else if mode == checkpointSync {
   182  			if err := pm.validateCheckpoint(peer); err != nil {
   183  				log.Debug("Failed to validate checkpoint", "reason", err)
   184  				pm.removePeer(peer.id)
   185  				return
   186  			}
   187  			pm.blockchain.(*light.LightChain).AddTrustedCheckpoint(checkpoint)
   188  		}
   189  		log.Debug("Checkpoint syncing start", "peer", peer.id, "checkpoint", checkpoint.SectionIndex)
   190  
   191  		// Fetch the start point block header.
   192  		//
   193  		// For the ethash consensus engine, the start header is the block header
   194  		// of the checkpoint.
   195  		//
   196  		// For the clique consensus engine, the start header is the block header
   197  		// of the latest epoch covered by checkpoint.
   198  		ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
   199  		defer cancel()
   200  		if !checkpoint.Empty() && !pm.blockchain.(*light.LightChain).SyncCheckpoint(ctx, checkpoint) {
   201  			log.Debug("Sync checkpoint failed")
   202  			pm.removePeer(peer.id)
   203  			return
   204  		}
   205  	}
   206  	// Fetch the remaining block headers based on the current chain header.
   207  	if err := pm.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), downloader.LightSync); err != nil {
   208  		log.Debug("Synchronise failed", "reason", err)
   209  		return
   210  	}
   211  	log.Debug("Synchronise finished", "elapsed", common.PrettyDuration(time.Since(start)))
   212  }