github.com/amazechain/amc@v0.1.3/internal/sync/initial-sync/service.go (about)

     1  // Package initialsync includes all initial block download and processing
     2  // logic for the node, using a round robin strategy and a finite-state-machine
     3  // to handle edge-cases in a beacon node's sync status.
     4  package initialsync
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"github.com/amazechain/amc/common"
    10  	"github.com/amazechain/amc/internal/p2p"
    11  	event "github.com/amazechain/amc/modules/event/v2"
    12  	"github.com/holiman/uint256"
    13  	"github.com/libp2p/go-libp2p/core/peer"
    14  	"github.com/paulbellamy/ratecounter"
    15  	"sync/atomic"
    16  	"time"
    17  
    18  	"github.com/pkg/errors"
    19  )
    20  
    21  // Config to set up the initial sync service.
    22  type Config struct {
    23  	P2P   p2p.P2P
    24  	Chain common.IBlockChain
    25  }
    26  
    27  // Service service.
    28  type Service struct {
    29  	cfg                    *Config
    30  	ctx                    context.Context
    31  	cancel                 context.CancelFunc
    32  	synced                 atomic.Bool
    33  	syncing                atomic.Bool
    34  	counter                *ratecounter.RateCounter
    35  	highestExpectedBlockNr *uint256.Int
    36  }
    37  
    38  // NewService configures the initial sync service responsible for bringing the node up to the
    39  // latest head of the blockchain.
    40  func NewService(ctx context.Context, cfg *Config) *Service {
    41  	ctx, cancel := context.WithCancel(ctx)
    42  	s := &Service{
    43  		cfg:     cfg,
    44  		ctx:     ctx,
    45  		cancel:  cancel,
    46  		counter: ratecounter.NewRateCounter(counterSeconds * time.Second),
    47  	}
    48  
    49  	return s
    50  }
    51  
    52  // Start the initial sync service.
    53  func (s *Service) Start() {
    54  
    55  	event.GlobalEvent.Send(common.DownloaderStartEvent{})
    56  	defer event.GlobalEvent.Send(common.DownloaderFinishEvent{})
    57  
    58  	log.Info("Starting initial chain sync...")
    59  	highestExpectedBlockNr := s.waitForMinimumPeers()
    60  	if err := s.roundRobinSync(highestExpectedBlockNr); err != nil {
    61  		if errors.Is(s.ctx.Err(), context.Canceled) {
    62  			return
    63  		}
    64  		panic(err)
    65  	}
    66  	log.Info(fmt.Sprintf("Synced up to blockNr: %d", s.cfg.Chain.CurrentBlock().Number64().Uint64()))
    67  	s.markSynced()
    68  }
    69  
    70  // Stop initial sync.
    71  func (s *Service) Stop() error {
    72  	s.cancel()
    73  	log.Info("InitialSync stopped")
    74  	return nil
    75  }
    76  
    77  // Status of initial sync.
    78  func (s *Service) Status() error {
    79  	if s.syncing.Load() == true {
    80  		return errors.New("syncing")
    81  	}
    82  	return nil
    83  }
    84  
    85  // Syncing returns true if initial sync is still running.
    86  func (s *Service) Syncing() bool {
    87  	return s.syncing.Load()
    88  }
    89  
    90  // Synced returns true if initial sync has been completed.
    91  func (s *Service) Synced() bool {
    92  	return s.synced.Load()
    93  }
    94  
    95  // Resync allows a node to start syncing again if it has fallen
    96  // behind the current network head.
    97  func (s *Service) Resync() error {
    98  	// Set it to false since we are syncing again.
    99  	s.markSyncing()
   100  	event.GlobalEvent.Send(common.DownloaderStartEvent{})
   101  	defer func() {
   102  		s.markSynced()
   103  		event.GlobalEvent.Send(common.DownloaderFinishEvent{})
   104  	}() // Reset it at the end of the method.
   105  	//
   106  	beforeBlockNr := s.cfg.Chain.CurrentBlock().Number64()
   107  	highestExpectedBlockNr := s.waitForMinimumPeers()
   108  	if err := s.roundRobinSync(highestExpectedBlockNr); err != nil {
   109  		log.Error("Resync fail", "err", err, "highestExpectedBlockNr", highestExpectedBlockNr, "currentNr", s.cfg.Chain.CurrentBlock().Number64(), "beforeResyncBlockNr", beforeBlockNr)
   110  		return err
   111  	}
   112  	//
   113  	log.Info("Resync attempt complete", "highestExpectedBlockNr", highestExpectedBlockNr, "currentNr", s.cfg.Chain.CurrentBlock().Number64(), "beforeResyncBlockNr", beforeBlockNr)
   114  	return nil
   115  }
   116  
   117  func (s *Service) waitForMinimumPeers() (highestExpectedBlockNr *uint256.Int) {
   118  	required := s.cfg.P2P.GetConfig().MinSyncPeers
   119  	var peers []peer.ID
   120  	for {
   121  		//todo
   122  		highestExpectedBlockNr, peers = s.cfg.P2P.Peers().BestPeers(s.cfg.P2P.GetConfig().MinSyncPeers, s.cfg.Chain.CurrentBlock().Number64())
   123  		if len(peers) >= required {
   124  			break
   125  		}
   126  		log.Info("Waiting for enough suitable peers before syncing (initial-sync.Server)", "suitable", len(peers), "required", required)
   127  		time.Sleep(handshakePollingInterval)
   128  	}
   129  	return
   130  }
   131  
   132  // markSynced marks node as synced and notifies feed listeners.
   133  func (s *Service) markSyncing() {
   134  	s.syncing.Swap(true)
   135  }
   136  
   137  // markSynced marks node as synced and notifies feed listeners.
   138  func (s *Service) markSynced() {
   139  	s.syncing.Swap(false)
   140  	s.synced.Swap(true)
   141  }