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 }