Exemple #1
0
        /// <summary>Constructs the headers from locator to consensus tip.</summary>
        /// <param name="locator">Block locator.</param>
        /// <param name="hashStop">Hash of the block after which constructing headers payload should stop.</param>
        /// <param name="lastHeader"><see cref="ChainedHeader"/> of the last header that was added to the <see cref="HeadersPayload"/>.</param>
        /// <returns><see cref="HeadersPayload"/> with headers from locator towards consensus tip or <c>null</c> in case locator was invalid.</returns>
        private HeadersPayload ConstructHeadersPayload(BlockLocator locator, uint256 hashStop, out ChainedHeader lastHeader)
        {
            ChainedHeader fork = this.chain.FindFork(locator);

            lastHeader = null;

            if (fork == null)
            {
                this.logger.LogTrace("(-)[INVALID_LOCATOR]:null");
                return(null);
            }

            var headers = new HeadersPayload();

            foreach (ChainedHeader header in this.chain.EnumerateToTip(fork).Skip(1))
            {
                lastHeader = header;
                headers.Headers.Add(header.Header);

                if ((header.HashBlock == hashStop) || (headers.Headers.Count == MaxItemsPerHeadersMessage))
                {
                    break;
                }
            }

            return(headers);
        }
Exemple #2
0
        /// <summary>Constructs the headers from locator to consensus tip.</summary>
        /// <param name="getHeadersPayload">The <see cref="GetHeadersPayload"/> payload that triggered the creation of this payload.</param>
        /// <param name="lastHeader"><see cref="ChainedHeader"/> of the last header that was added to the <see cref="HeadersPayload"/>.</param>
        /// <returns>Payload with headers from locator towards consensus tip or <c>null</c> in case locator was invalid.</returns>
        protected virtual Payload ConstructHeadersPayload(GetHeadersPayload getHeadersPayload, out ChainedHeader lastHeader)
        {
            ChainedHeader fork = this.chain.FindFork(getHeadersPayload.BlockLocator);

            lastHeader = null;

            if (fork == null)
            {
                this.logger.LogTrace("(-)[INVALID_LOCATOR]:null");
                return(null);
            }

            var headersPayload = new HeadersPayload();

            foreach (ChainedHeader header in this.chain.EnumerateToTip(fork).Skip(1))
            {
                lastHeader = header;
                headersPayload.Headers.Add(header.Header);

                if ((header.HashBlock == getHeadersPayload.HashStop) || (headersPayload.Headers.Count == MaxItemsPerHeadersMessage))
                {
                    break;
                }
            }

            this.logger.LogTrace("{0} headers were selected for sending, last one is '{1}'.", headersPayload.Headers.Count, headersPayload.Headers.LastOrDefault()?.GetHash());

            return(headersPayload);
        }
Exemple #3
0
        /// <summary>Constructs the headers from locator to consensus tip.</summary>
        /// <param name="getHeadersPayload">The <see cref="GetHeadersPayload"/> payload that triggered the creation of this payload.</param>
        /// <param name="lastHeader"><see cref="ChainedHeader"/> of the last header that was added to the <see cref="HeadersPayload"/>.</param>
        /// <returns>Payload with headers from locator towards consensus tip or <c>null</c> in case locator was invalid.</returns>
        protected virtual Payload ConstructHeadersPayload(GetHeadersPayload getHeadersPayload, out ChainedHeader lastHeader)
        {
            ChainedHeader fork = this.ChainIndexer.FindFork(getHeadersPayload.BlockLocator);

            lastHeader = null;

            if (fork == null)
            {
                this.logger.LogTrace("(-)[INVALID_LOCATOR]:null");
                return(null);
            }

            var headersPayload = new HeadersPayload();

            ChainedHeader header = this.GetLastHeaderToSend(fork, getHeadersPayload.HashStop);

            for (int heightIndex = header.Height; heightIndex > fork.Height; heightIndex--)
            {
                lastHeader = header;

                headersPayload.Headers.Add(header.Header);

                header = header.Previous;
            }

            this.logger.LogDebug("{0} headers were selected for sending, last one is '{1}'.", headersPayload.Headers.Count, headersPayload.Headers.LastOrDefault()?.GetHash());

            // We need to reverse it as it was added to the list backwards.
            headersPayload.Headers.Reverse();

            return(headersPayload);
        }
Exemple #4
0
        private void OnGetHeadersMessageReceived(GetBlocksPayload payload)
        {
            UInt256 hash = payload.HashStart;
            int     count = payload.Count <0 || payload.Count> HeadersPayload.MaxHeadersCount ? HeadersPayload.MaxHeadersCount : payload.Count;
            DataCache <UInt256, TrimmedBlock> cache = Blockchain.Singleton.Store.GetBlocks();
            TrimmedBlock state = cache.TryGet(hash);

            if (state == null)
            {
                return;
            }
            List <Header> headers = new List <Header>();

            for (uint i = 1; i <= count; i++)
            {
                uint index = state.Index + i;
                hash = Blockchain.Singleton.GetBlockHash(index);
                if (hash == null)
                {
                    break;
                }
                Header header = cache.TryGet(hash)?.Header;
                if (header == null)
                {
                    break;
                }
                headers.Add(header);
            }
            if (headers.Count == 0)
            {
                return;
            }
            Context.Parent.Tell(Message.Create(MessageCommand.Headers, HeadersPayload.Create(headers)));
        }
Exemple #5
0
        /// <summary>Validates the headers payload.</summary>
        /// <param name="peer">The peer who sent the payload.</param>
        /// <param name="headersPayload">Headers payload to validate.</param>
        /// <param name="validationError">The validation error that is set in case <c>false</c> is returned.</param>
        /// <returns><c>true</c> if payload was valid, <c>false</c> otherwise.</returns>
        private bool ValidateHeadersPayload(INetworkPeer peer, HeadersPayload headersPayload, out string validationError)
        {
            validationError = null;

            if (headersPayload.Headers.Count > MaxItemsPerHeadersMessage)
            {
                this.logger.LogDebug("Headers payload with {0} headers was received. Protocol violation. Banning the peer.", headersPayload.Headers.Count);

                validationError = "Protocol violation.";

                this.logger.LogTrace("(-)[TOO_MANY_HEADERS]:false");
                return(false);
            }

            // Check headers for consecutiveness.
            for (int i = 1; i < headersPayload.Headers.Count; i++)
            {
                if (headersPayload.Headers[i].HashPrevBlock != headersPayload.Headers[i - 1].GetHash())
                {
                    this.logger.LogDebug("Peer '{0}' presented non-consecutiveness hashes at position {1} with prev hash '{2}' not matching hash '{3}'.",
                                         peer.RemoteSocketEndpoint, i, headersPayload.Headers[i].HashPrevBlock, headersPayload.Headers[i - 1].GetHash());

                    validationError = "Peer presented nonconsecutive headers.";

                    this.logger.LogTrace("(-)[NONCONSECUTIVE]:false");
                    return(false);
                }
            }

            return(true);
        }
Exemple #6
0
        private void OnGetHeadersMessageReceived(GetBlocksPayload payload)
        {
            if (!localNode.ServiceEnabled)
            {
                return;
            }
            if (Blockchain.Default == null)
            {
                return;
            }
            UInt256 hash = payload.HashStart.Select(p => Blockchain.Default.GetHeader(p)).Where(p => p != null).OrderBy(p => p.Index).Select(p => p.Hash).FirstOrDefault();

            if (hash == null || hash == payload.HashStop)
            {
                return;
            }
            List <Header> headers = new List <Header>();

            do
            {
                hash = Blockchain.Default.GetNextBlockHash(hash);
                if (hash == null)
                {
                    break;
                }
                headers.Add(Blockchain.Default.GetHeader(hash));
            } while (hash != payload.HashStop && headers.Count < 2000);
            EnqueueMessage("headers", HeadersPayload.Create(headers));
        }
Exemple #7
0
        /// <summary>
        /// Processes "getheaders" message received from the peer.
        /// </summary>
        /// <param name="peer">Peer from which the message was received.</param>
        /// <param name="getHeadersPayload">Payload of "getheaders" message to process.</param>
        /// <remarks>
        /// "getheaders" message is sent by the peer in response to "headers" message until an empty array is received.
        /// <para>
        /// This payload notifies peers of our current best validated height, which is held by consensus tip.
        /// </para>
        /// <para>
        /// If the peer is behind/equal to our best height an empty array is sent back.
        /// </para>
        /// </remarks>
        private async Task ProcessGetHeadersAsync(INetworkPeer peer, GetHeadersPayload getHeadersPayload)
        {
            this.logger.LogTrace("({0}:'{1}',{2}:'{3}')", nameof(peer), peer.RemoteSocketEndpoint, nameof(getHeadersPayload), getHeadersPayload);

            // Ignoring "getheaders" from peers because node is in initial block download unless the peer is whitelisted.
            // We don't want to reveal our position in IBD which can be used by attacker. Also we don't won't to deliver peers any blocks
            // because that will slow down our own syncing process.
            if (this.initialBlockDownloadState.IsInitialBlockDownload() && !peer.Behavior <IConnectionManagerBehavior>().Whitelisted)
            {
                this.logger.LogTrace("(-)[IGNORE_ON_IBD]");
                return;
            }

            HeadersPayload headersPayload = this.ConstructHeadersPayload(getHeadersPayload.BlockLocator, getHeadersPayload.HashStop, out ChainedHeader lastHeader);

            if (headersPayload != null)
            {
                this.logger.LogTrace("{0} headers were selected for sending, last one is '{1}'.", headersPayload.Headers.Count, headersPayload.Headers.LastOrDefault()?.GetHash());

                try
                {
                    this.BestSentHeader = lastHeader;

                    await peer.SendMessageAsync(headersPayload).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    this.logger.LogTrace("Unable to send headers message to peer '{0}'.", peer.RemoteSocketEndpoint);
                }
            }

            this.logger.LogTrace("(-)");
        }
Exemple #8
0
        private async Task OnGetHeadersMessageReceivedAsync(GetBlocksPayload payload)
        {
            if (!localNode.ServiceEnabled)
            {
                return;
            }
            if (Blockchain.Default == null)
            {
                return;
            }
            if (!Blockchain.Default.Ability.HasFlag(BlockchainAbility.BlockIndexes))
            {
                return;
            }
            UInt256 hash = payload.HashStart.Select(p => Blockchain.Default.GetHeader(p)).Where(p => p != null).OrderBy(p => p.Height).Select(p => p.Hash).FirstOrDefault();

            if (hash == null || hash == payload.HashStop)
            {
                return;
            }
            List <Block> headers = new List <Block>();

            do
            {
                hash = Blockchain.Default.GetNextBlockHash(hash);
                if (hash == null)
                {
                    break;
                }
                headers.Add(Blockchain.Default.GetHeader(hash));
            } while (hash != payload.HashStop && headers.Count < 2000);
            await SendMessageAsync("headers", HeadersPayload.Create(headers));
        }
        /// <summary>
        /// Processes "getheaders" message received from the peer.
        /// </summary>
        /// <param name="peer">Peer from which the message was received.</param>
        /// <param name="getHeadersPayload">Payload of "getheaders" message to process.</param>
        /// <remarks>
        /// "getheaders" message is sent by the peer in response to "inv(block)" message
        /// after the connection is established, or in response to "headers" message
        /// until an empty array is returned.
        /// <para>
        /// This payload notifies peers of our current best validated height,
        /// which is held by consensus tip (not concurrent chain tip).
        /// </para>
        /// <para>
        /// If the peer is behind/equal to our best height an empty array is sent back.
        /// </para>
        /// </remarks>
        private async Task ProcessGetHeadersAsync(NetworkPeer peer, GetHeadersPayload getHeadersPayload)
        {
            this.logger.LogTrace("({0}:'{1}',{2}:'{3}')", nameof(peer), peer.RemoteSocketEndpoint, nameof(getHeadersPayload), getHeadersPayload);

            if (!this.CanRespondToGetHeaders)
            {
                this.logger.LogTrace("(-)[CANT_RESPOND_TO_HEADERS]");
                return;
            }

            // Ignoring "getheaders" from peers because node is in initial block download unless the peer is whitelisted.
            if (this.initialBlockDownloadState.IsInitialBlockDownload() && !peer.Behavior <ConnectionManagerBehavior>().Whitelisted)
            {
                this.logger.LogTrace("(-)[IGNORE_ON_IBD]");
                return;
            }

            HeadersPayload headers      = new HeadersPayload();
            ChainedBlock   consensusTip = this.chainState.ConsensusTip;

            consensusTip = this.Chain.GetBlock(consensusTip.HashBlock);

            ChainedBlock fork = this.Chain.FindFork(getHeadersPayload.BlockLocators);

            if (fork != null)
            {
                if ((consensusTip == null) || (fork.Height > consensusTip.Height))
                {
                    // Fork not yet validated.
                    fork = null;
                }

                if (fork != null)
                {
                    foreach (ChainedBlock header in this.Chain.EnumerateToTip(fork).Skip(1))
                    {
                        if (header.Height > consensusTip.Height)
                        {
                            break;
                        }

                        headers.Headers.Add(header.Header);
                        if ((header.HashBlock == getHeadersPayload.HashStop) || (headers.Headers.Count == 2000))
                        {
                            break;
                        }
                    }
                }
            }

            // Set our view of peer's tip equal to the last header that was sent to it.
            if (headers.Headers.Count != 0)
            {
                this.pendingTip = this.Chain.GetBlock(headers.Headers.Last().GetHash()) ?? this.pendingTip;
            }

            await peer.SendMessageAsync(headers).ConfigureAwait(false);

            this.logger.LogTrace("(-)");
        }
Exemple #10
0
 private void OnHeadersMessageReceived(HeadersPayload payload)
 {
     if (payload.Headers.Length == 0)
     {
         return;
     }
     system.Blockchain.Tell(payload.Headers, Context.Parent);
 }
Exemple #11
0
        public void ConstructorTest()
        {
            var hds = new BlockHeader[1];
            var pl  = new HeadersPayload(hds);

            Assert.Same(hds, pl.Headers);
            Assert.Equal(PayloadType.Headers, pl.PayloadType);
        }
Exemple #12
0
        private void RespondToHeadersPayload(Node node, HeadersPayload headersPayload)
        {
            var message = this.CreateGetDataPayload(headersPayload.Headers.Select(item => item.GetHash()));

            if (message.Inventory.Any())
            {
                node.SendMessage(message);
            }
        }
Exemple #13
0
        void NewNodeMessage(IncomingMessage message)
        {
            if (message.Message.Payload is VerAckPayload)
            {
                _Nodes.Add(message.Node);
            }
            if (message.Message.Payload is InvPayload)
            {
                InvPayload invPayload = (InvPayload)message.Message.Payload;
                message.Node.SendMessage(new GetDataPayload(invPayload.Inventory.ToArray()));
            }
            if (message.Message.Payload is TxPayload)
            {
                TxPayload txPayload = (TxPayload)message.Message.Payload;
                _ReceivedTransactions.AddOrUpdate(txPayload.Object.GetHash(), txPayload.Object, (k, v) => v);
            }
            if (message.Message.Payload is GetHeadersPayload)
            {
                var headers  = (GetHeadersPayload)message.Message.Payload;
                var fork     = _Server.ChainBuilder.Chain.FindFork(headers.BlockLocators);
                var response =
                    _Server.ChainBuilder.Chain
                    .ToEnumerable(true)
                    .TakeWhile(f => f.HashBlock != fork.HashBlock && f.HashBlock != headers.HashStop)
                    .Select(f => f.Header)
                    .ToArray();
                HeadersPayload res = new HeadersPayload();
                res.Headers.AddRange(response);
                message.Node.SendMessage(res);
            }

            if (message.Message.Payload is GetDataPayload)
            {
                Transaction tx;
                Block       block;
                var         getData = message.Message.Payload as GetDataPayload;
                foreach (var inv in getData.Inventory)
                {
                    if (inv.Type == InventoryType.MSG_TX)
                    {
                        if (_Transactions.TryGetValue(inv.Hash, out tx))
                        {
                            message.Node.SendMessage(new TxPayload(tx));
                        }
                    }
                    if (inv.Type == InventoryType.MSG_BLOCK)
                    {
                        if (_Blocks.TryGetValue(inv.Hash, out block))
                        {
                            message.Node.SendMessage(new BlockPayload(block));
                        }
                    }
                }
            }
        }
Exemple #14
0
        private void OnHeadersMessageReceived(Message msg)
        {
            HeadersPayload payload = msg.GetPayload <HeadersPayload>();

            if (payload.Headers.Length == 0)
            {
                return;
            }
            system.Blockchain.Tell(payload.Headers, Context.Parent);
            system.TaskManager.Tell(new TaskManager.HeaderMessageReceived(), Context.Parent);
        }
Exemple #15
0
        public void DeserializeAndSerialize()
        {
            var header = new Header();

            TestUtils.SetupHeaderWithValues(header, UInt256.Zero, out _, out _, out _, out _, out _, out _);
            var test  = HeadersPayload.Create(header);
            var clone = test.ToArray().AsSerializable <HeadersPayload>();

            Assert.AreEqual(test.Headers.Length, clone.Headers.Length);
            Assert.AreEqual(test.Headers[0], clone.Headers[0]);
        }
Exemple #16
0
        public void DeserializeAndSerialize()
        {
            var header = new Header();

            TestUtils.SetupHeaderWithValues(header, UInt256.Zero, out UInt256 merkRoot, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal);
            var test  = HeadersPayload.Create(header);
            var clone = test.ToArray().AsSerializable <HeadersPayload>();

            Assert.AreEqual(test.Headers.Length, clone.Headers.Length);
            Assert.AreEqual(test.Headers[0], clone.Headers[0]);
        }
Exemple #17
0
        public void Size_Get()
        {
            var header = new Header();

            TestUtils.SetupHeaderWithValues(header, UInt256.Zero, out UInt256 merkRoot, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal);

            var test = HeadersPayload.Create();

            test.Size.Should().Be(1);
            test = HeadersPayload.Create(header);
            test.Size.Should().Be(1 + header.Size);
        }
Exemple #18
0
        public void SerializeTest()
        {
            var pl     = new HeadersPayload(new BlockHeader[] { BlockHeaderTests.GetSampleBlockHeader() });
            var stream = new FastStream(Constants.BlockHeaderSize + 2);

            pl.Serialize(stream);

            byte[] hd       = BlockHeaderTests.GetSampleBlockHeaderBytes();
            byte[] expected = new byte[Constants.BlockHeaderSize + 2];
            expected[0] = 1;
            Buffer.BlockCopy(hd, 0, expected, 1, Constants.BlockHeaderSize);
            expected[^ 1] = 0;
Exemple #19
0
        public void Size_Get()
        {
            var header = new Header();

            TestUtils.SetupHeaderWithValues(header, UInt256.Zero, out _, out _, out _, out _, out _, out _);

            var test = HeadersPayload.Create();

            test.Size.Should().Be(1);
            test = HeadersPayload.Create(header);
            test.Size.Should().Be(1 + header.Size);
        }
Exemple #20
0
 private async Task OnHeadersMessageReceivedAsync(HeadersPayload payload)
 {
     if (Blockchain.Default == null)
     {
         return;
     }
     Blockchain.Default.AddHeaders(payload.Headers);
     if (Blockchain.Default.HeaderHeight < Version.StartHeight)
     {
         await SendMessageAsync("getheaders", GetBlocksPayload.Create(Blockchain.Default.GetLeafHeaderHashes()));
     }
 }
Exemple #21
0
 private void OnHeadersMessageReceived(HeadersPayload payload)
 {
     if (Blockchain.Default == null)
     {
         return;
     }
     Blockchain.Default.AddHeaders(payload.Headers);
     if (Blockchain.Default.HeaderHeight < Version.StartHeight)
     {
         EnqueueMessage("getheaders", GetBlocksPayload.Create(Blockchain.Default.CurrentHeaderHash), true);
     }
 }
        public static void Process(BaseChain chain, Node node, IncomingMessage message)
        {
            if (message.Message.Command != "headers")
            {
                return;
            }

            NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();

            HeadersPayload payload = (HeadersPayload)message.Message.Payload;

            _logger.Debug("Processing Headers Message: {0}", payload.Headers.ToString());
        }
Exemple #23
0
        private void OnGetHeadersMessageReceived(Message msg)
        {
            GetBlocksPayload payload = msg.GetPayload <GetBlocksPayload>();

            UInt256 hash = payload.HashStart[0];

            if (hash == payload.HashStop)
            {
                return;
            }
            DataCache <UInt256, BlockState> cache = blockchain.Store.GetBlocks();
            BlockState state = cache.TryGet(hash);

            if (state == null)
            {
                return;
            }
            List <Header> headers = new List <Header>();

            for (uint i = 1; i <= HeadersPayload.MaxHeadersCount; i++)
            {
                uint index = state.TrimmedBlock.Index + i;
                hash = blockchain.GetBlockHash(index);
                if (hash == null)
                {
                    break;
                }
                if (hash == payload.HashStop)
                {
                    break;
                }
                Header header = cache.TryGet(hash)?.TrimmedBlock.Header;
                if (header == null)
                {
                    break;
                }
                headers.Add(header);
            }
            blockchain.Log($"OnGetHeaders, blockIndex:{state.TrimmedBlock.Index}, count:{headers.Count}, [{remoteNode.Remote.Address}]");
            if (headers.Count == 0)
            {
                return;
            }
            Context.Parent.Tell(Message.Create(MessageType.Headers, HeadersPayload.Create(headers)));
        }
Exemple #24
0
        private void OnGetHeadersMessageReceived(GetBlocksPayload payload)
        {
            UInt256 hash = payload.HashStart[0];

            if (hash == payload.HashStop)
            {
                return;
            }
            DataCache <UInt256, BlockState> cache = blockchain.Store.GetBlocks();
            BlockState state = cache.TryGet(hash);

            if (state == null)
            {
                return;
            }
            List <Header> headers = new List <Header>();

            for (uint i = 1; i <= HeadersPayload.MaxHeadersCount; i++)
            {
                uint index = state.TrimmedBlock.Index + i;
                hash = blockchain.GetBlockHash(index);
                if (hash == null)
                {
                    break;
                }
                Header header = cache.TryGet(hash)?.TrimmedBlock.Header;
                if (header == null)
                {
                    break;
                }
                headers.Add(header);
            }
            if (headers.Count == 0)
            {
                return;
            }
            Context.Parent.Tell(Message.Create("headers", HeadersPayload.Create(headers)));
        }
Exemple #25
0
		void Intercept(IncomingMessage message, Action act)
		{
			var inv = message.Message.Payload as InvPayload;
			if(inv != null)
			{
				if(inv.Inventory.Any(i => ((i.Type & InventoryType.MSG_BLOCK) != 0) && !Chain.Contains(i.Hash)))
				{
					_Refresh.Dispose(); //No need of periodical refresh, the peer is notifying us
					if(AutoSync)
						TrySync();
				}
			}

			var getheaders = message.Message.Payload as GetHeadersPayload;
			if(getheaders != null && CanRespondToGetHeaders)
			{
				HeadersPayload headers = new HeadersPayload();
				var fork = Chain.FindFork(getheaders.BlockLocators);
				if(fork != null)
					foreach(var header in Chain.EnumerateToTip(fork).Skip(1))
					{
						headers.Headers.Add(header.Header);
						if(header.HashBlock == getheaders.HashStop || headers.Headers.Count == 2000)
							break;
					}
				AttachedNode.SendMessageAsync(headers);
			}

			var newheaders = message.Message.Payload as HeadersPayload;
			var pendingTipBefore = GetPendingTip();
			if(newheaders != null && CanSync)
			{
				var tip = GetPendingTip();
				foreach(var header in newheaders.Headers)
				{
					var prev = tip.FindAncestorOrSelf(header.HashPrevBlock);
					if(prev == null)
						break;
					tip = new ChainedBlock(header, header.GetHash(), prev);
					if(!AttachedNode.IsTrusted)
					{
						var validated = Chain.GetBlock(tip.HashBlock) != null || tip.Validate(AttachedNode.Network);
						if(!validated)
						{
							invalidHeaderReceived = true;
							break;
						}
					}
					_PendingTip = tip;
				}
				if(_PendingTip.Height > Chain.Tip.Height)
				{
					Chain.SetTip(_PendingTip);
				}

				var chainedPendingTip = Chain.GetBlock(_PendingTip.HashBlock);
				if(chainedPendingTip != null)
				{
					_PendingTip = chainedPendingTip; //This allows garbage collection to collect the duplicated pendingtip and ancestors
				}
				if(newheaders.Headers.Count != 0 && pendingTipBefore.HashBlock != GetPendingTip().HashBlock)
					TrySync();
				Interlocked.Decrement(ref _SynchingCount);
			}

			act();
		}
Exemple #26
0
        /// <summary>
        /// Processes "headers" message received from the peer.
        /// </summary>
        /// <param name="peer">Peer from which the message was received.</param>
        /// <param name="headersPayload">Payload of "headers" message to process.</param>
        /// <remarks>
        /// "headers" message is sent in response to "getheaders" message or it is solicited
        /// by the peer when a new block is validated (unless in IBD).
        /// <para>
        /// When we receive "headers" message from the peer, we can adjust our knowledge
        /// of the peer's view of the chain. We update its pending tip, which represents
        /// the tip of the best chain we think the peer has.
        /// </para>
        /// </remarks>
        private async Task ProcessHeadersAsync(INetworkPeer peer, HeadersPayload headersPayload)
        {
            List <BlockHeader> headers = headersPayload.Headers;

            if (headers.Count == 0)
            {
                this.logger.LogTrace("Headers payload with no headers was received. Assuming we're synced with the peer.");
                this.logger.LogTrace("(-)[NO_HEADERS]");
                return;
            }

            if (!this.ValidateHeadersPayload(peer, headersPayload, out string validationError))
            {
                this.peerBanning.BanAndDisconnectPeer(peer.PeerEndPoint, validationError);

                this.logger.LogTrace("(-)[VALIDATION_FAILED]");
                return;
            }

            using (await this.asyncLock.LockAsync().ConfigureAwait(false))
            {
                if (this.cachedHeaders.Count > CacheSyncHeadersThreshold) // TODO when proven headers are implemented combine this with size threshold of N mb.
                {
                    // Ignore this message because cache is full.
                    this.logger.LogTrace("(-)[CACHE_IS_FULL]");
                    return;
                }

                // If queue is not empty, add to queue instead of calling CM.
                if (this.cachedHeaders.Count != 0)
                {
                    this.cachedHeaders.AddRange(headers);

                    this.logger.LogTrace("{0} headers were added to cache, new cache size is {1}.", headers.Count, this.cachedHeaders.Count);
                    this.logger.LogTrace("(-)[CACHED]");
                    return;
                }

                ConnectNewHeadersResult result = await this.PresentHeadersLockedAsync(headers).ConfigureAwait(false);

                if (result == null)
                {
                    this.logger.LogTrace("Processing of {0} headers failed.", headers.Count);
                    this.logger.LogTrace("(-)[PROCESSING_FAILED]");
                    return;
                }

                this.ExpectedPeerTip = result.Consumed;
                this.UpdateBestSentHeader(this.ExpectedPeerTip);

                if (result.Consumed.HashBlock != headers.Last().GetHash())
                {
                    // Some headers were not consumed, add to cache.
                    int consumedCount = headers.IndexOf(result.Consumed.Header) + 1;
                    this.cachedHeaders.AddRange(headers.Skip(consumedCount));

                    this.logger.LogTrace("{0} out of {1} items were not consumed and added to cache.", headers.Count - consumedCount, headers.Count);
                }

                if (this.cachedHeaders.Count == 0)
                {
                    await this.ResyncAsync().ConfigureAwait(false);
                }
            }
        }
        void Intercept(IncomingMessage message, Action act)
        {
            var inv = message.Message.Payload as InvPayload;

            if (inv != null)
            {
                if (inv.Inventory.Any(i => ((i.Type & InventoryType.MSG_BLOCK) != 0) && !Chain.Contains(i.Hash)))
                {
                    _Refresh.Dispose();                     //No need of periodical refresh, the peer is notifying us
                    if (AutoSync)
                    {
                        TrySync();
                    }
                }
            }

            var getheaders = message.Message.Payload as GetHeadersPayload;

            if (getheaders != null && CanRespondToGetHeaders && !StripHeader)
            {
                HeadersPayload headers    = new HeadersPayload();
                var            highestPow = SharedState.HighestValidatedPoW;
                highestPow = highestPow == null ? null : Chain.GetBlock(highestPow.HashBlock);
                var fork = Chain.FindFork(getheaders.BlockLocators);
                if (fork != null)
                {
                    if (highestPow != null && fork.Height > highestPow.Height)
                    {
                        fork = null;                         //fork not yet validated
                    }
                    if (fork != null)
                    {
                        foreach (var header in Chain.EnumerateToTip(fork).Skip(1))
                        {
                            if (highestPow != null && header.Height > highestPow.Height)
                            {
                                break;
                            }
                            headers.Headers.Add(header.Header);
                            if (header.HashBlock == getheaders.HashStop || headers.Headers.Count == 2000)
                            {
                                break;
                            }
                        }
                    }
                }
                AttachedNode.SendMessageAsync(headers);
            }

            var newheaders       = message.Message.Payload as HeadersPayload;
            var pendingTipBefore = GetPendingTipOrChainTip();

            if (newheaders != null && CanSync)
            {
                var tip = GetPendingTipOrChainTip();
                foreach (var header in newheaders.Headers)
                {
                    var prev = tip.FindAncestorOrSelf(header.HashPrevBlock);
                    if (prev == null)
                    {
                        break;
                    }
                    tip = new ChainedBlock(header, header.GetHash(), prev);
                    var validated = Chain.GetBlock(tip.HashBlock) != null || (SkipPoWCheck || tip.Validate(AttachedNode.Network));
                    validated &= !SharedState.IsMarkedInvalid(tip.HashBlock);
                    if (!validated)
                    {
                        invalidHeaderReceived = true;
                        break;
                    }
                    _PendingTip = tip;
                }

                bool isHigherBlock = false;
                if (SkipPoWCheck)
                {
                    isHigherBlock = _PendingTip.Height > Chain.Tip.Height;
                }
                else
                {
                    isHigherBlock = _PendingTip.GetChainWork(true) > Chain.Tip.GetChainWork(true);
                }

                if (isHigherBlock)
                {
                    Chain.SetTip(_PendingTip);
                    if (StripHeader)
                    {
                        _PendingTip.StripHeader();
                    }
                }

                var chainedPendingTip = Chain.GetBlock(_PendingTip.HashBlock);
                if (chainedPendingTip != null)
                {
                    _PendingTip = chainedPendingTip;                     //This allows garbage collection to collect the duplicated pendingtip and ancestors
                }
                if (newheaders.Headers.Count != 0 && pendingTipBefore.HashBlock != GetPendingTipOrChainTip().HashBlock)
                {
                    TrySync();
                }
                Interlocked.Decrement(ref _SynchingCount);
            }

            act();
        }
Exemple #28
0
        void Intercept(IncomingMessage message, Action act)
        {
            var inv = message.Message.Payload as InvPayload;

            if (inv != null)
            {
                if (inv.Inventory.Any(i => ((i.Type & InventoryType.MSG_BLOCK) != 0) && !Chain.Contains(i.Hash)))
                {
                    _Refresh.Dispose();                     //No need of periodical refresh, the peer is notifying us
                    if (AutoSync)
                    {
                        TrySync();
                    }
                }
            }

            // == GetHeadersPayload ==
            // represents our height from the peer's point of view
            // it is sent from the peer on first connect, in response to  Inv(Block)
            // or in response to HeaderPayload until an empty array is returned
            // this payload notifies peers of our current best validated height
            // use the ChainState.HighestValidatedPoW property (not Chain.Tip)
            // if the peer is behind/equal to our best height an empty array is sent back

            // Ignoring getheaders from peers because node is in initial block download
            var getheaders = message.Message.Payload as GetHeadersPayload;

            if (getheaders != null && CanRespondToGetHeaders &&
                (!this.SharedState.IsInitialBlockDownload ||
                 this.AttachedNode.Behavior <ConnectionManagerBehavior>().Whitelisted))               // if not in IBD whitelisted won't be checked
            {
                HeadersPayload headers    = new HeadersPayload();
                var            highestPow = SharedState.HighestValidatedPoW;
                highestPow = Chain.GetBlock(highestPow.HashBlock);
                var fork = Chain.FindFork(getheaders.BlockLocators);

                if (fork != null)
                {
                    if (highestPow == null || fork.Height > highestPow.Height)
                    {
                        fork = null;                         //fork not yet validated
                    }
                    if (fork != null)
                    {
                        foreach (var header in Chain.EnumerateToTip(fork).Skip(1))
                        {
                            if (header.Height > highestPow.Height)
                            {
                                break;
                            }
                            headers.Headers.Add(header.Header);
                            if (header.HashBlock == getheaders.HashStop || headers.Headers.Count == 2000)
                            {
                                break;
                            }
                        }
                    }
                }
                AttachedNode.SendMessageAsync(headers);
            }

            // == HeadersPayload ==
            // represents the peers height from our point view
            // this updates the pending tip parameter which is the
            // peers current best validated height
            // if the peer's height is higher Chain.Tip is updated to have
            // the most PoW header
            // is sent in response to GetHeadersPayload or is solicited by the
            // peer when a new block is validated (and not in IBD)

            var newheaders       = message.Message.Payload as HeadersPayload;
            var pendingTipBefore = GetPendingTipOrChainTip();

            if (newheaders != null && CanSync)
            {
                // TODO: implement MAX_HEADERS_RESULTS in NBitcoin.HeadersPayload

                var tip = GetPendingTipOrChainTip();
                foreach (var header in newheaders.Headers)
                {
                    var prev = tip.FindAncestorOrSelf(header.HashPrevBlock);
                    if (prev == null)
                    {
                        break;
                    }
                    tip = new ChainedBlock(header, header.GetHash(), prev);
                    var validated = Chain.GetBlock(tip.HashBlock) != null || tip.Validate(AttachedNode.Network);
                    validated &= !SharedState.IsMarkedInvalid(tip.HashBlock);
                    if (!validated)
                    {
                        invalidHeaderReceived = true;
                        break;
                    }
                    _PendingTip = tip;
                }

                if (_PendingTip.ChainWork > Chain.Tip.ChainWork)
                {
                    Chain.SetTip(_PendingTip);
                }

                var chainedPendingTip = Chain.GetBlock(_PendingTip.HashBlock);
                if (chainedPendingTip != null)
                {
                    _PendingTip = chainedPendingTip;                     //This allows garbage collection to collect the duplicated pendingtip and ancestors
                }

                if (newheaders.Headers.Count != 0 && pendingTipBefore.HashBlock != GetPendingTipOrChainTip().HashBlock)
                {
                    TrySync();
                }

                Interlocked.Decrement(ref _SynchingCount);
            }

            act();
        }
Exemple #29
0
        public IEnumerable <ChainedHeader> GetHeadersFromFork(INetworkPeer peer, ChainedHeader currentTip, uint256 hashStop = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            this.AssertStateAsync(peer, NetworkPeerState.HandShaked, cancellationToken).GetAwaiter().GetResult();

            using (var listener = new NetworkPeerListener(peer))
            {
                int acceptMaxReorgDepth = 0;
                while (true)
                {
                    // Get before last so, at the end, we should only receive 1 header equals to this one (so we will not have race problems with concurrent GetChains).
                    BlockLocator awaited = currentTip.Previous == null?currentTip.GetLocator() : currentTip.Previous.GetLocator();

                    peer.SendMessageAsync(new GetHeadersPayload()
                    {
                        BlockLocator = awaited,
                        HashStop     = hashStop
                    }, cancellationToken).GetAwaiter().GetResult();

                    while (true)
                    {
                        bool           isOurs  = false;
                        HeadersPayload headers = null;

                        using (CancellationTokenSource headersCancel = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
                        {
                            headersCancel.CancelAfter(TimeSpan.FromMinutes(1.0));
                            try
                            {
                                headers = listener.ReceivePayloadAsync <HeadersPayload>(headersCancel.Token).GetAwaiter().GetResult();
                            }
                            catch (OperationCanceledException)
                            {
                                acceptMaxReorgDepth += 6;
                                if (cancellationToken.IsCancellationRequested)
                                {
                                    throw;
                                }

                                // Send a new GetHeaders.
                                break;
                            }
                        }

                        // In the special case where the remote node is at height 0 as well as us, then the headers count will be 0.
                        if ((headers.Headers.Count == 0) && (peer.PeerVersion.StartHeight == 0) && (currentTip.HashBlock == peer.Network.GenesisHash))
                        {
                            yield break;
                        }

                        if ((headers.Headers.Count == 1) && (headers.Headers[0].GetHash() == currentTip.HashBlock))
                        {
                            yield break;
                        }

                        foreach (BlockHeader header in headers.Headers)
                        {
                            uint256 hash = header.GetHash();
                            if (hash == currentTip.HashBlock)
                            {
                                continue;
                            }

                            // The previous headers request timeout, this can arrive in case of big reorg.
                            if (header.HashPrevBlock != currentTip.HashBlock)
                            {
                                int           reorgDepth     = 0;
                                ChainedHeader tempCurrentTip = currentTip;
                                while (reorgDepth != acceptMaxReorgDepth && tempCurrentTip != null && header.HashPrevBlock != tempCurrentTip.HashBlock)
                                {
                                    reorgDepth++;
                                    tempCurrentTip = tempCurrentTip.Previous;
                                }

                                if (reorgDepth != acceptMaxReorgDepth && tempCurrentTip != null)
                                {
                                    currentTip = tempCurrentTip;
                                }
                            }

                            if (header.HashPrevBlock == currentTip.HashBlock)
                            {
                                isOurs     = true;
                                currentTip = new ChainedHeader(header, hash, currentTip);

                                yield return(currentTip);

                                if (currentTip.HashBlock == hashStop)
                                {
                                    yield break;
                                }
                            }
                            else
                            {
                                break;  // Not our headers, continue receive.
                            }
                        }

                        if (isOurs)
                        {
                            break;  //Go ask for next header.
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Processes "headers" message received from the peer.
        /// </summary>
        /// <param name="peer">Peer from which the message was received.</param>
        /// <param name="headersPayload">Payload of "headers" message to process.</param>
        /// <remarks>
        /// "headers" message is sent in response to "getheaders" message or it is solicited
        /// by the peer when a new block is validated (unless in IBD).
        /// <para>
        /// When we receive "headers" message from the peer, we can adjust our knowledge
        /// of the peer's view of the chain. We update its pending tip, which represents
        /// the tip of the best chain we think the peer has.
        /// </para>
        /// <para>
        /// If we receive a valid header from peer which work is higher than the work
        /// of our best chain's tip, we update our view of the best chain to that tip.
        /// </para>
        /// </remarks>
        private async Task ProcessHeadersAsync(NetworkPeer peer, HeadersPayload headersPayload)
        {
            this.logger.LogTrace("({0}:'{1}',{2}:'{3}')", nameof(peer), peer.RemoteSocketEndpoint, nameof(headersPayload), headersPayload);

            if (!this.CanSync)
            {
                this.logger.LogTrace("(-)[CANT_SYNC]");
                return;
            }

            if (headersPayload.Headers.Count == 0)
            {
                this.logger.LogTrace("Headers payload with no headers was received. Assuming we're synced with the peer.");
                this.logger.LogTrace("(-)[NO_HEADERS]");
                return;
            }

            ChainedBlock pendingTipBefore = this.pendingTip;

            this.logger.LogTrace("Pending tip is '{0}', received {1} new headers.", pendingTipBefore, headersPayload.Headers.Count);

            bool doTrySync = false;

            // TODO: implement MAX_HEADERS_RESULTS in NBitcoin.HeadersPayload

            ChainedBlock tip = pendingTipBefore;

            foreach (BlockHeader header in headersPayload.Headers)
            {
                ChainedBlock prev = tip?.FindAncestorOrSelf(header.HashPrevBlock);
                if (prev == null)
                {
                    this.logger.LogTrace("Previous header of the new header '{0}' was not found on the peer's chain, the view of the peer's chain is probably outdated.", header);

                    // We have received a header from the peer for which we don't register a previous header.
                    // This can happen if our information about where the peer is is invalid.
                    // However, if the previous header is on the chain that we recognize,
                    // we can fix it.

                    // Try to find the header's previous hash on our best chain.
                    prev = this.Chain.GetBlock(header.HashPrevBlock);

                    if (prev == null)
                    {
                        this.logger.LogTrace("Previous header of the new header '{0}' was not found on our chain either.", header);

                        // If we can't connect the header we received from the peer, we might be on completely different chain or
                        // a reorg happened recently. If we ignored it, we would have invalid view of the peer and the propagation
                        // of blocks would not work well. So we ask the peer for headers using "getheaders" message.
                        // Enforce a sync.
                        doTrySync = true;
                        break;
                    }

                    // Now we know the previous block header and thus we can connect the new header.
                }

                tip = new ChainedBlock(header, header.GetHash(peer.Network.NetworkOptions), prev);
                bool validated = this.Chain.GetBlock(tip.HashBlock) != null || tip.Validate(peer.Network);
                validated &= !this.chainState.IsMarkedInvalid(tip.HashBlock);
                if (!validated)
                {
                    this.logger.LogTrace("Validation of new header '{0}' failed.", tip);
                    this.InvalidHeaderReceived = true;
                    break;
                }

                this.pendingTip = tip;
            }

            if (pendingTipBefore != this.pendingTip)
            {
                this.logger.LogTrace("Pending tip changed to '{0}'.", this.pendingTip);
            }

            if ((this.pendingTip != null) && (this.pendingTip.ChainWork > this.Chain.Tip.ChainWork))
            {
                // Long reorganization protection on POS networks.
                bool reorgPrevented = false;
                uint maxReorgLength = this.chainState.MaxReorgLength;
                if (maxReorgLength != 0)
                {
                    Network      network      = peer.Network;
                    ChainedBlock consensusTip = this.chainState.ConsensusTip;
                    if ((network != null) && (consensusTip != null))
                    {
                        ChainedBlock fork = this.pendingTip.FindFork(consensusTip);
                        if ((fork != null) && (fork != consensusTip))
                        {
                            int reorgLength = consensusTip.Height - fork.Height;
                            if (reorgLength > maxReorgLength)
                            {
                                this.logger.LogTrace("Reorganization of length {0} prevented, maximal reorganization length is {1}, consensus tip is '{2}'.", reorgLength, maxReorgLength, consensusTip);
                                this.InvalidHeaderReceived = true;
                                reorgPrevented             = true;
                            }
                            else
                            {
                                this.logger.LogTrace("Reorganization of length {0} accepted, consensus tip is '{1}'.", reorgLength, consensusTip);
                            }
                        }
                    }
                }

                // Switch to better chain.
                if (!reorgPrevented)
                {
                    this.logger.LogTrace("New chain tip '{0}' selected, chain work is '{1}'.", this.pendingTip, this.pendingTip.ChainWork);
                    this.Chain.SetTip(this.pendingTip);
                }
            }

            ChainedBlock chainedPendingTip = this.pendingTip == null ? null : this.Chain.GetBlock(this.pendingTip.HashBlock);

            if (chainedPendingTip != null)
            {
                // This allows garbage collection to collect the duplicated pendingTip and ancestors.
                this.pendingTip = chainedPendingTip;
            }

            // If we made any advancement or the sync is enforced by 'doTrySync'- continue syncing.
            if (doTrySync || (this.pendingTip == null) || (pendingTipBefore == null) || (pendingTipBefore.HashBlock != this.pendingTip.HashBlock))
            {
                await this.TrySyncAsync().ConfigureAwait(false);
            }

            this.logger.LogTrace("(-)");
        }
        private void AttachedPeer_MessageReceived(NetworkPeer peer, IncomingMessage message)
        {
            this.logger.LogTrace("({0}:'{1}',{2}:'{3}')", nameof(peer), peer.RemoteSocketEndpoint, nameof(message), message.Message.Command);

            switch (message.Message.Payload)
            {
            case InvPayload inv:
            {
                if (inv.Inventory.Any(i => ((i.Type & InventoryType.MSG_BLOCK) != 0) && !this.Chain.Contains(i.Hash)))
                {
                    // No need of periodical refresh, the peer is notifying us.
                    this.refreshTimer.Dispose();
                    if (this.AutoSync)
                    {
                        this.TrySync();
                    }
                }
                break;
            }

            case GetHeadersPayload getHeaders:
            {
                // Represents our height from the peer's point of view.
                // It is sent from the peer on first connect, in response to Inv(Block)
                // or in response to HeaderPayload until an empty array is returned.
                // This payload notifies peers of our current best validated height.
                // Use the ChainState.ConsensusTip property (not Chain.Tip)
                // if the peer is behind/equal to our best height an empty array is sent back.

                if (!this.CanRespondToGetHeaders)
                {
                    break;
                }

                // Ignoring "getheaders" from peers because node is in initial block download.
                // If not in IBD whitelisted won't be checked.
                if (this.initialBlockDownloadState.IsInitialBlockDownload() && !peer.Behavior <ConnectionManagerBehavior>().Whitelisted)
                {
                    break;
                }

                HeadersPayload headers      = new HeadersPayload();
                ChainedBlock   consensusTip = this.chainState.ConsensusTip;
                consensusTip = this.Chain.GetBlock(consensusTip.HashBlock);

                ChainedBlock fork = this.Chain.FindFork(getHeaders.BlockLocators);
                if (fork != null)
                {
                    if ((consensusTip == null) || (fork.Height > consensusTip.Height))
                    {
                        // Fork not yet validated.
                        fork = null;
                    }

                    if (fork != null)
                    {
                        foreach (ChainedBlock header in this.Chain.EnumerateToTip(fork).Skip(1))
                        {
                            if (header.Height > consensusTip.Height)
                            {
                                break;
                            }

                            headers.Headers.Add(header.Header);
                            if ((header.HashBlock == getHeaders.HashStop) || (headers.Headers.Count == 2000))
                            {
                                break;
                            }
                        }
                    }
                }

                peer.SendMessageVoidAsync(headers);
                break;
            }

            case HeadersPayload newHeaders:
            {
                // Represents the peers height from our point view.
                // This updates the pending tip parameter which is
                // the peers current best validated height.
                // If the peer's height is higher Chain.Tip is updated to have
                // the most PoW header.
                // It is sent in response to GetHeadersPayload or is solicited by the
                // peer when a new block is validated (and not in IBD).

                if (!this.CanSync)
                {
                    break;
                }

                ChainedBlock pendingTipBefore = this.GetPendingTipOrChainTip();
                this.logger.LogTrace("Pending tip is '{0}', received {1} new headers.", pendingTipBefore, newHeaders.Headers.Count);

                // TODO: implement MAX_HEADERS_RESULTS in NBitcoin.HeadersPayload

                ChainedBlock tip = pendingTipBefore;
                foreach (BlockHeader header in newHeaders.Headers)
                {
                    ChainedBlock prev = tip.FindAncestorOrSelf(header.HashPrevBlock);
                    if (prev == null)
                    {
                        this.logger.LogTrace("Previous header of the new header '{0}' was not found on the peer's chain, the view of the peer's chain is probably outdated.", header);

                        // We have received a header from the peer for which we don't register a previous header.
                        // This can happen if our information about where the peer is is invalid.
                        // However, if the previous header is on the chain that we recognize,
                        // we can fix it.

                        // Try to find the header's previous hash on our best chain.
                        prev = this.Chain.GetBlock(header.HashPrevBlock);

                        if (prev == null)
                        {
                            this.logger.LogTrace("Previous header of the new header '{0}' was not found on our chain either.", header);

                            // If we can't connect the header we received from the peer, we might be on completely different chain or
                            // a reorg happened recently. If we ignored it, we would have invalid view of the peer and the propagation
                            // of blocks would not work well. So we ask the peer for headers using "getheaders" message.
                            var getHeadersPayload = new GetHeadersPayload()
                            {
                                BlockLocators = pendingTipBefore.GetLocator(),
                                HashStop      = null
                            };

                            peer.SendMessageVoidAsync(getHeadersPayload);
                            break;
                        }

                        // Now we know the previous block header and thus we can connect the new header.
                    }

                    tip = new ChainedBlock(header, header.GetHash(peer.Network.NetworkOptions), prev);
                    bool validated = this.Chain.GetBlock(tip.HashBlock) != null || tip.Validate(peer.Network);
                    validated &= !this.chainState.IsMarkedInvalid(tip.HashBlock);
                    if (!validated)
                    {
                        this.logger.LogTrace("Validation of new header '{0}' failed.", tip);
                        this.InvalidHeaderReceived = true;
                        break;
                    }

                    this.pendingTip = tip;
                }

                if (pendingTipBefore != this.pendingTip)
                {
                    this.logger.LogTrace("Pending tip changed to '{0}'.", this.pendingTip);
                }

                if (this.pendingTip.ChainWork > this.Chain.Tip.ChainWork)
                {
                    // Long reorganization protection on POS networks.
                    bool reorgPrevented = false;
                    uint maxReorgLength = this.chainState.MaxReorgLength;
                    if (maxReorgLength != 0)
                    {
                        Network      network      = peer.Network;
                        ChainedBlock consensusTip = this.chainState.ConsensusTip;
                        if ((network != null) && (consensusTip != null))
                        {
                            ChainedBlock fork = this.pendingTip.FindFork(consensusTip);
                            if ((fork != null) && (fork != consensusTip))
                            {
                                int reorgLength = consensusTip.Height - fork.Height;
                                if (reorgLength > maxReorgLength)
                                {
                                    this.logger.LogTrace("Reorganization of length {0} prevented, maximal reorganization length is {1}, consensus tip is '{2}'.", reorgLength, maxReorgLength, consensusTip);
                                    this.InvalidHeaderReceived = true;
                                    reorgPrevented             = true;
                                }
                                else
                                {
                                    this.logger.LogTrace("Reorganization of length {0} accepted, consensus tip is '{1}'.", reorgLength, consensusTip);
                                }
                            }
                        }
                    }

                    // Switch to better chain.
                    if (!reorgPrevented)
                    {
                        this.logger.LogTrace("New chain tip '{0}' selected, chain work is '{1}'.", this.pendingTip, this.pendingTip.ChainWork);
                        this.Chain.SetTip(this.pendingTip);
                    }
                }

                ChainedBlock chainedPendingTip = this.Chain.GetBlock(this.pendingTip.HashBlock);
                if (chainedPendingTip != null)
                {
                    // This allows garbage collection to collect the duplicated pendingTip and ancestors.
                    this.pendingTip = chainedPendingTip;
                }

                if ((!this.InvalidHeaderReceived) && (newHeaders.Headers.Count != 0) && (pendingTipBefore.HashBlock != this.GetPendingTipOrChainTip().HashBlock))
                {
                    this.TrySync();
                }

                break;
            }
            }

            this.logger.LogTrace("(-)");
        }