async Task <bool> DownloadTransactionSlices(RemoteSyncItem remote, ChainSyncItem chainSyncItem) { var chainId = chainSyncItem.ChainId; var chainType = chainSyncItem.ChainType; var chainIndex = chainSyncItem.ChainIndex; chainSyncItem.UpdateLastBlockInfo((await remote.Client.DownloadLastBlockInfo(chainType, chainId, chainIndex)).Data); var localStartSlice = 0; var localLast = TransactionStorage.GetTransactionSlices(_storage, chainType, chainId, chainIndex, true).LastOrDefault(); if (localLast.Value != null) { localStartSlice = localLast.Value.SliceId + 1; } var remoteLastAvailableSlice = TransactionSliceInfo.GetSliceIndex(chainSyncItem.BlockState.LastTransactionId) - 1; if (remoteLastAvailableSlice < 0) // -1: ignore last "hot" slice { Log.Debug($"Skipping transaction slice download for chain {chainId}/{chainIndex}, no available remote stored slice found.", this); return(true); } var count = 0; if (remoteLastAvailableSlice >= 0 && remoteLastAvailableSlice >= localStartSlice) { for (var sliceId = localStartSlice; sliceId <= remoteLastAvailableSlice; sliceId++) { Log.Trace($"Downloading transaction slice {sliceId}/{remoteLastAvailableSlice} for chain {chainId}.", this); var sliceData = (await remote.Client.DownloadTransactionSlice(_storage, chainType, chainId, chainIndex, sliceId)).Data; var error = false; if (sliceData == null || !sliceData.Move()) { Log.Trace($"Slice {sliceId} download for chain {chainId}/{chainIndex} failed.", this); sliceData?.Delete(); error = true; } sliceData?.Dispose(); if (error) { return(false); } count++; } } if (count > 0) // check for new slices { return(await DownloadTransactionSlices(remote, chainSyncItem)); } return(true); }
async Task <bool> DownloadBlockSlices(RemoteSyncItem remote, ChainSyncItem chainSyncItem) { var chainType = chainSyncItem.ChainType; var chainId = chainSyncItem.ChainId; var chainIndex = chainSyncItem.ChainIndex; chainSyncItem.UpdateLastBlockInfo((await remote.Client.DownloadLastBlockInfo(chainType, chainId, chainIndex)).Data); var lastBlockId = chainSyncItem.BlockState.LastBlockId; if (lastBlockId <= Protocol.InvalidBlockId) { Log.Debug($"Skipping block slice download for chain {chainId}/{chainIndex}, blockid invalid.", this); return(true); } var remoteLastAvailableSlice = BlockSliceInfo.GetSliceIndex(lastBlockId) - 1; if (remoteLastAvailableSlice < 0) // -1: ignore last "hot" slice { Log.Debug($"Skipping block slice download for chain {chainId}/{chainIndex}, no available remote stored slice found.", this); return(true); } var localStartSlice = 0L; var localLast = BlockStorage.GetBlockSlices(_storage, chainType, chainId, chainIndex, true).LastOrDefault(); if (localLast.Value != null) { localStartSlice = localLast.Value.SliceIndex + 1; } if (!(chainSyncItem.LowestFoundBlockSliceId <= localStartSlice)) { Log.Error($"Download block slices for chain {chainId}/{chainIndex} failed, last local slice is {localStartSlice}, lowest remote slice found is {chainSyncItem.LowestFoundBlockSliceId}.", this); return(false); } var count = 0; for (var sliceIndex = localStartSlice; sliceIndex <= remoteLastAvailableSlice; sliceIndex++) { Log.Trace($"Downloading block slice {sliceIndex}/{remoteLastAvailableSlice} for chain {chainId}/{chainIndex}.", this); var sliceData = (await remote.Client.DownloadBlockSlice(_storage, chainType, chainId, chainIndex, sliceIndex)).Data; var error = false; if (sliceData == null || !sliceData.Move()) { Log.Trace($"Block slice {sliceIndex} download failed.", this); sliceData?.Delete(); error = true; } sliceData?.Dispose(); if (error) { return(false); } count++; } if (count > 0) { return(await DownloadBlockSlices(remote, chainSyncItem)); } return(true); }
async Task DownloadBlock(long blockId, ChainSyncItem chainSyncItem, HashSet <NodeConnection> nodeConnections, int tries = 0) { var retry = false; var sync = chainSyncItem.Sync; try { await sync.DownloadLock.WaitAsync(); SyncDownload download; lock (sync.Lock) { sync.NewBlocks.TryGetValue(blockId, out download); if (download == null) { return; } if (!download.TimedOut && download.Downloading) { return; } download.Downloading = true; download.DownloadTime = DateTime.UtcNow; } nodeConnections = nodeConnections ?? new HashSet <NodeConnection>(); foreach (var connection in nodeConnections) { var nodeInfo = connection.NodeInfo; if (nodeInfo.IsPublicEndPoint && nodeInfo.NodeId != _configuration.LocaleNodeInfo.NodeId) { var client = new NodeClient(nodeInfo.PublicEndPoint); var blockData = (await client.DownloadBlockData(chainSyncItem.ChainType, chainSyncItem.ChainId, chainSyncItem.ChainIndex, blockId)).Data; if (blockData != null) { download.UpdateBlockData(blockData); await HandleBlockData(blockData, nodeConnections); goto end; } else { retry = true; } } } foreach (var endPoint in chainSyncItem.Chain.AvailableEndpoints) { var client = new NodeClient(endPoint.EndPoint); if (endPoint.NodeInfo == null) { endPoint.NodeInfo = (await client.DownloadNodeInfo(_configuration.NetworkPublicKey)).Data; } if (endPoint.NodeInfo != null && endPoint.NodeInfo.NodeId != _configuration.LocaleNodeInfo.NodeId) { var blockData = (await client.DownloadBlockData(chainSyncItem.ChainType, chainSyncItem.ChainId, chainSyncItem.ChainIndex, blockId)).Data; if (blockData != null) { download.UpdateBlockData(blockData); await HandleBlockData(blockData, nodeConnections); goto end; } else { retry = true; } } } foreach (var connection in nodeConnections) { await connection.Send(new NodeBlockDataRequestMessage(chainSyncItem.ChainType, blockId, chainSyncItem.ChainId, chainSyncItem.ChainIndex) { SignKey = _configuration.LocaleNodePrivateKey }); await Task.Delay(50); } end: lock (sync.Lock) { download.Downloading = false; } } catch (Exception ex) { Log.HandleException(ex); } finally { sync.DownloadLock.Release(); } if (retry && tries < 10) { await Task.Delay(50 *(tries + 1)); TaskRunner.Run(() => DownloadBlock(blockId, chainSyncItem, nodeConnections, tries + 1)); } }
async Task <bool> DownloadBlocks(RemoteSyncItem remote, ChainSyncItem chainSyncItem) { var chainType = chainSyncItem.ChainType; var chainId = chainSyncItem.ChainId; var chainIndex = chainSyncItem.ChainIndex; chainSyncItem.UpdateLastBlockInfo((await remote.Client.DownloadLastBlockInfo(chainType, chainId, chainIndex)).Data); var chain = _chainManager.GetChain(chainSyncItem.ChainType, chainId, chainIndex); var lastChainBlockId = chainSyncItem.BlockState.LastBlockId; var lastBlock = chain.BlockStorage.LastBlock; var nextBlockId = Protocol.GenesisBlockId; if (lastBlock != null) { nextBlockId = lastBlock.BlockId + 1; } var sync = chainSyncItem.Sync; var newBlocks = chainSyncItem.Sync.NewBlocks; var lockitem = chainSyncItem.Sync.Lock; var count = 0; for (var blockId = nextBlockId; blockId <= lastChainBlockId; blockId++) { Log.Trace($"Downloading block {blockId}/{lastChainBlockId} for chain {chainId}/{chainIndex}.", this); var blockData = (await remote.Client.DownloadBlockData(chainType, chainId, chainIndex, blockId)).Data; if (blockData == null) { Log.Trace($"Block {blockId} download failed.", this); return(false); } //await HandleBlock(blockData, null); lock (lockitem) { if (newBlocks.TryGetValue(blockId, out var dl)) { if (dl.BlockData == null) { dl.UpdateBlockData(blockData); } } else { sync.Add(blockId, new SyncDownload(blockId).UpdateBlockData(blockData)); } } count++; } if (newBlocks.ContainsKey(nextBlockId)) { var last = chainSyncItem.Sync.NewBlocks[nextBlockId]; await HandleBlockData(last.BlockData, null); } if (count > 0) { return(await DownloadBlocks(remote, chainSyncItem)); } return(true); }
async Task HandleBlockData(BlockData blockData, ChainSyncItem chainSyncItem, Chain.Chain chain, HashSet <NodeConnection> nodeConnections) { try { if (blockData == null) { return; } var block = blockData.Block; var blockId = block.BlockId; var blockStorage = chain.BlockStorage; var lastStoredBlockId = blockStorage.LastStoredBlockId; var chainId = block.ChainId; var sync = chainSyncItem.Sync; if (blockId <= blockStorage.LastStoredBlockId) { return; } lock (sync.Lock) { if (sync.NewBlocks.Count > 0) { // remove older blocks var first = sync.NewBlocks.First().Value; while (first != null) { if (first.BlockId <= lastStoredBlockId) { sync.Remove(first.BlockId); } else { break; } if (sync.NewBlocks.Count > 0) { first = sync.NewBlocks.First().Value; } else { break; } } } } if (!_coreChain.IsBlockSignatureValid(blockData)) { return; } lock (sync.Lock) { if (sync.NewBlocks.ContainsKey(blockId)) { var data = sync.NewBlocks[blockId]; data.UpdateBlockData(blockData); var first = sync.NewBlocks.First().Value; if (first.BlockId != blockId) // if first in the list, let it pass { if (first.BlockData != null) { TaskRunner.Run(() => HandleBlockData(first.BlockData, chainSyncItem, chain, nodeConnections)); } return; } } } await sync.ConsumeLock.WaitAsync(); var result = await blockStorage.StoreBlock(blockData); if (result == BlockConsumeResult.Ok) { Log.Trace($"New block consumed {blockId}, chainid {chainId}/{block.ChainIndex} ({block.ChainType}). Last stored block id {lastStoredBlockId}.", this); _chainManager.ConsumeBlockData(blockData); } sync.ConsumeLock.Release(); if (result == BlockConsumeResult.InvalidHash) { Log.Warn($"Valid block {blockId} for chain {chainId} with invalid hash received.", this); lock (sync.Lock) { sync.Remove(blockId); return; } } nodeConnections = nodeConnections ?? new HashSet <NodeConnection>(); lock (sync.Lock) { if (result == BlockConsumeResult.Ok) { var index = sync.IndexOf(blockId); if (index >= 0) { for (var i = 0; i <= index; i++) { sync.RemoveAt(0); } } if (sync.NewBlocks.Count > 0) { var first = sync.NewBlocks.First().Value; if (first.BlockData != null) { TaskRunner.Run(() => HandleBlockData(first.BlockData, chainSyncItem, chain, nodeConnections)); } } return; } if (sync.NewBlocks.ContainsKey(blockId)) { return; } var startBlock = lastStoredBlockId + 1; var endBlock = blockId; if (sync.NewBlocks.Count == 0) { for (var i = startBlock; i <= endBlock; i++) { var download = new SyncDownload(i); if (download.BlockId == endBlock) { download.UpdateBlockData(blockData); } sync.Add(download.BlockId, download); TaskRunner.Run(() => DownloadBlock(download.BlockId, chainSyncItem, nodeConnections)); if ((i - startBlock) > 50) { break; } } } else { var first = sync.NewBlocks.First().Value; var last = sync.NewBlocks.Last().Value; var start = Math.Min(first.BlockId, startBlock); var end = Math.Max(last.BlockId, endBlock); for (var i = start; i <= end; i++) { var added = sync.NewBlocks.TryGetValue(i, out var download); if (download != null && download.TimedOut && download.BlockData == null) { TaskRunner.Run(() => DownloadBlock(download.BlockId, chainSyncItem, nodeConnections)); } download = download ?? new SyncDownload(i); if (download.BlockId == endBlock) { download.UpdateBlockData(blockData); } if (!added) { sync.Add(download.BlockId, download); } if ((!added || download.TimedOut) && download.BlockData == null) { TaskRunner.Run(() => DownloadBlock(download.BlockId, chainSyncItem, nodeConnections)); if (sync.NewBlocks.Count > 50) { break; } } } } if (sync.NewBlocks.Count > 0) { var first = sync.NewBlocks.First().Value; if (first.BlockData != null) { TaskRunner.Run(() => HandleBlockData(first.BlockData, chainSyncItem, chain, nodeConnections)); } else if (first.TimedOut) { TaskRunner.Run(() => DownloadBlock(first.BlockId, chainSyncItem, nodeConnections)); } } } } catch (Exception ex) { Log.HandleException(ex, this); } }
async Task <Dictionary <Tuple <int, uint, ChainType>, ChainSyncItem> > GetChainSyncItems() { var chains = new List <Chain.Chain> { _chainManager.CoreChain }; chains.AddRange(_chainManager.ServiceChains.Values); chains.AddRange(_chainManager.DataChains.Values); chains.AddRange(_chainManager.MaintainChains.Values); var chainSyncs = new Dictionary <Tuple <int, uint, ChainType>, ChainSyncItem>(); var remotes = new List <RemoteSyncItem>(); foreach (var chain in chains) { var chainType = chain.ChainType; var chainId = chain.ChainId; var chainIndex = chain.ChainIndex; var chainSync = new ChainSyncItem(chain); chainSyncs.Add(new Tuple <int, uint, ChainType>(chainId, chainIndex, chainType), chainSync); Chain.Chain.CreateRequiredDirectories(_storage, chain.ChainType, chainId, chainIndex, true); var endPoints = chain.AvailableEndpoints; foreach (var endPoint in endPoints) { var client = new NodeClient(endPoint.EndPoint); var nodeInfo = endPoint.NodeInfo; if (nodeInfo == null) { nodeInfo = (await client.DownloadNodeInfo(_configuration.NetworkPublicKey)).Data; } if (nodeInfo != null && !(nodeInfo.NodeId == _configuration.LocaleNodeInfo.NodeId)) { if (endPoint.NodeInfo == null) { endPoint.NodeInfo = nodeInfo; } var lastBlockInfo = (await client.DownloadLastBlockInfo(chainType, chainId, chainIndex)).Data; if (lastBlockInfo != null) { var blockSliceInfo = (await client.DownloadBlockStorageSliceInfo(chainType, chainId, chainIndex)).Data; var transactionSliceInfo = (await client.DownloadTransactionStorageSliceInfo(chainType, chainId, chainIndex)).Data; var remote = new RemoteSyncItem(client, lastBlockInfo); chainSync.AddRemote(remote); if (blockSliceInfo != null) { chainSync.UpdateLowestFoundBlockSliceId(blockSliceInfo.FirstStoredSliceId); } if (transactionSliceInfo != null) { chainSync.UpdateLowestFoundTransactionSliceId(transactionSliceInfo.FirstStoredSliceId); } remotes.Add(remote); } } } } // get latest chain info foreach (var chain in chainSyncs.Values) { var chainType = chain.ChainType; var chainId = chain.ChainId; var chainIndex = chain.ChainIndex; foreach (var remote in chain.Remotes) { var lastBlockInfo = (await remote.Client.DownloadLastBlockInfo(chainType, chainId, chainIndex)).Data; chain.UpdateLastBlockInfo(lastBlockInfo); } } return(chainSyncs); }