public HeaderNode AddToBlockIndex(BlockHeader header) { using (GlobalLocks.WriteOnMainAsync().GetAwaiter().GetResult()) { // Check for duplicate if (TryGetKnownHeaderNode(header.Hash, out HeaderNode? headerNode)) { return(headerNode); } if (!TryGetKnownHeaderNode(header.PreviousBlockHash, out HeaderNode? previousHeader)) { ThrowHelper.ThrowNotSupportedException("Previous hash not found (shouldn't happen)."); } headerNode = new HeaderNode(header, previousHeader); if (BestHeader == null || BestHeader.ChainWork < headerNode.ChainWork) { BestHeader = headerNode; } HeadersTree.Add(headerNode); _blockHeaderRepository.TryAdd(header); return(headerNode); } }
public bool TryGetAtHeight(int height, [MaybeNullWhen(false)] out HeaderNode?headerNode) { using (GlobalLocks.ReadOnMainAsync().GetAwaiter().GetResult()) { return(HeadersTree.TryGetNodeOnBestChain(height, out headerNode)); } }
/// <summary> /// The consumer that perform validation. /// </summary> /// <param name="cancellation">The cancellation.</param> private async Task ValidationWorkAsync(CancellationToken cancellation) { await foreach (HeadersToValidate request in _headersToValidate.Reader.ReadAllAsync(cancellation)) { if (request.Headers.Count == 0) { continue; //if there aren't headers to validate, ignore the request } _logger.LogDebug("Validating {HeadersCount} headers", request.Headers.Count); var newValidatedHeaderNodes = new List <HeaderNode>(); HeaderNode? lastValidatedHeaderNode = null; BlockHeader? lastValidatedBlockHeader = null; BlockValidationState?state = null; BlockHeader? invalidBlockHeader = null; /// If during validation a new header is found, this will be set to true. /// Once a new header is found, every other new header is expected to be new too /// because we don't store unconnecting headers. //bool hasNewHeaders = false; int validatedHeaders = 0; using (await GlobalLocks.WriteOnMainAsync()) { foreach (BlockHeader header in request.Headers) { using (_logger.BeginScope("Validating header {ValidationRuleType}", header !.Hash)) { if (!AcceptBlockHeaderLocked(header, out state, out HeaderNode? validatedHeaderNode, out bool newHeaderFound)) { invalidBlockHeader = header; break; } validatedHeaders++; lastValidatedBlockHeader = header; lastValidatedHeaderNode = validatedHeaderNode; if (newHeaderFound) { //hasNewHeaders = true; newValidatedHeaderNodes.Add(validatedHeaderNode); } } } } // publish events out of lock if (state !.IsInvalid()) { // signal header validation failed _eventBus.Publish(new BlockHeaderValidationFailed(invalidBlockHeader !, state, request.Peer)); }
public bool TryGetNext(HeaderNode headerNode, [MaybeNullWhen(false)] out HeaderNode nextHeaderNode) { using (GlobalLocks.ReadOnMainAsync().GetAwaiter().GetResult()) { if (IsInBestChain(headerNode) && HeadersTree.TryGetNodeOnBestChain(headerNode.Height + 1, out nextHeaderNode)) { return(true); } nextHeaderNode = null; return(false); } }
/// <summary> /// The consumer that perform validation. /// </summary> /// <param name="cancellation">The cancellation.</param> private async Task ValidationWorkAsync(CancellationToken cancellation) { await foreach (BlockToValidate request in _blocksToValidate.Reader.ReadAllAsync(cancellation)) { using (IDisposable logScope = _logger.BeginScope("Validating block {BlockHash}", request.Block.Header !.Hash)) { BlockValidationState?state = null; bool isNew = false; using (await GlobalLocks.WriteOnMainAsync()) { AcceptBlockLocked(request.Block, out state, out isNew); } // publish events out of lock if (state !.IsInvalid()) { // signal header validation failed _eventBus.Publish(new BlockValidationFailed(request.Block, state, request.Peer)); }
/// <summary> /// Processes the headers. /// It's invoked because of "headers" or "cmpctblock" message /// </summary> /// <param name="headers">The headers.</param> /// <returns></returns> private async Task <bool> ProcessHeadersAsync(BlockHeader[] headers) { int protocolVersion = PeerContext.NegotiatedProtocolVersion.Version; int headersCount = headers.Length; if (headersCount == 0) { logger.LogDebug("Peer didn't returned any headers, let's assume we reached its tip."); return(true); } using (GlobalLocks.ReadOnMainAsync().GetAwaiter().GetResult()) { if (await HandleAsNotConnectingAnnouncementAsync(headers).ConfigureAwait(false)) { // fully handled as non connecting announcement return(true); } // compute hashes in parallel to speed up the operation and check sent headers are sequential. Parallel.ForEach(headers, header => { header.Hash = _blockHeaderHashCalculator.ComputeHash(header, protocolVersion); }); } // Ensure headers are consecutive. for (int i = 1; i < headersCount; i++) { if (headers[i].PreviousBlockHash != headers[i - 1].Hash) { Misbehave(20, "Non continuous headers sequence."); return(false); } } //enqueue headers for validation await _headerValidator.RequestValidationAsync(new HeadersToValidate(headers, PeerContext)).ConfigureAwait(false); return(true); }
public HeaderNode FindForkInGlobalIndex(BlockLocator locator) { using (GlobalLocks.ReadOnMainAsync().GetAwaiter().GetResult()) { // Find the latest block common to locator and chain - we expect that // locator.vHave is sorted descending by height. foreach (UInt256?hash in locator.BlockLocatorHashes) { if (TryGetKnownHeaderNode(hash, out HeaderNode? pindex)) { if (IsInBestChain(pindex)) { return(pindex); } if (pindex.GetAncestor(ChainTip.Height) == ChainTip) { return(ChainTip); } } } } return(HeadersTree.Genesis); }
public BlockHeader GetTipHeader() { using Microsoft.VisualStudio.Threading.AsyncReaderWriterLock.Releaser readMainLock = GlobalLocks.ReadOnMainAsync().GetAwaiter().GetResult(); if (!_blockHeaderRepository.TryGet(ChainTip.Hash, out BlockHeader? header)) { ThrowHelper.ThrowBlockHeaderRepositoryException($"Unexpected error, cannot fetch the tip at height {ChainTip.Height}."); } return(header !); }
public BlockLocator?GetLocator(HeaderNode headerNode) { using Microsoft.VisualStudio.Threading.AsyncReaderWriterLock.Releaser readMainLock = GlobalLocks.ReadOnMainAsync().GetAwaiter().GetResult(); return(HeadersTree.GetLocator(headerNode)); }
public bool TryGetKnownHeaderNode(UInt256?blockHash, [MaybeNullWhen(false)] out HeaderNode node) { //using var readLock = new ReadLock(this.theLock); using Microsoft.VisualStudio.Threading.AsyncReaderWriterLock.Releaser readMainLock = GlobalLocks.ReadOnMainAsync().GetAwaiter().GetResult(); return(HeadersTree.TryGetNode(blockHash, false, out node)); }
public bool TryGetBestChainHeaderNode(UInt256 blockHash, [MaybeNullWhen(false)] out HeaderNode node) { using Microsoft.VisualStudio.Threading.AsyncReaderWriterLock.Releaser readMainLock = GlobalLocks.ReadOnMainAsync().GetAwaiter().GetResult(); return(HeadersTree.TryGetNode(blockHash, true, out node)); }
private async Task SyncLoopAsync(CancellationToken cancellationToken) { using Microsoft.VisualStudio.Threading.AsyncReaderWriterLock.Releaser readLock = GlobalLocks.ReadOnMainAsync().GetAwaiter().GetResult(); HeaderNode?bestHeaderNode = _chainState.BestHeader; if (!_chainState.TryGetBlockHeader(bestHeaderNode, out BlockHeader? bestBlockHeader)) { ThrowHelper.ThrowNotSupportedException("BestHeader should always be available, this should never happen"); } if (!_status.IsSynchronizingHeaders) { _status.IsSynchronizingHeaders = true; _status.HeadersSyncTimeout = _dateTimeProvider.GetTimeMicros() + HEADERS_DOWNLOAD_TIMEOUT_BASE + HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER * ( (_dateTimeProvider.GetAdjustedTimeAsUnixTimestamp() - bestBlockHeader.TimeStamp) / _consensusParameters.PowTargetSpacing ); /* If possible, start at the block preceding the currently * best known header. This ensures that we always get a * non-empty list of headers back as long as the peer * is up-to-date. With a non-empty response, we can initialise * the peer's known best block. This wouldn't be possible * if we requested starting at pindexBestHeader and * got back an empty response. */ HeaderNode?pindexStart = bestHeaderNode.Previous ?? bestHeaderNode; logger.LogDebug("Starting syncing headers from height {LocatorHeight} (peer starting height: {StartingHeight})", pindexStart.Height, _status.PeerStartingHeight); var newGetHeaderRequest = new GetHeadersMessage { Version = (uint)PeerContext.NegotiatedProtocolVersion.Version, BlockLocator = _chainState.GetLocator(pindexStart), HashStop = UInt256.Zero }; await SendMessageAsync(newGetHeaderRequest).ConfigureAwait(false); } CheckSyncStallingLocked(bestBlockHeader); ConsiderEviction(_dateTimeProvider.GetTime()); }