private bool IsDemandNeeded( BlockChain <T> blockChain, Func <IBlockExcerpt, bool> predicate, BlockDemand demand) { IComparer <IBlockExcerpt> canonComparer = blockChain.Policy.CanonicalChainComparer; BlockDemand?oldDemand = _blockDemands.ContainsKey(demand.Peer) ? _blockDemands[demand.Peer] : (BlockDemand?)null; bool needed; if (IsDemandStale(demand)) { needed = false; } else if (predicate(demand)) { if (oldDemand is { } old) { needed = IsDemandStale(old) || canonComparer.Compare(old, demand) < 0; } else { needed = true; } }
public void Add( BlockChain <T> blockChain, Func <IBlockExcerpt, bool> predicate, BlockDemand demand) { if (IsDemandNeeded(blockChain, predicate, demand)) { _blockDemands[demand.Peer] = demand; Log.Debug( "BlockDemand #{Index} {BlockHash} from peer {Peer} added.", demand.Index, demand.Hash, demand.Peer); } else { Log.Debug( "BlockDemand #{Index} {BlockHash} from peer {Peer} ignored.", demand.Index, demand.Hash, demand.Peer); } }
private async Task ProcessBlockHeader( BlockHeaderMessage message, CancellationToken cancellationToken = default(CancellationToken)) { if (!(message.Remote is BoundPeer peer)) { _logger.Information( "BlockHeaderMessage was sent from invalid peer " + "{PeerAddress}; ignored.", message.Remote.Address ); return; } if (!message.GenesisHash.Equals(BlockChain.Genesis.Hash)) { _logger.Information( "BlockHeaderMessage was sent from the peer " + "{PeerAddress} with different genesis block {hash}; ignored.", message.Remote.Address, message.GenesisHash ); return; } BlockHeaderReceived.Set(); BlockHeader header = message.Header; _logger.Debug( $"Received a {nameof(BlockHeader)} #{{BlockIndex}} {{BlockHash}}.", header.Index, ByteUtil.Hex(header.Hash) ); try { header.Validate(DateTimeOffset.UtcNow); } catch (InvalidBlockException ibe) { _logger.Information( ibe, "A received header #{BlockIndex} {BlockHash} seems invalid; ignored.", header.Index, ByteUtil.Hex(header.Hash) ); return; } using (await _blockSyncMutex.LockAsync(cancellationToken)) { if (IsDemandNeeded(header, peer)) { _logger.Debug( "BlockDemand #{index} {blockHash} from {peer}.", header.Index, ByteUtil.Hex(header.Hash), peer); BlockDemand = new BlockDemand(header, peer, DateTimeOffset.UtcNow); } else { _logger.Debug( "No blocks are required " + "(current: {Current}, demand: {Demand}, received: {Received});" + $" {nameof(BlockHeaderMessage)} is ignored.", BlockChain.Tip.Index, BlockDemand?.Header.Index, header.Index); } } }
private async Task ProcessFillBlocks( TimeSpan timeout, CancellationToken cancellationToken ) { var sessionRandom = new Random(); IComparer <BlockPerception> canonComparer = BlockChain.Policy.CanonicalChainComparer; while (!cancellationToken.IsCancellationRequested) { int sessionId = sessionRandom.Next(); if (!(BlockDemand is { } blockDemand)) { await Task.Delay(1, cancellationToken); continue; } BoundPeer peer = blockDemand.Peer; try { if (canonComparer.Compare( BlockChain.PerceiveBlock(BlockDemand?.Header), BlockChain.PerceiveBlock(BlockChain.Tip) ) <= 0) { using (await _blockSyncMutex.LockAsync(cancellationToken)) { BlockDemand = null; continue; } } var hash = new BlockHash(blockDemand.Header.Hash); const string startLogMsg = "{SessionId}: Got a new " + nameof(BlockDemand) + " from {Peer}; started " + "to fetch the block #{BlockIndex} {BlockHash}..."; _logger.Debug(startLogMsg, sessionId, peer, blockDemand.Header.Index, hash); await SyncPreviousBlocksAsync( blockChain : BlockChain, peer : peer, stop : hash, progress : null, timeout : timeout, totalBlockCount : 0, logSessionId : sessionId, cancellationToken : cancellationToken ); _logger.Debug( "{SessionId}: Synced block(s) from {Peer}; broadcast them to neighbors...", sessionId, peer ); // FIXME: Clean up events BlockReceived.Set(); BlockAppended.Set(); BroadcastBlock(peer.Address, BlockChain.Tip); ProcessFillBlocksFinished.Set(); } catch (TimeoutException) { const string msg = "{SessionId}: Timeout occurred during " + nameof(ProcessFillBlocks) + "() from {Peer}."; _logger.Debug(msg, sessionId, peer); } catch (InvalidBlockIndexException ibie) { const string msg = "{SessionId}: " + nameof(InvalidBlockIndexException) + " occurred during " + nameof(ProcessFillBlocks) + "() from {Peer}: {Exception}"; _logger.Warning(ibie, msg, sessionId, peer, ibie); } catch (Exception e) { const string msg = "{SessionId}: Unexpected exception occurred during " + nameof(ProcessFillBlocks) + "() from {Peer}: {Exception}"; _logger.Error(e, msg, sessionId, peer, e); } finally { using (await _blockSyncMutex.LockAsync(cancellationToken)) { const string msg = "{SessionId}: " + nameof(ProcessFillBlocks) + "() finished."; _logger.Debug(msg, sessionId); if (BlockDemand.Equals(blockDemand)) { const string resetMsg = "{SessionId}: Reset " + nameof(BlockDemand) + "..."; _logger.Debug(resetMsg, sessionId); BlockDemand = null; } } } } }