github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/netsync/chainmgr/fast_sync.go (about) 1 package chainmgr 2 3 import ( 4 "sync" 5 6 log "github.com/sirupsen/logrus" 7 8 "github.com/bytom/bytom/errors" 9 "github.com/bytom/bytom/netsync/peers" 10 "github.com/bytom/bytom/p2p/security" 11 "github.com/bytom/bytom/protocol/bc" 12 "github.com/bytom/bytom/protocol/bc/types" 13 ) 14 15 var ( 16 minSizeOfSyncSkeleton = 2 17 maxSizeOfSyncSkeleton = 11 18 numOfBlocksSkeletonGap = maxNumOfBlocksPerMsg 19 maxNumOfBlocksPerSync = numOfBlocksSkeletonGap * uint64(maxSizeOfSyncSkeleton-1) 20 fastSyncPivotGap = uint64(64) 21 minGapStartFastSync = uint64(128) 22 23 errNoSyncPeer = errors.New("can't find sync peer") 24 errSkeletonSize = errors.New("fast sync skeleton size wrong") 25 errNoMainSkeleton = errors.New("No main skeleton found") 26 errNoSkeletonFound = errors.New("No skeleton found") 27 ) 28 29 type fastSync struct { 30 chain Chain 31 msgFetcher MsgFetcher 32 blockProcessor *blockProcessor 33 peers *peers.PeerSet 34 mainSyncPeer *peers.Peer 35 } 36 37 func newFastSync(chain Chain, msgFetcher MsgFetcher, storage *storage, peers *peers.PeerSet) *fastSync { 38 return &fastSync{ 39 chain: chain, 40 msgFetcher: msgFetcher, 41 blockProcessor: newBlockProcessor(chain, storage, peers), 42 peers: peers, 43 } 44 } 45 46 func (fs *fastSync) blockLocator() []*bc.Hash { 47 header := fs.chain.BestBlockHeader() 48 locator := []*bc.Hash{} 49 step := uint64(1) 50 51 for header != nil { 52 headerHash := header.Hash() 53 locator = append(locator, &headerHash) 54 if header.Height == 0 { 55 break 56 } 57 58 var err error 59 if header.Height < step { 60 header, err = fs.chain.GetHeaderByHeight(0) 61 } else { 62 header, err = fs.chain.GetHeaderByHeight(header.Height - step) 63 } 64 if err != nil { 65 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("blockKeeper fail on get blockLocator") 66 break 67 } 68 69 if len(locator) >= 9 { 70 step *= 2 71 } 72 } 73 return locator 74 } 75 76 // createFetchBlocksTasks get the skeleton and assign tasks according to the skeleton. 77 func (fs *fastSync) createFetchBlocksTasks(stopBlock *types.Block) ([]*fetchBlocksWork, error) { 78 // Find peers that meet the height requirements. 79 peers := fs.peers.GetPeersByHeight(stopBlock.Height + fastSyncPivotGap) 80 if len(peers) == 0 { 81 return nil, errNoSyncPeer 82 } 83 84 // parallel fetch the skeleton from peers. 85 stopHash := stopBlock.Hash() 86 skeletonMap := fs.msgFetcher.parallelFetchHeaders(peers, fs.blockLocator(), &stopHash, numOfBlocksSkeletonGap-1) 87 if len(skeletonMap) == 0 { 88 return nil, errNoSkeletonFound 89 } 90 91 mainSkeleton, ok := skeletonMap[fs.mainSyncPeer.ID()] 92 if !ok { 93 return nil, errNoMainSkeleton 94 } 95 96 if len(mainSkeleton) < minSizeOfSyncSkeleton { 97 fs.peers.ProcessIllegal(fs.mainSyncPeer.ID(), security.LevelMsgIllegal, errSkeletonSize.Error()) 98 return nil, errSkeletonSize 99 } 100 101 // collect peers that match the skeleton of the primary sync peer 102 fs.msgFetcher.addSyncPeer(fs.mainSyncPeer.ID()) 103 delete(skeletonMap, fs.mainSyncPeer.ID()) 104 for peerID, skeleton := range skeletonMap { 105 if len(skeleton) != len(mainSkeleton) { 106 log.WithFields(log.Fields{"module": logModule, "main skeleton": len(mainSkeleton), "got skeleton": len(skeleton)}).Warn("different skeleton length") 107 continue 108 } 109 110 for i, header := range skeleton { 111 if header.Hash() != mainSkeleton[i].Hash() { 112 log.WithFields(log.Fields{"module": logModule, "header index": i, "main skeleton": mainSkeleton[i].Hash(), "got skeleton": header.Hash()}).Warn("different skeleton hash") 113 continue 114 } 115 } 116 fs.msgFetcher.addSyncPeer(peerID) 117 } 118 119 blockFetchTasks := make([]*fetchBlocksWork, 0) 120 // create download task 121 for i := 0; i < len(mainSkeleton)-1 && i < maxSizeOfSyncSkeleton-1; i++ { 122 blockFetchTasks = append(blockFetchTasks, &fetchBlocksWork{startHeader: mainSkeleton[i], stopHeader: mainSkeleton[i+1]}) 123 } 124 125 return blockFetchTasks, nil 126 } 127 128 func (fs *fastSync) process() error { 129 stopBlock, err := fs.findSyncRange() 130 if err != nil { 131 return err 132 } 133 134 tasks, err := fs.createFetchBlocksTasks(stopBlock) 135 if err != nil { 136 return err 137 } 138 139 downloadNotifyCh := make(chan struct{}, 1) 140 processStopCh := make(chan struct{}) 141 var wg sync.WaitGroup 142 wg.Add(2) 143 go fs.msgFetcher.parallelFetchBlocks(tasks, downloadNotifyCh, processStopCh, &wg) 144 go fs.blockProcessor.process(downloadNotifyCh, processStopCh, tasks[0].startHeader.Height, &wg) 145 wg.Wait() 146 fs.msgFetcher.resetParameter() 147 log.WithFields(log.Fields{"module": logModule, "height": fs.chain.BestBlockHeight()}).Info("fast sync complete") 148 return nil 149 } 150 151 // findSyncRange find the start and end of this sync. 152 // sync length cannot be greater than maxFastSyncBlocksNum. 153 func (fs *fastSync) findSyncRange() (*types.Block, error) { 154 bestHeight := fs.chain.BestBlockHeight() 155 length := fs.mainSyncPeer.Height() - fastSyncPivotGap - bestHeight 156 if length > maxNumOfBlocksPerSync { 157 length = maxNumOfBlocksPerSync 158 } 159 160 return fs.msgFetcher.requireBlock(fs.mainSyncPeer.ID(), bestHeight+length) 161 } 162 163 func (fs *fastSync) setSyncPeer(peer *peers.Peer) { 164 fs.mainSyncPeer = peer 165 }