github.com/turingchain2020/turingchain@v1.1.21/blockchain/download.go (about)

     1  // Copyright Turing Corp. 2018 All Rights Reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package blockchain
     6  
     7  import (
     8  	"fmt"
     9  	"sync/atomic"
    10  	"time"
    11  
    12  	"github.com/turingchain2020/turingchain/common"
    13  	"github.com/turingchain2020/turingchain/types"
    14  	"github.com/golang/protobuf/proto"
    15  )
    16  
    17  //var
    18  var (
    19  	tempBlockKey     = []byte("TB:")
    20  	lastTempBlockKey = []byte("LTB:")
    21  )
    22  
    23  //const
    24  const (
    25  	//waitTimeDownLoad 节点启动之后等待开始快速下载的时间秒,超时就切换到普通同步模式
    26  	waitTimeDownLoad = 120
    27  
    28  	//快速下载时需要的最少peer数量
    29  	bestPeerCount = 2
    30  
    31  	normalDownLoadMode = 0
    32  	fastDownLoadMode   = 1
    33  	chunkDownLoadMode  = 2
    34  )
    35  
    36  //DownLoadInfo blockchain模块下载block处理结构体
    37  type DownLoadInfo struct {
    38  	StartHeight int64
    39  	EndHeight   int64
    40  	Pids        []string
    41  }
    42  
    43  //ErrCountInfo  启动download时read一个block失败等待最长时间为2分钟,120秒
    44  type ErrCountInfo struct {
    45  	Height int64
    46  	Count  int64
    47  }
    48  
    49  //存储temp block height 对应的block
    50  func calcHeightToTempBlockKey(height int64) []byte {
    51  	return append(tempBlockKey, []byte(fmt.Sprintf("%012d", height))...)
    52  }
    53  
    54  //存储last temp block height
    55  func calcLastTempBlockHeightKey() []byte {
    56  	return lastTempBlockKey
    57  }
    58  
    59  //GetDownloadSyncStatus 获取下载区块的同步模式
    60  func (chain *BlockChain) GetDownloadSyncStatus() int {
    61  	chain.downLoadModeLock.Lock()
    62  	defer chain.downLoadModeLock.Unlock()
    63  	return chain.downloadMode
    64  }
    65  
    66  //UpdateDownloadSyncStatus 更新下载区块的同步模式
    67  func (chain *BlockChain) UpdateDownloadSyncStatus(mode int) {
    68  	chain.downLoadModeLock.Lock()
    69  	defer chain.downLoadModeLock.Unlock()
    70  	chain.downloadMode = mode
    71  }
    72  
    73  //FastDownLoadBlocks 开启快速下载区块的模式
    74  func (chain *BlockChain) FastDownLoadBlocks() {
    75  	curHeight := chain.GetBlockHeight()
    76  	lastTempHight := chain.GetLastTempBlockHeight()
    77  
    78  	synlog.Info("FastDownLoadBlocks", "curHeight", curHeight, "lastTempHight", lastTempHight)
    79  
    80  	//需要执行完上次已经下载并临时存贮在db中的blocks
    81  	if lastTempHight != -1 && lastTempHight > curHeight {
    82  		chain.ReadBlockToExec(lastTempHight, false)
    83  	}
    84  	//1:满足bestpeer数量,并且落后区块数量大于5000个开启快速同步
    85  	//2:落后区块数量小于5000个不开启快速同步,启动普通同步模式
    86  	//3:启动二分钟如果还不满足快速下载的条件就直接退出,启动普通同步模式
    87  
    88  	startTime := types.Now()
    89  
    90  	for {
    91  		curheight := chain.GetBlockHeight()
    92  		peerMaxBlkHeight := chain.GetPeerMaxBlkHeight()
    93  		pids := chain.GetBestChainPids()
    94  		//节点启动时只有落后最优链batchsyncblocknum个区块时才开启这种下载模式
    95  		if pids != nil && peerMaxBlkHeight != -1 && curheight+batchsyncblocknum >= peerMaxBlkHeight {
    96  			chain.UpdateDownloadSyncStatus(normalDownLoadMode)
    97  			synlog.Info("FastDownLoadBlocks:quit!", "curheight", curheight, "peerMaxBlkHeight", peerMaxBlkHeight)
    98  			break
    99  		} else if curheight+batchsyncblocknum < peerMaxBlkHeight && len(pids) >= bestPeerCount {
   100  			synlog.Info("start download blocks!FastDownLoadBlocks", "curheight", curheight, "peerMaxBlkHeight", peerMaxBlkHeight)
   101  			go chain.ProcDownLoadBlocks(curheight, peerMaxBlkHeight, false, pids)
   102  			go chain.ReadBlockToExec(peerMaxBlkHeight, true)
   103  			break
   104  		} else if types.Since(startTime) > waitTimeDownLoad*time.Second || chain.cfg.SingleMode {
   105  			chain.UpdateDownloadSyncStatus(normalDownLoadMode)
   106  			synlog.Info("FastDownLoadBlocks:waitTimeDownLoad:quit!", "curheight", curheight, "peerMaxBlkHeight", peerMaxBlkHeight, "pids", pids)
   107  			break
   108  		} else {
   109  			synlog.Info("FastDownLoadBlocks task sleep 1 second !")
   110  			time.Sleep(time.Second)
   111  		}
   112  	}
   113  }
   114  
   115  //ReadBlockToExec 执行快速下载临时存储在db中的block
   116  func (chain *BlockChain) ReadBlockToExec(height int64, isNewStart bool) {
   117  	synlog.Info("ReadBlockToExec starting!!!", "height", height, "isNewStart", isNewStart)
   118  	var waitCount ErrCountInfo
   119  	waitCount.Height = 0
   120  	waitCount.Count = 0
   121  	cfg := chain.client.GetConfig()
   122  	for {
   123  		select {
   124  		case <-chain.quit:
   125  			return
   126  		default:
   127  		}
   128  		curheight := chain.GetBlockHeight()
   129  		peerMaxBlkHeight := chain.GetPeerMaxBlkHeight()
   130  
   131  		// 节点同步阶段自己高度小于最大高度batchsyncblocknum时存储block到db批量处理时不刷盘
   132  		if peerMaxBlkHeight > curheight+batchsyncblocknum && !chain.cfgBatchSync {
   133  			atomic.CompareAndSwapInt32(&chain.isbatchsync, 1, 0)
   134  		} else {
   135  			atomic.CompareAndSwapInt32(&chain.isbatchsync, 0, 1)
   136  		}
   137  		if (curheight >= peerMaxBlkHeight && peerMaxBlkHeight != -1) || curheight >= height {
   138  			chain.cancelDownLoadFlag(isNewStart)
   139  			synlog.Info("ReadBlockToExec complete!", "curheight", curheight, "height", height, "peerMaxBlkHeight", peerMaxBlkHeight)
   140  			break
   141  		}
   142  		block, err := chain.ReadBlockByHeight(curheight + 1)
   143  		if err != nil {
   144  			//在downLoadTask任务退出后,尝试获取block2分钟,还获取不到就直接退出download下载
   145  			if isNewStart {
   146  				if !chain.downLoadTask.InProgress() {
   147  					if waitCount.Height == curheight+1 {
   148  						waitCount.Count++
   149  					} else {
   150  						waitCount.Height = curheight + 1
   151  						waitCount.Count = 1
   152  					}
   153  					if waitCount.Count >= 120 {
   154  						chain.cancelDownLoadFlag(isNewStart)
   155  						synlog.Error("ReadBlockToExec:ReadBlockByHeight:timeout", "height", curheight+1, "peerMaxBlkHeight", peerMaxBlkHeight, "err", err)
   156  						break
   157  					}
   158  					time.Sleep(time.Second)
   159  					continue
   160  				} else {
   161  					synlog.Info("ReadBlockToExec:ReadBlockByHeight", "height", curheight+1, "peerMaxBlkHeight", peerMaxBlkHeight, "err", err)
   162  					time.Sleep(time.Second)
   163  					continue
   164  				}
   165  			} else {
   166  				chain.cancelDownLoadFlag(isNewStart)
   167  				synlog.Error("ReadBlockToExec:ReadBlockByHeight", "height", curheight+1, "peerMaxBlkHeight", peerMaxBlkHeight, "err", err)
   168  				break
   169  			}
   170  		}
   171  		_, ismain, isorphan, err := chain.ProcessBlock(false, &types.BlockDetail{Block: block}, "download", true, -1)
   172  		if err != nil {
   173  			//执行失败强制结束快速下载模式并切换到普通下载模式
   174  			if isNewStart && chain.downLoadTask.InProgress() {
   175  				Err := chain.downLoadTask.Cancel()
   176  				if Err != nil {
   177  					synlog.Error("ReadBlockToExec:downLoadTask.Cancel!", "height", block.Height, "hash", common.ToHex(block.Hash(cfg)), "isNewStart", isNewStart, "err", Err)
   178  				}
   179  				chain.DefaultDownLoadInfo()
   180  			}
   181  
   182  			//清除快速下载的标识并从缓存中删除此执行失败的区块,
   183  			chain.cancelDownLoadFlag(isNewStart)
   184  			chain.blockStore.db.Delete(calcHeightToTempBlockKey(block.Height))
   185  
   186  			synlog.Error("ReadBlockToExec:ProcessBlock:err!", "height", block.Height, "hash", common.ToHex(block.Hash(cfg)), "isNewStart", isNewStart, "err", err)
   187  			break
   188  		}
   189  		synlog.Debug("ReadBlockToExec:ProcessBlock:success!", "height", block.Height, "ismain", ismain, "isorphan", isorphan, "hash", common.ToHex(block.Hash(cfg)))
   190  	}
   191  }
   192  
   193  //CancelDownLoadFlag 清除快速下载模式的一些标志
   194  func (chain *BlockChain) cancelDownLoadFlag(isNewStart bool) {
   195  	if isNewStart {
   196  		chain.UpdateDownloadSyncStatus(normalDownLoadMode)
   197  	}
   198  	chain.DelLastTempBlockHeight()
   199  	synlog.Info("cancelFastDownLoadFlag", "isNewStart", isNewStart)
   200  }
   201  
   202  //ReadBlockByHeight 从数据库中读取快速下载临时存储的block信息
   203  func (chain *BlockChain) ReadBlockByHeight(height int64) (*types.Block, error) {
   204  	blockByte, err := chain.blockStore.db.Get(calcHeightToTempBlockKey(height))
   205  	if blockByte == nil || err != nil {
   206  		return nil, types.ErrHeightNotExist
   207  	}
   208  	var block types.Block
   209  	err = proto.Unmarshal(blockByte, &block)
   210  	if err != nil {
   211  		storeLog.Error("ReadBlockByHeight", "err", err)
   212  		return nil, err
   213  	}
   214  	//读取成功之后将将此临时存贮删除
   215  	err = chain.blockStore.db.Delete(calcHeightToTempBlockKey(height - 1))
   216  	if err != nil {
   217  		storeLog.Error("ReadBlockByHeight:Delete", "height", height, "err", err)
   218  	}
   219  	return &block, err
   220  }
   221  
   222  //WriteBlockToDbTemp 快速下载的block临时存贮到数据库
   223  func (chain *BlockChain) WriteBlockToDbTemp(block *types.Block, lastHeightSave bool) error {
   224  	if block == nil {
   225  		panic("WriteBlockToDbTemp block is nil")
   226  	}
   227  	sync := true
   228  	if atomic.LoadInt32(&chain.isbatchsync) == 0 {
   229  		sync = false
   230  	}
   231  	beg := types.Now()
   232  	defer func() {
   233  		chainlog.Debug("WriteBlockToDbTemp", "height", block.Height, "sync", sync, "cost", types.Since(beg))
   234  	}()
   235  	newbatch := chain.blockStore.NewBatch(sync)
   236  
   237  	blockByte, err := proto.Marshal(block)
   238  	if err != nil {
   239  		chainlog.Error("WriteBlockToDbTemp:Marshal", "height", block.Height)
   240  	}
   241  	newbatch.Set(calcHeightToTempBlockKey(block.Height), blockByte)
   242  	if lastHeightSave {
   243  		heightbytes := types.Encode(&types.Int64{Data: block.Height})
   244  		newbatch.Set(calcLastTempBlockHeightKey(), heightbytes)
   245  	}
   246  	err = newbatch.Write()
   247  	if err != nil {
   248  		panic(err)
   249  	}
   250  	return nil
   251  }
   252  
   253  //GetLastTempBlockHeight 从数据库中获取快速下载的最新的block高度
   254  func (chain *BlockChain) GetLastTempBlockHeight() int64 {
   255  	heightbytes, err := chain.blockStore.db.Get(calcLastTempBlockHeightKey())
   256  	if heightbytes == nil || err != nil {
   257  		chainlog.Error("GetLastTempBlockHeight", "err", err)
   258  		return -1
   259  	}
   260  
   261  	var height types.Int64
   262  	err = types.Decode(heightbytes, &height)
   263  	if err != nil {
   264  		chainlog.Error("GetLastTempBlockHeight:Decode", "err", err)
   265  		return -1
   266  	}
   267  	return height.Data
   268  }
   269  
   270  //DelLastTempBlockHeight 快速下载结束时删除此标志位
   271  func (chain *BlockChain) DelLastTempBlockHeight() {
   272  	err := chain.blockStore.db.Delete(calcLastTempBlockHeightKey())
   273  	if err != nil {
   274  		synlog.Error("DelLastTempBlockHeight", "err", err)
   275  	}
   276  }
   277  
   278  //ProcDownLoadBlocks 处理下载blocks
   279  func (chain *BlockChain) ProcDownLoadBlocks(StartHeight int64, EndHeight int64, chunkDown bool, pids []string) {
   280  	info := chain.GetDownLoadInfo()
   281  
   282  	//可能存在上次DownLoad处理过程中下载区块超时,DownLoad任务退出,但DownLoad没有恢复成默认值
   283  	if info.StartHeight != -1 || info.EndHeight != -1 {
   284  		synlog.Info("ProcDownLoadBlocks", "pids", info.Pids, "StartHeight", info.StartHeight, "EndHeight", info.EndHeight)
   285  	}
   286  
   287  	chain.DefaultDownLoadInfo()
   288  	chain.InitDownLoadInfo(StartHeight, EndHeight, pids)
   289  	if chunkDown {
   290  		chain.ReqDownLoadChunkBlocks()
   291  	} else {
   292  		chain.ReqDownLoadBlocks()
   293  	}
   294  }
   295  
   296  //InitDownLoadInfo 开始新的DownLoad处理
   297  func (chain *BlockChain) InitDownLoadInfo(StartHeight int64, EndHeight int64, pids []string) {
   298  	chain.downLoadlock.Lock()
   299  	defer chain.downLoadlock.Unlock()
   300  
   301  	chain.downLoadInfo.StartHeight = StartHeight
   302  	chain.downLoadInfo.EndHeight = EndHeight
   303  	chain.downLoadInfo.Pids = pids
   304  	synlog.Debug("InitDownLoadInfo begin", "StartHeight", StartHeight, "EndHeight", EndHeight, "pids", pids)
   305  
   306  }
   307  
   308  //DefaultDownLoadInfo 将DownLoadInfo恢复成默认值
   309  func (chain *BlockChain) DefaultDownLoadInfo() {
   310  	chain.downLoadlock.Lock()
   311  	defer chain.downLoadlock.Unlock()
   312  
   313  	chain.downLoadInfo.StartHeight = -1
   314  	chain.downLoadInfo.EndHeight = -1
   315  	chain.downLoadInfo.Pids = nil
   316  	synlog.Debug("DefaultDownLoadInfo")
   317  }
   318  
   319  //GetDownLoadInfo 获取DownLoadInfo
   320  func (chain *BlockChain) GetDownLoadInfo() *DownLoadInfo {
   321  	chain.downLoadlock.Lock()
   322  	defer chain.downLoadlock.Unlock()
   323  	return chain.downLoadInfo
   324  }
   325  
   326  //UpdateDownLoadStartHeight 更新DownLoad请求的起始block高度
   327  func (chain *BlockChain) UpdateDownLoadStartHeight(StartHeight int64) {
   328  	chain.downLoadlock.Lock()
   329  	defer chain.downLoadlock.Unlock()
   330  
   331  	chain.downLoadInfo.StartHeight = StartHeight
   332  	synlog.Debug("UpdateDownLoadStartHeight", "StartHeight", chain.downLoadInfo.StartHeight, "EndHeight", chain.downLoadInfo.EndHeight, "pids", len(chain.downLoadInfo.Pids))
   333  }
   334  
   335  //UpdateDownLoadPids 更新bestpeers列表
   336  func (chain *BlockChain) UpdateDownLoadPids() {
   337  	pids := chain.GetBestChainPids()
   338  
   339  	chain.downLoadlock.Lock()
   340  	defer chain.downLoadlock.Unlock()
   341  	if len(pids) != 0 {
   342  		chain.downLoadInfo.Pids = pids
   343  		synlog.Info("UpdateDownLoadPids", "StartHeight", chain.downLoadInfo.StartHeight, "EndHeight", chain.downLoadInfo.EndHeight, "pids", len(chain.downLoadInfo.Pids))
   344  	}
   345  }
   346  
   347  //ReqDownLoadBlocks 请求DownLoad处理的blocks
   348  func (chain *BlockChain) ReqDownLoadBlocks() {
   349  	info := chain.GetDownLoadInfo()
   350  	if info.StartHeight != -1 && info.EndHeight != -1 && info.Pids != nil {
   351  		synlog.Info("ReqDownLoadBlocks", "StartHeight", info.StartHeight, "EndHeight", info.EndHeight, "pids", len(info.Pids))
   352  		err := chain.FetchBlock(info.StartHeight, info.EndHeight, info.Pids, true)
   353  		if err != nil {
   354  			synlog.Error("ReqDownLoadBlocks:FetchBlock", "err", err)
   355  		}
   356  	}
   357  }
   358  
   359  //DownLoadTimeOutProc 快速下载模式下载区块超时的处理函数
   360  func (chain *BlockChain) DownLoadTimeOutProc(height int64) {
   361  	info := chain.GetDownLoadInfo()
   362  	synlog.Info("DownLoadTimeOutProc", "timeoutheight", height, "StartHeight", info.StartHeight, "EndHeight", info.EndHeight)
   363  
   364  	// 下载超时需要检测下载的pid是否存在,如果所有下载peer都失连,需要退出本次下载
   365  	// 在处理分叉时从指定节点下载区块超时时,可能是节点失连导致,此时需要退出本次下载
   366  	if info.Pids != nil {
   367  		var exist bool
   368  		for _, pid := range info.Pids {
   369  			peerinfo := chain.GetPeerInfo(pid)
   370  			if peerinfo != nil {
   371  				exist = true
   372  			}
   373  		}
   374  		if !exist {
   375  			synlog.Info("DownLoadTimeOutProc:peer not exist!", "info.Pids", info.Pids, "GetPeers", chain.GetPeers())
   376  			return
   377  		}
   378  	}
   379  	if info.StartHeight != -1 && info.EndHeight != -1 && info.Pids != nil {
   380  		//从超时的高度继续下载区块
   381  		if info.StartHeight > height {
   382  			chain.UpdateDownLoadStartHeight(height)
   383  			info.StartHeight = height
   384  		}
   385  		synlog.Info("DownLoadTimeOutProc:FetchBlock", "StartHeight", info.StartHeight, "EndHeight", info.EndHeight, "pids", len(info.Pids))
   386  		err := chain.FetchBlock(info.StartHeight, info.EndHeight, info.Pids, true)
   387  		if err != nil {
   388  			synlog.Error("DownLoadTimeOutProc:FetchBlock", "err", err)
   389  		}
   390  	}
   391  }
   392  
   393  // DownLoadBlocks 下载区块
   394  func (chain *BlockChain) DownLoadBlocks() {
   395  	if !chain.cfg.DisableShard && chain.cfg.EnableFetchP2pstore {
   396  		// 1.节点开启时候首先尝试进行chunkDownLoad下载
   397  		chain.UpdateDownloadSyncStatus(chunkDownLoadMode) // 默认模式是fastDownLoadMode
   398  		if chain.GetDownloadSyncStatus() == chunkDownLoadMode {
   399  			chain.ChunkDownLoadBlocks()
   400  		}
   401  	} else {
   402  		// 2.其次尝试开启快速下载模式,目前默认开启
   403  		if chain.GetDownloadSyncStatus() == fastDownLoadMode {
   404  			chain.FastDownLoadBlocks()
   405  		}
   406  	}
   407  }
   408  
   409  //ChunkDownLoadBlocks 开启快速下载区块的模式
   410  func (chain *BlockChain) ChunkDownLoadBlocks() {
   411  	curHeight := chain.GetBlockHeight()
   412  	lastTempHight := chain.GetLastTempBlockHeight()
   413  
   414  	synlog.Info("ChunkDownLoadBlocks", "curHeight", curHeight, "lastTempHight", lastTempHight)
   415  
   416  	//需要执行完上次已经下载并临时存贮在db中的blocks
   417  	if lastTempHight != -1 && lastTempHight > curHeight {
   418  		chain.ReadBlockToExec(lastTempHight, false)
   419  	}
   420  	//1:落后区块数量大于MaxRollBlockNum个开启chunk同步,否则开启快速同步
   421  	//2:启动二分钟如果还不满足chunk同步,则进行快速同步
   422  
   423  	startTime := types.Now()
   424  
   425  	for {
   426  		curheight := chain.GetBlockHeight()
   427  		peerMaxBlkHeight := chain.GetPeerMaxBlkHeight()
   428  		targetHeight := chain.calcSafetyChunkHeight(peerMaxBlkHeight) // 节点生成chunk的高度滞后于当前高度
   429  		pids := chain.GetBestChainPids()
   430  		//节点启动时只有落后最优链batchsyncblocknum个区块时才开启这种下载模式
   431  		if pids != nil && peerMaxBlkHeight != -1 && curheight+batchsyncblocknum >= peerMaxBlkHeight {
   432  			synlog.Info("ChunkDownLoadBlocks:quit!", "curheight", curheight, "peerMaxBlkHeight", peerMaxBlkHeight)
   433  			chain.UpdateDownloadSyncStatus(normalDownLoadMode)
   434  			break
   435  		} else if curheight < targetHeight && pids != nil {
   436  			synlog.Info("start download blocks!ChunkDownLoadBlocks", "curheight", curheight, "peerMaxBlkHeight", peerMaxBlkHeight, "targetHeight", targetHeight)
   437  			go chain.ProcDownLoadBlocks(curheight, targetHeight, true, pids)
   438  			// 下载chunk后在该进程执行临时区块
   439  			go chain.ReadBlockToExec(targetHeight, true)
   440  			break
   441  		} else if types.Since(startTime) > waitTimeDownLoad*time.Second*3 || chain.cfg.SingleMode {
   442  			synlog.Info("ChunkDownLoadBlocks:waitTimeDownLoad:quit!", "curheight", curheight, "peerMaxBlkHeight", peerMaxBlkHeight, "pids", pids)
   443  			chain.UpdateDownloadSyncStatus(normalDownLoadMode)
   444  			break
   445  		} else {
   446  			synlog.Info("ChunkDownLoadBlocks task sleep 1 second !")
   447  			time.Sleep(time.Second)
   448  		}
   449  	}
   450  }
   451  
   452  //ReqDownLoadChunkBlocks 请求DownLoad处理的blocks
   453  func (chain *BlockChain) ReqDownLoadChunkBlocks() {
   454  	info := chain.GetDownLoadInfo()
   455  	if info.StartHeight != -1 && info.EndHeight != -1 && info.Pids != nil {
   456  		synlog.Info("ReqDownLoadChunkBlocks", "StartHeight", info.StartHeight, "EndHeight", info.EndHeight, "pids", len(info.Pids))
   457  		err := chain.FetchChunkBlock(info.StartHeight, info.EndHeight, info.Pids, true)
   458  		if err != nil {
   459  			synlog.Error("ReqDownLoadChunkBlocks:FetchBlock", "err", err)
   460  		}
   461  	}
   462  }
   463  
   464  //DownLoadChunkTimeOutProc 快速下载模式下载区块超时的处理函数
   465  func (chain *BlockChain) DownLoadChunkTimeOutProc(height int64) {
   466  	info := chain.GetDownLoadInfo()
   467  	synlog.Info("DownLoadChunkTimeOutProc", "real chunkNum", height, "info.StartHeight", info.StartHeight, "info.EndHeight", info.EndHeight)
   468  	//  TODO 需要检查当前是否有连接节点,如果没有则可能没有连接节点导致超时
   469  	if len(chain.GetPeers()) == 0 {
   470  		synlog.Info("DownLoadChunkTimeOutProc:peers not exist!")
   471  		return
   472  	}
   473  	if info.StartHeight != -1 && info.EndHeight != -1 && info.Pids != nil {
   474  		//从超时的高度继续下载区块
   475  		if info.StartHeight > height {
   476  			chain.UpdateDownLoadStartHeight(height)
   477  			info.StartHeight = height
   478  		}
   479  		synlog.Info("DownLoadChunkTimeOutProc:FetchChunkBlock", "StartHeight", info.StartHeight, "EndHeight", info.EndHeight, "pids", len(info.Pids))
   480  		err := chain.FetchChunkBlock(info.StartHeight, info.EndHeight, info.Pids, true)
   481  		if err != nil {
   482  			synlog.Error("DownLoadChunkTimeOutProc:FetchChunkBlock", "err", err)
   483  		}
   484  	}
   485  }