/// <summary> /// Download and attach blocks /// UseSuggestedPeer == true: Download blocks from suggested peer directly; /// Target download height > peer lib height, download blocks from suggested peer; /// Target download height <= peer lib height, select a random peer to download. /// </summary> /// <param name="downloadBlockDto"></param> /// <returns></returns> public async Task <DownloadBlocksResult> DownloadBlocksAsync(DownloadBlockDto downloadBlockDto) { var downloadResult = new DownloadBlocksResult(); var peerPubkey = downloadBlockDto.SuggestedPeerPubkey; try { if (UseSuggestedPeer(downloadBlockDto)) { downloadResult = await DownloadBlocksAsync(peerPubkey, downloadBlockDto); } else { // If cannot get the blocks, there should be network problems or bad peer, // because we have selected peer with lib height greater than or equal to the target height. // 1. network problems, need to retry from other peer. // 2. not network problems, this peer or the last peer is bad peer, we need to remove it. var downloadTargetHeight = downloadBlockDto.PreviousBlockHeight + downloadBlockDto.MaxBlockDownloadCount; var exceptedPeers = new List <string> { _blockSyncStateProvider.LastRequestPeerPubkey }; var retryTimes = 2; while (true) { peerPubkey = GetRandomPeerPubkey(downloadBlockDto.SuggestedPeerPubkey, downloadTargetHeight, exceptedPeers); downloadResult = await DownloadBlocksAsync(peerPubkey, downloadBlockDto); if (downloadResult.Success || retryTimes <= 0) { break; } exceptedPeers.Add(peerPubkey); retryTimes--; } if (downloadResult.Success && downloadResult.DownloadBlockCount == 0) { await CheckBadPeerAsync(peerPubkey, downloadBlockDto.PreviousBlockHash, downloadBlockDto.PreviousBlockHeight); } } } catch (BlockDownloadException e) { await LocalEventBus.PublishAsync(new BadPeerFoundEventData { BlockHash = e.BlockHash, BlockHeight = e.BlockHeight, PeerPubkey = e.PeerPubkey }); } return(downloadResult); }
private bool UseSuggestedPeer(DownloadBlockDto downloadBlockDto) { if (downloadBlockDto.UseSuggestedPeer) { return(true); } var suggestedPeer = _networkService.GetPeerByPubkey(downloadBlockDto.SuggestedPeerPubkey); var downloadTargetHeight = downloadBlockDto.PreviousBlockHeight + downloadBlockDto.MaxBlockDownloadCount; if (downloadTargetHeight > suggestedPeer.LastKnownLibHeight) { return(true); } return(false); }
/// <summary> /// Download and attach blocks /// UseSuggestedPeer == true: Download blocks from suggested peer directly; /// Target download height > peer lib height, download blocks from suggested peer; /// Target download height <= peer lib height, select a random peer to download. /// </summary> /// <param name="downloadBlockDto"></param> /// <returns></returns> public async Task <DownloadBlocksResult> DownloadBlocksAsync(DownloadBlockDto downloadBlockDto) { DownloadBlocksResult downloadResult; var peerPubkey = downloadBlockDto.SuggestedPeerPubkey; if (UseSuggestedPeer(downloadBlockDto)) { downloadResult = await DownloadBlocksAsync(downloadBlockDto.PreviousBlockHash, peerPubkey, downloadBlockDto.BatchRequestBlockCount, downloadBlockDto.MaxBlockDownloadCount); } else { // If cannot get the blocks, there should be network problems or bad peer, // because we have selected peer with lib height greater than or equal to the target height. // 1. network problems, need to retry from other peer. // 2. not network problems, this peer or the last peer is bad peer, we need to remove it. var downloadTargetHeight = downloadBlockDto.PreviousBlockHeight + downloadBlockDto.MaxBlockDownloadCount; var exceptedPeers = new List <string> { _blockSyncStateProvider.LastRequestPeerPubkey }; var retryTimes = 1; while (true) { peerPubkey = GetRandomPeerPubkey(downloadBlockDto.SuggestedPeerPubkey, downloadTargetHeight, exceptedPeers); downloadResult = await DownloadBlocksAsync(downloadBlockDto.PreviousBlockHash, peerPubkey, downloadBlockDto.BatchRequestBlockCount, downloadBlockDto.MaxBlockDownloadCount); if (downloadResult.Success || retryTimes <= 0) { break; } exceptedPeers.Add(peerPubkey); retryTimes--; } if (downloadResult.Success && downloadResult.DownloadBlockCount == 0) { var checkResult = await CheckIrreversibleBlockHashAsync(downloadBlockDto.PreviousBlockHash, downloadBlockDto.PreviousBlockHeight); if (checkResult.HasValue) { Logger.LogWarning( $"Found bad peer: peerPubkey: {peerPubkey}, block hash: {downloadBlockDto.PreviousBlockHash}, block height: {downloadBlockDto.PreviousBlockHeight}"); await LocalEventBus.PublishAsync(new IncorrectIrreversibleBlockEventData { BlockHash = downloadBlockDto.PreviousBlockHash, BlockHeight = downloadBlockDto.PreviousBlockHeight, BlockSenderPubkey = checkResult.Value ? peerPubkey : _blockSyncStateProvider.LastRequestPeerPubkey }); } } } return(downloadResult); }
private async Task <DownloadBlocksResult> DownloadBlocksAsync(string peerPubkey, DownloadBlockDto downloadBlockDto) { var downloadBlockCount = 0; var lastDownloadBlockHash = downloadBlockDto.PreviousBlockHash; var lastDownloadBlockHeight = downloadBlockDto.PreviousBlockHeight; Logger.LogDebug( $"Download blocks start with block height: {lastDownloadBlockHeight}, hash: {lastDownloadBlockHash}, PeerPubkey: {peerPubkey}"); while (downloadBlockCount < downloadBlockDto.MaxBlockDownloadCount) { var getBlocksResult = await _networkService.GetBlocksAsync(lastDownloadBlockHash, downloadBlockDto.BatchRequestBlockCount, peerPubkey); if (!getBlocksResult.Success) { return(new DownloadBlocksResult { Success = false }); } var blocksWithTransactions = getBlocksResult.Payload; if (blocksWithTransactions == null || !blocksWithTransactions.Any()) { Logger.LogDebug( $"No blocks returned from peer: {peerPubkey}, Previous block height: {lastDownloadBlockHeight}, hash: {lastDownloadBlockHash}."); break; } foreach (var blockWithTransactions in blocksWithTransactions) { Logger.LogDebug($"Processing block {blockWithTransactions}."); if (blockWithTransactions.Height != lastDownloadBlockHeight + 1 || blockWithTransactions.Header.PreviousBlockHash != lastDownloadBlockHash) { Logger.LogWarning( $"Received invalid block, peer: {peerPubkey}, block hash: {blockWithTransactions.GetHash()}, block height: {blockWithTransactions.Height}"); throw new BlockDownloadException(blockWithTransactions.GetHash(), blockWithTransactions.Height, peerPubkey); } lastDownloadBlockHash = blockWithTransactions.GetHash(); lastDownloadBlockHeight = blockWithTransactions.Height; EnqueueAttachBlockJob(blockWithTransactions, peerPubkey); downloadBlockCount++; } var lastBlock = blocksWithTransactions.Last(); lastDownloadBlockHash = lastBlock.GetHash(); lastDownloadBlockHeight = lastBlock.Height; } if (downloadBlockCount != 0) { _blockSyncStateProvider.SetDownloadJobTargetState(lastDownloadBlockHash, false); _blockSyncStateProvider.LastRequestPeerPubkey = peerPubkey; } return(new DownloadBlocksResult { Success = true, DownloadBlockCount = downloadBlockCount, LastDownloadBlockHash = lastDownloadBlockHash, LastDownloadBlockHeight = lastDownloadBlockHeight }); }
public async Task <DownloadBlocksResult> DownloadBlocksAsync(DownloadBlockDto downloadBlockDto) { var downloadBlockCount = 0; var lastDownloadBlockHash = downloadBlockDto.PreviousBlockHash; var lastDownloadBlockHeight = downloadBlockDto.PreviousBlockHeight; while (downloadBlockCount < downloadBlockDto.MaxBlockDownloadCount) { Logger.LogDebug( $"Request blocks start with block hash: {lastDownloadBlockHash}, block height: {lastDownloadBlockHeight}"); var blocksWithTransactions = await _networkService.GetBlocksAsync(lastDownloadBlockHash, downloadBlockDto.BatchRequestBlockCount, downloadBlockDto.SuggestedPeerPubkey); if (blocksWithTransactions == null || !blocksWithTransactions.Any()) { Logger.LogWarning("No blocks returned."); break; } if (blocksWithTransactions.First().Header.PreviousBlockHash != lastDownloadBlockHash) { throw new InvalidOperationException( $"Previous block not match previous {lastDownloadBlockHash}, network back {blocksWithTransactions.First().Header.PreviousBlockHash}"); } foreach (var blockWithTransactions in blocksWithTransactions) { Logger.LogDebug($"Processing block {blockWithTransactions}."); _blockSyncQueueService.Enqueue( async() => { await _blockSyncAttachService.AttachBlockWithTransactionsAsync(blockWithTransactions, async() => { _blockSyncStateProvider.TryUpdateDownloadJobTargetState(blockWithTransactions.GetHash(), true); }); }, OSConstants.BlockSyncAttachQueueName); downloadBlockCount++; } var lastBlock = blocksWithTransactions.Last(); lastDownloadBlockHash = lastBlock.GetHash(); lastDownloadBlockHeight = lastBlock.Height; } if (lastDownloadBlockHash != null) { _blockSyncStateProvider.SetDownloadJobTargetState(lastDownloadBlockHash, false); } return(new DownloadBlocksResult { DownloadBlockCount = downloadBlockCount, LastDownloadBlockHash = lastDownloadBlockHash, LastDownloadBlockHeight = lastDownloadBlockHeight }); }