예제 #1
0
        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);
            }
        }
예제 #2
0
 public bool TryGetAtHeight(int height, [MaybeNullWhen(false)] out HeaderNode?headerNode)
 {
     using (GlobalLocks.ReadOnMainAsync().GetAwaiter().GetResult())
     {
         return(HeadersTree.TryGetNodeOnBestChain(height, out headerNode));
     }
 }
예제 #3
0
        /// <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));
                }
예제 #4
0
        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);
            }
        }
예제 #5
0
        /// <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));
                    }
예제 #6
0
        /// <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);
        }
예제 #7
0
        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);
        }
예제 #8
0
        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 !);
        }
예제 #9
0
 public BlockLocator?GetLocator(HeaderNode headerNode)
 {
     using Microsoft.VisualStudio.Threading.AsyncReaderWriterLock.Releaser readMainLock = GlobalLocks.ReadOnMainAsync().GetAwaiter().GetResult();
     return(HeadersTree.GetLocator(headerNode));
 }
예제 #10
0
 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));
 }
예제 #11
0
 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));
 }
예제 #12
0
        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());
        }