Example #1
0
        /// <summary>Distributes download job's headers to peers that can provide blocks represented by those headers.</summary>
        /// <remarks>
        /// If some of the blocks from the job can't be provided by any peer those headers will be added to a <param name="failedHashes"></param>.
        /// <para>
        /// Have to be locked by <see cref="queueLock"/>.
        /// </para>
        /// <para>
        /// Node's quality score is being considered as a weight during the random distribution of the hashes to download among the nodes.
        /// </para>
        /// </remarks>
        /// <param name="downloadJob">Download job to be partially of fully consumed.</param>
        /// <param name="failedHashes">List of failed hashes which will be extended in case there is no peer to claim required hash.</param>
        /// <param name="emptySlots">Number of empty slots. This is the maximum number of assignments that can be created.</param>
        /// <returns>List of downloads that were distributed between the peers.</returns>
        private List <AssignedDownload> DistributeHeadersLocked(DownloadJob downloadJob, List <uint256> failedHashes, int emptySlots)
        {
            var newAssignments = new List <AssignedDownload>();

            HashSet <IBlockPullerBehavior> peerBehaviors;

            lock (this.peerLock)
            {
                peerBehaviors = new HashSet <IBlockPullerBehavior>(this.pullerBehaviorsByPeerId.Values);
            }

            bool jobFailed = false;

            if (peerBehaviors.Count == 0)
            {
                this.logger.LogDebug("There are no peers that can participate in download job distribution! Job ID {0} failed.", downloadJob.Id);
                jobFailed = true;
            }

            int lastSucceededIndex = -1;

            for (int index = 0; (index < downloadJob.Headers.Count) && (index < emptySlots) && !jobFailed; index++)
            {
                ChainedHeader header = downloadJob.Headers[index];

                while (!jobFailed)
                {
                    // Weighted random selection based on the peer's quality score.
                    double sumOfQualityScores = peerBehaviors.Sum(x => x.QualityScore);
                    double scoreToReachPeer   = this.random.NextDouble() * sumOfQualityScores;

                    IBlockPullerBehavior selectedBehavior = peerBehaviors.First();

                    foreach (IBlockPullerBehavior peerBehavior in peerBehaviors)
                    {
                        if (peerBehavior.QualityScore >= scoreToReachPeer)
                        {
                            selectedBehavior = peerBehavior;
                            break;
                        }

                        scoreToReachPeer -= peerBehavior.QualityScore;
                    }

                    INetworkPeer attachedPeer = selectedBehavior.AttachedPeer;

                    // Behavior's tip can't be null because we only have behaviors inserted in the behaviors structure after the tip is set.
                    if ((attachedPeer != null) && (selectedBehavior.Tip.FindAncestorOrSelf(header) != null))
                    {
                        int peerId = attachedPeer.Connection.Id;

                        // Assign to this peer.
                        newAssignments.Add(new AssignedDownload()
                        {
                            PeerId       = peerId,
                            JobId        = downloadJob.Id,
                            AssignedTime = this.dateTimeProvider.GetUtcNow(),
                            Header       = header
                        });

                        lastSucceededIndex = index;

                        this.logger.LogTrace("Block '{0}' was assigned to peer ID {1}.", header.HashBlock, peerId);
                        break;
                    }
                    else
                    {
                        // Peer doesn't claim this header.
                        peerBehaviors.Remove(selectedBehavior);

                        if (peerBehaviors.Count != 0)
                        {
                            continue;
                        }

                        jobFailed = true;
                        this.logger.LogDebug("Job {0} failed because there is no peer claiming header '{1}'.", downloadJob.Id, header);
                    }
                }
            }

            if (!jobFailed)
            {
                downloadJob.Headers.RemoveRange(0, lastSucceededIndex + 1);
            }
            else
            {
                int removeFrom = (lastSucceededIndex == -1) ? 0 : lastSucceededIndex + 1;

                IEnumerable <uint256> failed = downloadJob.Headers.GetRange(removeFrom, downloadJob.Headers.Count - removeFrom).Select(x => x.HashBlock);
                failedHashes.AddRange(failed);

                downloadJob.Headers.Clear();
            }

            return(newAssignments);
        }
Example #2
0
 /// <summary>Handles peer's disconnection.</summary>
 /// <param name="peer">Peer which disposal should be safely handled.</param>
 public void OnPeerDisconnectedHandler(INetworkPeer peer)
 {
     this.peersToDispose.Enqueue(peer);
 }
        public void CheckBlocksAnnounced_AndQueueEmptiesOverTime_ForMultiplePeers_WhenOneIsDisconnected()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                CoreNode stratisNodeSync = builder.CreateStratisPowNode(this.network).WithReadyBlockchainData(ReadyBlockchain.BitcoinRegTest10Miner).Start();

                CoreNode stratisNode1 = builder.CreateStratisPowNode(this.network).Start();
                CoreNode stratisNode2 = builder.CreateStratisPowNode(this.network).Start();
                CoreNode stratisNode3 = builder.CreateStratisPowNode(this.network).Start();

                // Change the other nodes' lists of default behaviours include the test behaviour in it.
                // We leave the other behaviors alone for this test because we want to see what messages the node gets under normal operation.
                IConnectionManager node1ConnectionManager = stratisNode1.FullNode.NodeService <IConnectionManager>();
                node1ConnectionManager.Parameters.TemplateBehaviors.Add(new TestBehavior());

                IConnectionManager node2ConnectionManager = stratisNode2.FullNode.NodeService <IConnectionManager>();
                node2ConnectionManager.Parameters.TemplateBehaviors.Add(new TestBehavior());

                // Connect other nodes to initial node.
                TestHelper.Connect(stratisNode1, stratisNodeSync);
                TestHelper.Connect(stratisNode2, stratisNodeSync);
                TestHelper.Connect(stratisNode3, stratisNodeSync);

                // Make node3 unable to respond to anything, effectively disconnecting it.
                IConnectionManager node3ConnectionManager = stratisNode3.FullNode.NodeService <IConnectionManager>();
                node3ConnectionManager.Parameters.TemplateBehaviors.Clear();
                node3ConnectionManager.Parameters.TemplateBehaviors.Add(new TestBehavior());

                INetworkPeer connectedPeer1 = node1ConnectionManager.ConnectedPeers.FindByEndpoint(stratisNodeSync.Endpoint);
                TestBehavior testBehavior1  = connectedPeer1.Behavior <TestBehavior>();

                INetworkPeer connectedPeer2 = node2ConnectionManager.ConnectedPeers.FindByEndpoint(stratisNodeSync.Endpoint);
                TestBehavior testBehavior2  = connectedPeer2.Behavior <TestBehavior>();

                INetworkPeer connectedPeer3 = node3ConnectionManager.ConnectedPeers.FindByEndpoint(stratisNodeSync.Endpoint);
                TestBehavior testBehavior3  = connectedPeer3.Behavior <TestBehavior>();

                // If the announce queue is not getting stalled, the other 2 nodes should sync properly.
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisNode1, stratisNodeSync));
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisNode2, stratisNodeSync));

                HashSet <uint256> advertised = new HashSet <uint256>();

                // Check to see that all blocks got advertised to node1 via the "headers" payload.
                foreach (IncomingMessage message in testBehavior1.receivedMessageTracker["headers"])
                {
                    if (message.Message.Payload is HeadersPayload)
                    {
                        foreach (BlockHeader header in ((HeadersPayload)message.Message.Payload).Headers)
                        {
                            advertised.Add(header.GetHash());
                        }
                    }
                }

                foreach (ChainedHeader chainedHeader in stratisNodeSync.FullNode.Chain.EnumerateToTip(this.network.GenesisHash))
                {
                    if ((!advertised.Contains(chainedHeader.HashBlock)) && (!(chainedHeader.HashBlock == this.network.GenesisHash)))
                    {
                        throw new Exception($"An expected block was not advertised to peer 1: {chainedHeader.HashBlock}");
                    }
                }

                advertised.Clear();

                // Check to see that all blocks got advertised to node1 via the "headers" payload.
                foreach (IncomingMessage message in testBehavior2.receivedMessageTracker["headers"])
                {
                    if (message.Message.Payload is HeadersPayload)
                    {
                        foreach (BlockHeader header in ((HeadersPayload)message.Message.Payload).Headers)
                        {
                            advertised.Add(header.GetHash());
                        }
                    }
                }

                foreach (ChainedHeader chainedHeader in stratisNodeSync.FullNode.Chain.EnumerateToTip(this.network.GenesisHash))
                {
                    if ((!advertised.Contains(chainedHeader.HashBlock)) && (!(chainedHeader.HashBlock == this.network.GenesisHash)))
                    {
                        throw new Exception($"An expected block was not advertised to peer 2: {chainedHeader.HashBlock}");
                    }
                }

                // Check current state of announce queue.
                BlockStoreSignaled blockStoreSignaled = stratisNodeSync.FullNode.NodeService <BlockStoreSignaled>();

                AsyncQueue <ChainedHeader> blocksToAnnounce = (AsyncQueue <ChainedHeader>)blockStoreSignaled.GetMemberValue("blocksToAnnounce");
                Queue <ChainedHeader>      queueItems       = (Queue <ChainedHeader>)blocksToAnnounce.GetMemberValue("items");

                // It should still eventually empty despite not being able to communicate with node3.
                TestHelper.WaitLoop(() => queueItems.Count == 0);
            }
        }
        /// <summary>Presents the headers to <see cref="ConsensusManager"/> and handles exceptions if any.</summary>
        /// <remarks>Have to be locked by <see cref="asyncLock"/>.</remarks>
        /// <param name="headers">List of headers that the peer presented.</param>
        /// <param name="triggerDownload">Specifies if the download should be scheduled for interesting blocks.</param>
        protected async Task <ConnectNewHeadersResult> PresentHeadersLockedAsync(List <BlockHeader> headers, bool triggerDownload = true)
        {
            ConnectNewHeadersResult result = null;

            INetworkPeer peer = this.AttachedPeer;

            if (peer == null)
            {
                this.logger.LogDebug("Peer detached!");
                this.logger.LogTrace("(-)[PEER_DETACHED]:null");
                return(null);
            }

            try
            {
                result = this.consensusManager.HeadersPresented(peer, headers, triggerDownload);
            }
            catch (ConnectHeaderException)
            {
                // This is typically thrown when the first header refers to a previous block hash that is not in
                // the header tree currently. This is not regarded as bannable, as it could be a legitimate reorg.
                this.logger.LogDebug("Unable to connect headers.");

                if (this.CheckIfUnsolicitedFutureHeader(headers))
                {
                    // However, during IBD it is much more likely that the unconnectable header is an unsolicited
                    // block advertisement. In that case we just ignore the failed header and don't modify the cache.
                    // TODO: Review more closely what Bitcoin Core does here - they seem to allow 8 unconnectable headers before
                    // applying partial DoS points to a peer. Incorporate that into rate limiting code?
                    this.logger.LogTrace("(-)[HEADER_FUTURE_CANT_CONNECT_2]");
                }
                else
                {
                    // Resync in case we can't connect the header.
                    this.cachedHeaders.Clear();
                    await this.ResyncAsync().ConfigureAwait(false);
                }
            }
            catch (ConsensusRuleException exception)
            {
                this.logger.LogDebug("Peer's header is invalid. Peer will be banned and disconnected. Error: {0}.", exception.ConsensusError);
                this.peerBanning.BanAndDisconnectPeer(peer.PeerEndPoint, $"Peer presented invalid header, error: {exception.ConsensusError}.");
            }
            catch (HeaderInvalidException)
            {
                this.logger.LogDebug("Peer's header is invalid. Peer will be banned and disconnected.");
                this.peerBanning.BanAndDisconnectPeer(peer.PeerEndPoint, $"Peer presented invalid header.");
            }
            catch (CheckpointMismatchException)
            {
                this.logger.LogDebug("Peer's headers violated a checkpoint. Peer will be banned and disconnected.");
                this.peerBanning.BanAndDisconnectPeer(peer.PeerEndPoint, "Peer presented header that violates a checkpoint.");
            }
            catch (MaxReorgViolationException)
            {
                this.logger.LogDebug("Peer violates max reorg. Peer will be banned and disconnected.");
                this.peerBanning.BanAndDisconnectPeer(peer.PeerEndPoint, "Peer violates max reorg rule.");
            }

            return(result);
        }
Example #5
0
 public static bool IsWhitelisted(this INetworkPeer peer)
 {
     return(peer.Behavior <IConnectionManagerBehavior>()?.Whitelisted == true);
 }
Example #6
0
 /// <summary>
 /// Callback that is called before the peer is disposed.
 /// </summary>
 /// <param name="peer">Peer that is being disposed.</param>
 private void OnPeerDisposed(INetworkPeer peer)
 {
     this.RemovePeer(peer);
 }
Example #7
0
        /// <summary>
        /// See <see cref="DiscoverPeers"/>.
        /// </summary>
        private async Task DiscoverPeersAsync()
        {
            var peersToDiscover           = new List <IPEndPoint>();
            List <PeerAddress> foundPeers = this.peerAddressManager.PeerSelector.SelectPeersForDiscovery(1000).ToList();

            peersToDiscover.AddRange(foundPeers.Select(p => p.Endpoint));

            if (peersToDiscover.Count == 0)
            {
                // On normal circumstances the dns seeds are attempted only once per node lifetime.
                if (this.isSeedAndDnsAttempted)
                {
                    this.logger.LogTrace("(-)[DNS_ATTEMPTED]");
                    return;
                }

                this.AddDNSSeedNodes(peersToDiscover);
                this.AddSeedNodes(peersToDiscover);
                this.isSeedAndDnsAttempted = true;

                if (peersToDiscover.Count == 0)
                {
                    this.logger.LogTrace("(-)[NO_ADDRESSES]");
                    return;
                }

                peersToDiscover = peersToDiscover.OrderBy(a => RandomUtils.GetInt32()).ToList();
            }
            else
            {
                // If all attempts have failed then attempt the dns seeds again.
                if (!this.isSeedAndDnsAttempted && foundPeers.All(peer => peer.Attempted))
                {
                    peersToDiscover.Clear();
                    this.AddDNSSeedNodes(peersToDiscover);
                    this.AddSeedNodes(peersToDiscover);
                    this.isSeedAndDnsAttempted = true;
                }
            }

            await peersToDiscover.ForEachAsync(5, this.nodeLifetime.ApplicationStopping, async (endPoint, cancellation) =>
            {
                using (CancellationTokenSource connectTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellation))
                {
                    this.logger.LogTrace("Attempting to discover from : '{0}'", endPoint);

                    connectTokenSource.CancelAfter(TimeSpan.FromSeconds(5));

                    INetworkPeer networkPeer = null;

                    try
                    {
                        NetworkPeerConnectionParameters clonedParameters = this.currentParameters.Clone();
                        clonedParameters.ConnectCancellation             = connectTokenSource.Token;

                        PeerAddressManagerBehaviour addressManagerBehaviour = clonedParameters.TemplateBehaviors.OfType <PeerAddressManagerBehaviour>().FirstOrDefault();
                        clonedParameters.TemplateBehaviors.Clear();
                        clonedParameters.TemplateBehaviors.Add(addressManagerBehaviour);

                        networkPeer = await this.networkPeerFactory.CreateConnectedNetworkPeerAsync(endPoint, clonedParameters).ConfigureAwait(false);
                        await networkPeer.VersionHandshakeAsync(connectTokenSource.Token).ConfigureAwait(false);
                        await networkPeer.SendMessageAsync(new GetAddrPayload(), connectTokenSource.Token).ConfigureAwait(false);

                        this.peerAddressManager.PeerDiscoveredFrom(endPoint, DateTimeProvider.Default.GetUtcNow());

                        connectTokenSource.Token.WaitHandle.WaitOne(TimeSpan.FromSeconds(5));
                    }
                    catch
                    {
                    }
                    finally
                    {
                        networkPeer?.Disconnect("Discovery job done");
                        networkPeer?.Dispose();
                    }

                    this.logger.LogTrace("Discovery from '{0}' finished", endPoint);
                }
            }).ConfigureAwait(false);
        }
        /// <inheritdoc />
        public async Task AnnounceBlocksAsync(List <ChainedHeader> blocksToAnnounce)
        {
            Guard.NotNull(blocksToAnnounce, nameof(blocksToAnnounce));

            if (!blocksToAnnounce.Any())
            {
                this.logger.LogTrace("(-)[NO_BLOCKS]");
                return;
            }

            INetworkPeer peer = this.AttachedPeer;

            if (peer == null)
            {
                this.logger.LogTrace("(-)[NO_PEER]");
                return;
            }

            bool revertToInv = ((!this.PreferHeaders && (!this.preferHeaderAndIDs || blocksToAnnounce.Count > 1)) || blocksToAnnounce.Count > MaxBlocksToAnnounce);

            this.logger.LogTrace("Block propagation preferences of the peer '{0}': prefer headers - {1}, prefer headers and IDs - {2}, will{3} revert to 'inv' now.", peer.RemoteSocketEndpoint, this.PreferHeaders, this.preferHeaderAndIDs, revertToInv ? "" : " NOT");

            var headers = new List <BlockHeader>();
            var inventoryBlockToSend = new List <ChainedHeader>();

            try
            {
                ChainedHeader bestSentHeader = this.consensusManagerBehavior.BestSentHeader;

                ChainedHeader bestIndex = null;
                if (!revertToInv)
                {
                    bool foundStartingHeader = false;

                    // In case we don't have any information about peer's tip send him only last header and don't update best sent header.
                    // We expect peer to answer with getheaders message.
                    if (bestSentHeader == null)
                    {
                        await peer.SendMessageAsync(this.BuildAnnouncedHeaderPayload(this.chainState.BlockStoreTip.Height, blocksToAnnounce.Last().Header)).ConfigureAwait(false);

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

                    // Try to find first chained block that the peer doesn't have, and then add all chained blocks past that one.
                    foreach (ChainedHeader chainedHeader in blocksToAnnounce)
                    {
                        bestIndex = chainedHeader;

                        if (!foundStartingHeader)
                        {
                            this.logger.LogTrace("Checking is the peer '{0}' can connect header '{1}'.", peer.RemoteSocketEndpoint, chainedHeader);

                            // Peer doesn't have a block at the height of our block and with the same hash?
                            if (bestSentHeader?.FindAncestorOrSelf(chainedHeader) != null)
                            {
                                this.logger.LogTrace("Peer '{0}' already has header '{1}'.", peer.RemoteSocketEndpoint, chainedHeader.Previous);
                                continue;
                            }

                            // Peer doesn't have a block at the height of our block.Previous and with the same hash?
                            if (bestSentHeader?.FindAncestorOrSelf(chainedHeader.Previous) == null)
                            {
                                // Peer doesn't have this header or the prior one - nothing will connect, so bail out.
                                this.logger.LogTrace("Neither the header nor its previous header found for peer '{0}', reverting to 'inv'.", peer.RemoteSocketEndpoint);
                                revertToInv = true;
                                break;
                            }

                            this.logger.LogTrace("Peer '{0}' can connect header '{1}'.", peer.RemoteSocketEndpoint, chainedHeader.Previous);
                            foundStartingHeader = true;
                        }

                        // If we reached here then it means that we've found starting header.
                        headers.Add(chainedHeader.Header);
                    }
                }

                if (!revertToInv && headers.Any())
                {
                    if ((headers.Count == 1) && this.preferHeaderAndIDs)
                    {
                        // TODO:
                    }
                    else if (this.PreferHeaders)
                    {
                        if (headers.Count > 1)
                        {
                            this.logger.LogDebug("Sending {0} headers, range {1} - {2}, to peer '{3}'.", headers.Count, headers.First(), headers.Last(), peer.RemoteSocketEndpoint);
                        }
                        else
                        {
                            this.logger.LogDebug("Sending header '{0}' to peer '{1}'.", headers.First(), peer.RemoteSocketEndpoint);
                        }

                        this.lastSentHeader = bestIndex;
                        this.consensusManagerBehavior.UpdateBestSentHeader(this.lastSentHeader);

                        await peer.SendMessageAsync(this.BuildAnnouncedHeaderPayload(this.chainState.BlockStoreTip.Height, headers.ToArray())).ConfigureAwait(false);

                        this.logger.LogTrace("(-)[SEND_HEADERS_PAYLOAD]");
                        return;
                    }
                    else
                    {
                        revertToInv = true;
                    }
                }

                if (revertToInv)
                {
                    // If falling back to using an inv, just try to inv the tip.
                    // The last entry in 'blocksToAnnounce' was our tip at some point in the past.
                    if (blocksToAnnounce.Any())
                    {
                        ChainedHeader chainedHeader = blocksToAnnounce.Last();
                        if (chainedHeader != null)
                        {
                            if ((bestSentHeader == null) || (bestSentHeader.GetAncestor(chainedHeader.Height) == null))
                            {
                                inventoryBlockToSend.Add(chainedHeader);
                                this.logger.LogDebug("Sending inventory hash '{0}' to peer '{1}'.", chainedHeader.HashBlock, peer.RemoteSocketEndpoint);
                            }
                        }
                    }
                }

                if (inventoryBlockToSend.Any())
                {
                    this.lastSentHeader = inventoryBlockToSend.Last();
                    this.consensusManagerBehavior.UpdateBestSentHeader(this.lastSentHeader);

                    await this.SendAsBlockInventoryAsync(peer, inventoryBlockToSend).ConfigureAwait(false);

                    this.logger.LogTrace("(-)[SEND_INVENTORY]");
                    return;
                }
            }
            catch (OperationCanceledException)
            {
                this.logger.LogTrace("(-)[CANCELED_EXCEPTION]");
                return;
            }
        }
Example #9
0
 public INetworkConnection(INetworkPeer peer)
 {
     this.ParentPeer           = peer;
     this.GameObjectController = null;
 }
        /// <summary>
        /// Processes "getblocks" message received from the peer.
        /// </summary>
        /// <param name="peer">Peer that sent the message.</param>
        /// <param name="getBlocksPayload">Payload of "getblocks" message to process.</param>
        private async Task ProcessGetBlocksAsync(INetworkPeer peer, GetBlocksPayload getBlocksPayload)
        {
            if (getBlocksPayload.BlockLocators.Blocks.Count > BlockLocator.MaxLocatorSize)
            {
                this.logger.LogTrace("Peer '{0}' sent getblocks with oversized locator, disconnecting.", peer.RemoteSocketEndpoint);

                peer.Disconnect("Peer sent getblocks with oversized locator");

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

            // We only want to work with blocks that are in the store,
            // so we first get information about the store's tip.
            ChainedHeader blockStoreTip = this.chainState.BlockStoreTip;

            if (blockStoreTip == null)
            {
                this.logger.LogTrace("(-)[REORG]");
                return;
            }

            this.logger.LogTrace("Block store tip is '{0}'.", blockStoreTip);

            // Now we want to find the last common block between our chain and the block locator the peer sent us.
            ChainedHeader chainTip  = this.chain.Tip;
            ChainedHeader forkPoint = null;

            // Find last common block between our chain and the block locator the peer sent us.
            while (forkPoint == null)
            {
                forkPoint = this.chain.FindFork(getBlocksPayload.BlockLocators.Blocks);
                if (forkPoint == null)
                {
                    this.logger.LogTrace("(-)[NO_FORK_POINT]");
                    return;
                }

                // In case of reorg, we just try again, eventually we succeed.
                if (chainTip.FindAncestorOrSelf(forkPoint) == null)
                {
                    chainTip  = this.chain.Tip;
                    forkPoint = null;
                }
            }

            this.logger.LogTrace("Fork point is '{0}'.", forkPoint);

            // If block store is lower than the fork point, or it is on different chain, we don't have anything to contribute to this peer at this point.
            if (blockStoreTip.FindAncestorOrSelf(forkPoint) == null)
            {
                this.logger.LogTrace("(-)[FORK_OUTSIDE_STORE]");
                return;
            }

            // Now we compile a list of blocks we want to send to the peer as inventory vectors.
            // This is needed as we want to traverse the chain in forward direction.
            int           maxHeight    = Math.Min(blockStoreTip.Height, forkPoint.Height + InvPayload.MaxGetBlocksInventorySize);
            ChainedHeader lastBlock    = blockStoreTip.GetAncestor(maxHeight);
            int           headersCount = maxHeight - forkPoint.Height;

            this.logger.LogTrace("Last block to announce is '{0}', number of blocks to announce is {1}.", lastBlock, headersCount);

            var headersToAnnounce = new ChainedHeader[headersCount];

            for (int i = headersCount - 1; i >= 0; i--)
            {
                headersToAnnounce[i] = lastBlock;
                lastBlock            = lastBlock.Previous;
            }

            // Now we compile inventory payload and we also consider hash stop given by the peer.
            bool          sendContinuation       = true;
            ChainedHeader lastAddedChainedHeader = null;
            var           inv = new InvPayload();

            for (int i = 0; i < headersToAnnounce.Length; i++)
            {
                ChainedHeader chainedHeader = headersToAnnounce[i];
                if (chainedHeader.HashBlock == getBlocksPayload.HashStop)
                {
                    this.logger.LogTrace("Hash stop has been reached.");
                    break;
                }

                this.logger.LogTrace("Adding block '{0}' to the inventory.", chainedHeader);
                lastAddedChainedHeader = chainedHeader;
                inv.Inventory.Add(new InventoryVector(InventoryType.MSG_BLOCK, chainedHeader.HashBlock));
                if (chainedHeader.HashBlock == chainTip.HashBlock)
                {
                    this.logger.LogTrace("Tip of the chain has been reached.");
                    sendContinuation = false;
                }
            }

            int count = inv.Inventory.Count;

            if (count > 0)
            {
                // If we reached the limmit size of inv, we need to tell the downloader to send another 'getblocks' message.
                if (count == InvPayload.MaxGetBlocksInventorySize && lastAddedChainedHeader != null)
                {
                    this.logger.LogTrace("Setting peer's last block sent to '{0}'.", lastAddedChainedHeader);
                    this.lastSentHeader = lastAddedChainedHeader;
                    this.consensusManagerBehavior.UpdateBestSentHeader(this.lastSentHeader);

                    // Set last item of the batch (unless we are announcing the tip), which is then used
                    // when the peer sends us "getdata" message. When we detect "getdata" message for this block,
                    // we will send continuation inventory message. This will cause the peer to ask for another batch of blocks.
                    // See ProcessGetDataAsync method.
                    if (sendContinuation)
                    {
                        this.getBlocksBatchLastItemHash = lastAddedChainedHeader.HashBlock;
                    }
                }

                this.logger.LogTrace("Sending inventory with {0} block hashes.", count);
                await peer.SendMessageAsync(inv).ConfigureAwait(false);
            }
            else
            {
                this.logger.LogTrace("Nothing to send.");
            }
        }
 private Task ProcessSendCmpctPayloadAsync(INetworkPeer peer, SendCmpctPayload sendCmpct)
 {
     // TODO: announce using compact blocks
     return(Task.CompletedTask);
 }
        /// <summary>
        /// Sends transaction inventory to attached peer.
        /// This is executed on a 10 second loop when MempoolSignaled is constructed.
        /// </summary>
        public async Task SendTrickleAsync()
        {
            this.logger.LogTrace("()");

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

            INetworkPeer peer = this.AttachedPeer;

            if (peer == null)
            {
                this.logger.LogTrace("(-)[NO_PEER]");
                return;
            }

            // before locking an exclusive task
            // check if there is anything to processes
            if (!await this.manager.MempoolLock.ReadAsync(() => this.inventoryTxToSend.Keys.Any()))
            {
                this.logger.LogTrace("(-)[NO_TXS]");
                return;
            }

            List <uint256> sends = await this.manager.MempoolLock.WriteAsync(() =>
            {
                this.logger.LogTrace("Creating list of transaction inventory to send.");

                // Determine transactions to relay
                // Produce a vector with all candidates for sending
                List <uint256> invs = this.inventoryTxToSend.Keys.Take(InventoryBroadcastMax).ToList();
                List <uint256> ret  = new List <uint256>();
                foreach (uint256 hash in invs)
                {
                    // Remove it from the to-be-sent set
                    this.inventoryTxToSend.Remove(hash);

                    // Check if not in the filter already
                    if (this.filterInventoryKnown.ContainsKey(hash))
                    {
                        continue;
                    }

                    // Not in the mempool anymore? don't bother sending it.
                    TxMempoolInfo txInfo = this.manager.Info(hash);
                    if (txInfo == null)
                    {
                        continue;
                    }

                    //if (filterrate && txinfo.feeRate.GetFeePerK() < filterrate) // TODO:filterrate
                    //{
                    //  continue;
                    //}
                    ret.Add(hash);
                }

                this.logger.LogTrace("Transaction inventory list created.");
                return(ret);
            });

            if (sends.Any())
            {
                this.logger.LogTrace("Sending transaction inventory to peer '{0}'.", peer.RemoteSocketEndpoint);
                try
                {
                    await this.SendAsTxInventoryAsync(peer, sends).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    this.logger.LogTrace("(-)[CANCELED_EXCEPTION]");
                    return;
                }
            }

            this.logger.LogTrace("(-)");
        }
        /// <summary>
        /// Processing of inventory payload message from the peer.
        /// Adds inventory to known inventory then sends GetDataPayload to the attached peer.
        /// </summary>
        /// <param name="peer">The peer sending the message.</param>
        /// <param name="invPayload">The inventory payload in the message.</param>
        private async Task ProcessInvAsync(INetworkPeer peer, InvPayload invPayload)
        {
            Guard.NotNull(peer, nameof(peer));
            this.logger.LogTrace("({0}:'{1}',{2}.{3}.{4}:{5})", nameof(peer), peer.RemoteSocketEndpoint, nameof(invPayload), nameof(invPayload.Inventory), nameof(invPayload.Inventory.Count), invPayload.Inventory.Count);
            if (peer != this.AttachedPeer)
            {
                this.logger.LogDebug("Attached peer '{0}' does not match the originating peer '{1}'.", this.AttachedPeer?.RemoteSocketEndpoint, peer.RemoteSocketEndpoint);
                this.logger.LogTrace("(-)[PEER_MISMATCH]");
                return;
            }

            if (invPayload.Inventory.Count > ConnectionManager.MaxInventorySize)
            {
                this.logger.LogTrace("(-)[MAX_INV_SZ]");
                //Misbehaving(pfrom->GetId(), 20); // TODO: Misbehaving
                return; //error("message inv size() = %u", vInv.size());
            }

            if (this.initialBlockDownloadState.IsInitialBlockDownload())
            {
                this.logger.LogTrace("(-)[IS_IBD]");
                return;
            }

            bool blocksOnly = !this.manager.mempoolSettings.RelayTxes;

            // Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true
            if (peer.Behavior <ConnectionManagerBehavior>().Whitelisted&& this.manager.mempoolSettings.WhiteListRelay)
            {
                blocksOnly = false;
            }

            //uint32_t nFetchFlags = GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus());

            GetDataPayload send = new GetDataPayload();

            foreach (InventoryVector inv in invPayload.Inventory.Where(inv => inv.Type.HasFlag(InventoryType.MSG_TX)))
            {
                //inv.type |= nFetchFlags;

                if (blocksOnly)
                {
                    this.logger.LogInformation("Transaction ID '{0}' inventory sent in violation of protocol peer '{1}'.", inv.Hash, peer.RemoteSocketEndpoint);
                }

                if (await this.orphans.AlreadyHaveAsync(inv.Hash))
                {
                    this.logger.LogDebug("Transaction ID '{0}' already in orphans, skipped.", inv.Hash);
                    continue;
                }

                send.Inventory.Add(inv);
            }

            // add to known inventory
            await this.manager.MempoolLock.WriteAsync(() =>
            {
                foreach (InventoryVector inventoryVector in send.Inventory)
                {
                    this.filterInventoryKnown.TryAdd(inventoryVector.Hash, inventoryVector.Hash);
                }
            });

            if (peer.IsConnected)
            {
                this.logger.LogTrace("Sending transaction inventory to peer '{0}'.", peer.RemoteSocketEndpoint);
                await peer.SendMessageAsync(send).ConfigureAwait(false);
            }

            this.logger.LogTrace("(-)");
        }
        /// <summary>
        /// Send the memory pool payload to the attached peer.
        /// Gets the transaction info from the memory pool and sends to the attached peer.
        /// </summary>
        /// <param name="peer">Peer sending the message.</param>
        /// <param name="message">The message payload.</param>
        private async Task SendMempoolPayloadAsync(INetworkPeer peer, MempoolPayload message)
        {
            Guard.NotNull(peer, nameof(peer));
            this.logger.LogTrace("({0}:'{1}',{2}:'{3}')", nameof(peer), peer.RemoteSocketEndpoint, nameof(message), message.Command);
            if (peer != this.AttachedPeer)
            {
                this.logger.LogDebug("Attached peer '{0}' does not match the originating peer '{1}'.", this.AttachedPeer?.RemoteSocketEndpoint, peer.RemoteSocketEndpoint);
                this.logger.LogTrace("(-)[PEER_MISMATCH]");
                return;
            }

            if (!this.CanSend)
            {
                return;
            }

            //if (!(pfrom->GetLocalServices() & NODE_BLOOM) && !pfrom->fWhitelisted)
            //{
            //  LogPrint("net", "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom->GetId());
            //  pfrom->fDisconnect = true;
            //  return true;
            //}

            //if (connman.OutboundTargetReached(false) && !pfrom->fWhitelisted)
            //{
            //  LogPrint("net", "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId());
            //  pfrom->fDisconnect = true;
            //  return true;
            //}

            List <TxMempoolInfo> vtxinfo = await this.manager.InfoAllAsync();

            Money filterrate = Money.Zero;

            // TODO: implement minFeeFilter
            //{
            //  LOCK(pto->cs_feeFilter);
            //  filterrate = pto->minFeeFilter;
            //}

            List <TxMempoolInfo> sends = await this.manager.MempoolLock.WriteAsync(() =>
            {
                List <TxMempoolInfo> ret = new List <TxMempoolInfo>();
                foreach (TxMempoolInfo txinfo in vtxinfo)
                {
                    uint256 hash = txinfo.Trx.GetHash();
                    this.inventoryTxToSend.Remove(hash);
                    if (filterrate != Money.Zero)
                    {
                        if (txinfo.FeeRate.FeePerK < filterrate)
                        {
                            continue;
                        }
                    }
                    this.filterInventoryKnown.TryAdd(hash, hash);
                }
                return(ret);
            });

            this.logger.LogTrace("Sending transaction inventory to peer '{0}'.", peer.RemoteSocketEndpoint);
            await this.SendAsTxInventoryAsync(peer, sends.Select(s => s.Trx.GetHash()));

            this.LastMempoolReq = this.manager.DateTimeProvider.GetTime();

            this.logger.LogTrace("(-)");
        }
Example #15
0
        private bool PingVersion()
        {
            INetworkPeer peer = this.AttachedPeer;

            return((peer != null) && (peer.Version > NBitcoin.Protocol.ProtocolVersion.BIP0031_VERSION));
        }
Example #16
0
 public INetworkConnection(INetworkPeer peer, INetworkObject gobject)
 {
     this.ParentPeer           = peer;
     this.GameObjectController = gobject;
 }
Example #17
0
        /// <summary>Attempts to connect to a random peer.</summary>
        public async Task ConnectAsync(PeerAddress peerAddress)
        {
            if (this.selfEndpointTracker.IsSelf(peerAddress.Endpoint))
            {
                this.logger.LogDebug("Connect aborted: {0} is self.", peerAddress.Endpoint);
                return;
            }

            if (this.IsPeerConnected(peerAddress.Endpoint))
            {
                this.logger.LogDebug("Connect aborted: {0} is already connected.", peerAddress.Endpoint);
                return;
            }

            if (peerAddress.IsBanned(this.dateTimeProvider.GetUtcNow()))
            {
                this.logger.LogDebug("Connect aborted: {0} is banned until {1}.", peerAddress.Endpoint, peerAddress.BanUntil);
                return;
            }

            INetworkPeer peer = null;

            try
            {
                using (CancellationTokenSource timeoutTokenSource = CancellationTokenSource.CreateLinkedTokenSource(this.NodeLifetime.ApplicationStopping))
                {
                    this.PeerAddressManager.PeerAttempted(peerAddress.Endpoint, this.dateTimeProvider.GetUtcNow());

                    NetworkPeerConnectionParameters clonedConnectParameters = this.CurrentParameters.Clone();
                    timeoutTokenSource.CancelAfter(5000);
                    clonedConnectParameters.ConnectCancellation = timeoutTokenSource.Token;

                    peer = await this.networkPeerFactory.CreateConnectedNetworkPeerAsync(peerAddress.Endpoint, clonedConnectParameters, this.networkPeerDisposer).ConfigureAwait(false);

                    await peer.VersionHandshakeAsync(this.Requirements, timeoutTokenSource.Token).ConfigureAwait(false);

                    this.AddPeer(peer);
                }
            }
            catch (OperationCanceledException)
            {
                if (this.NodeLifetime.ApplicationStopping.IsCancellationRequested)
                {
                    this.logger.LogDebug("Peer {0} connection canceled because application is stopping.", peerAddress.Endpoint);
                    peer?.Disconnect("Application stopping");
                }
                else
                {
                    this.logger.LogDebug("Peer {0} connection timeout.", peerAddress.Endpoint);
                    peerAddress.SetHandshakeAttempted(this.dateTimeProvider.GetUtcNow());
                    peer?.Disconnect("Connection timeout");
                }
            }
            catch (NBitcoin.Protocol.ProtocolException)
            {
                this.logger.LogDebug("Handshake rejected by peer '{0}'.", peerAddress.Endpoint);
                peerAddress.SetHandshakeAttempted(this.dateTimeProvider.GetUtcNow());
                peer?.Disconnect("Error while handshaking");
            }
            catch (Exception exception)
            {
                this.logger.LogDebug("Exception occurred while connecting: {0}", exception is SocketException ? exception.Message : exception.ToString());
                peerAddress.SetHandshakeAttempted(this.dateTimeProvider.GetUtcNow());
                peer?.Disconnect("Error while connecting", exception);
            }
        }
Example #18
0
 private static bool Disconnected(INetworkPeer peer)
 {
     return((peer.State == NetworkPeerState.Created) || (peer.State == NetworkPeerState.Disconnecting) ||
            (peer.State == NetworkPeerState.Failed) || (peer.State == NetworkPeerState.Offline));
 }
        private async Task ProcessConversionRequestPayloadAsync(INetworkPeer peer, ConversionRequestPayload payload)
        {
            if (!this.federationManager.IsFederationMember)
            {
                return;
            }

            this.logger.LogDebug("Conversion request payload request for id '{0}' received from '{1}':'{2}' proposing transaction ID '{4}'.", payload.RequestId, peer.PeerEndPoint.Address, peer.RemoteSocketEndpoint.Address, payload.RequestId, payload.TransactionId);

            if (payload.TransactionId == BigInteger.MinusOne)
            {
                return;
            }

            // Check that the payload is signed by a multisig federation member.
            PubKey pubKey;

            try
            {
                pubKey = PubKey.RecoverFromMessage(payload.RequestId + payload.TransactionId, payload.Signature);

                if (!this.federationManager.IsMultisigMember(pubKey))
                {
                    this.logger.LogWarning("Conversion request payload for '{0}'. Computed pubkey '{1}'.", payload.RequestId, pubKey?.ToHex());

                    return;
                }
            }
            catch (Exception)
            {
                this.logger.LogWarning("Received malformed conversion request payload for '{0}'.", payload.RequestId);
                return;
            }

            BigInteger confirmationCount;

            try
            {
                // Check that the transaction ID in the payload actually exists, and is unconfirmed.
                confirmationCount = await this.ethClientProvider.GetClientForChain(payload.DestinationChain).GetMultisigConfirmationCountAsync(payload.TransactionId).ConfigureAwait(false);
            }
            catch (Exception)
            {
                return;
            }

            // We presume that the initial submitter of the transaction must have at least confirmed it. Otherwise just ignore this coordination attempt.
            if (confirmationCount < 1)
            {
                this.logger.LogInformation("Multisig wallet transaction {0} has no confirmations.", payload.TransactionId);
                return;
            }

            // Only add votes if the conversion request has not already been finalized.
            ConversionRequest conversionRequest = this.conversionRequestRepository.Get(payload.RequestId);

            if (conversionRequest != null && conversionRequest.RequestStatus != ConversionRequestStatus.VoteFinalised)
            {
                this.conversionRequestCoordinationService.AddVote(payload.RequestId, payload.TransactionId, pubKey);
            }

            if (payload.IsRequesting)
            {
                string signature = this.federationManager.CurrentFederationKey.SignMessage(payload.RequestId + payload.TransactionId);
                await this.AttachedPeer.SendMessageAsync(ConversionRequestPayload.Reply(payload.RequestId, payload.TransactionId, signature, payload.DestinationChain)).ConfigureAwait(false);
            }
        }
Example #20
0
 public virtual void Dispose()
 {
     this.AttachedPeer = null;
 }
        /// <summary>
        /// Processes "headers" message received from the peer.
        /// </summary>
        /// <param name="peer">Peer from which the message was received.</param>
        /// <param name="headers">List of headers 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>
        protected virtual async Task ProcessHeadersAsync(INetworkPeer peer, List <BlockHeader> headers)
        {
            this.logger.LogDebug("Received {0} headers. First: '{1}'  Last: '{2}'.", headers.Count, headers.FirstOrDefault()?.ToString(), headers.LastOrDefault()?.ToString());

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

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

                this.logger.LogDebug("Headers are invalid. Peer was banned.");
                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.LogDebug("Cache is full. Headers ignored.");
                    this.logger.LogTrace("(-)[CACHE_IS_FULL]");
                    return;
                }

                // If queue is not empty, add to queue instead of calling CM.
                if (this.cachedHeaders.Count != 0)
                {
                    uint256 cachedHeader  = this.cachedHeaders.Last().GetHash();
                    uint256 prevNewHeader = headers.First().HashPrevBlock;

                    // Ensure headers can connect to cached headers.
                    if (cachedHeader == prevNewHeader)
                    {
                        this.cachedHeaders.AddRange(headers);

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

                    // Workaround for special case when peer announces new block but we don't want to clean the cache.
                    // For example, we are busy syncing and have D E F in the cache (assume A B C are already in the
                    // CHT). The peer now advertises new block M because we haven't completed IBD yet.
                    // We do not want to clear the currently useful cache unnecessarily.
                    if (this.CheckIfUnsolicitedFutureHeader(headers))
                    {
                        this.logger.LogTrace("(-)[HEADER_FUTURE_CANT_CONNECT]");
                        return;
                    }

                    // The header(s) could not be connected and were not an unsolicited block advertisement.
                    this.cachedHeaders.Clear();
                    await this.ResyncAsync().ConfigureAwait(false);

                    this.logger.LogDebug("Header {0} could not be connected to last cached header {1}, clear cache and resync.", headers[0].GetHash(), cachedHeader);
                    this.logger.LogTrace("(-)[FAILED_TO_ATTACH_TO_CACHE]");
                    return;
                }

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

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

                    return;
                }

                if (result.Consumed == null)
                {
                    this.cachedHeaders.AddRange(headers);
                    this.logger.LogDebug("All {0} items were not consumed and added to cache.", headers.Count);

                    return;
                }

                if (result.Consumed == this.BestReceivedTip)
                {
                    this.logger.LogDebug("No new headers were presented");
                    this.logger.LogTrace("(-)[NO_NEW_HEADERS_PRESENTED]");

                    return;
                }

                this.logger.LogTrace("{0} is {1} and {2} is {3}", nameof(this.BestReceivedTip), this.BestReceivedTip, nameof(result.Consumed), result.Consumed);

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

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

                    this.logger.LogDebug("{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);
                }
            }
        }
        /// <summary>
        /// Processes "headers" message received from the peer.
        /// </summary>
        /// <param name="peer">Peer from which the message was received.</param>
        /// <param name="headers">List of headers 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>
        protected virtual async Task ProcessHeadersAsync(INetworkPeer peer, List <BlockHeader> headers)
        {
            this.logger.LogDebug("Received {0} headers. First: '{1}'  Last: '{2}'.", headers.Count, headers.FirstOrDefault()?.ToString(), headers.LastOrDefault()?.ToString());

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

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

                this.logger.LogDebug("Headers are invalid. Peer was banned.");
                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.LogDebug("Cache is full. Headers ignored.");
                    this.logger.LogTrace("(-)[CACHE_IS_FULL]");
                    return;
                }

                // If queue is not empty, add to queue instead of calling CM.
                if (this.cachedHeaders.Count != 0)
                {
                    uint256 cachedHeader  = this.cachedHeaders.Last().GetHash();
                    uint256 prevNewHeader = headers.First().HashPrevBlock;

                    // ensure headers can connect to cached headers.
                    if (cachedHeader == prevNewHeader)
                    {
                        this.cachedHeaders.AddRange(headers);

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

                    // Workaround for special case when peer announces new block but we don't want to clean the cache.
                    if (headers.Count == 1 && this.BestReceivedTip != null)
                    {
                        // Distance of header from the peer expected tip.
                        double distanceSeconds = (headers[0].BlockTime - this.BestReceivedTip.Header.BlockTime).TotalSeconds;

                        if (this.chain.Network.MaxTipAge < distanceSeconds)
                        {
                            // A single header that is not connected to last header is likely an
                            // unsolicited header that is a result of the peer tip being extended.
                            // If the header time is far in the future we ignore it.
                            this.logger.LogTrace("(-)[HEADER_FUTURE_CANT_CONNECT]");
                            return;
                        }
                    }

                    this.cachedHeaders.Clear();
                    await this.ResyncAsync().ConfigureAwait(false);

                    this.logger.LogDebug("Header {0} could not be connected to last cached header {1}, clear cache and resync.", headers[0].GetHash(), cachedHeader);
                    this.logger.LogTrace("(-)[FAILED_TO_ATTACH_TO_CACHE]");
                    return;
                }

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

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

                    return;
                }

                if (result.Consumed == null)
                {
                    this.cachedHeaders.AddRange(headers);
                    this.logger.LogDebug("All {0} items were not consumed and added to cache.", headers.Count);

                    return;
                }

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

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

                    this.logger.LogDebug("{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);
                }
            }
        }
Example #23
0
 public NetworkPeerBehaviorsCollection(INetworkPeer peer)
 {
     this.peer = peer;
 }
        /// <summary>Attempts to connect to a random peer.</summary>
        internal async Task ConnectAsync(PeerAddress peerAddress)
        {
            if (this.selfEndpointTracker.IsSelf(peerAddress.Endpoint))
            {
                this.logger.LogTrace("{0} is self. Therefore not connecting.", peerAddress.Endpoint);
                this.logger.LogTrace("(-)");
                return;
            }

            // Connect if local, ip range filtering disabled or ip range filtering enabled and peer in a different group.
            if (peerAddress.Endpoint.Address.IsRoutable(false) && this.ConnectionSettings.IpRangeFiltering && this.PeerIsPartOfExistingGroup(peerAddress))
            {
                this.logger.LogTrace("(-)[RANGE_FILTERED]");
                return;
            }

            INetworkPeer peer = null;

            try
            {
                using (CancellationTokenSource timeoutTokenSource = CancellationTokenSource.CreateLinkedTokenSource(this.nodeLifetime.ApplicationStopping))
                {
                    this.peerAddressManager.PeerAttempted(peerAddress.Endpoint, this.dateTimeProvider.GetUtcNow());

                    NetworkPeerConnectionParameters clonedConnectParamaters = this.CurrentParameters.Clone();
                    timeoutTokenSource.CancelAfter(5000);
                    clonedConnectParamaters.ConnectCancellation = timeoutTokenSource.Token;

                    peer = await this.networkPeerFactory.CreateConnectedNetworkPeerAsync(peerAddress.Endpoint, clonedConnectParamaters, this.networkPeerDisposer).ConfigureAwait(false);

                    await peer.VersionHandshakeAsync(this.Requirements, timeoutTokenSource.Token).ConfigureAwait(false);

                    this.AddPeer(peer);
                }
            }
            catch (OperationCanceledException)
            {
                if (this.nodeLifetime.ApplicationStopping.IsCancellationRequested)
                {
                    this.logger.LogDebug("Peer {0} connection canceled because application is stopping.", peerAddress.Endpoint);
                    peer?.Disconnect("Application stopping");
                }
                else
                {
                    this.logger.LogDebug("Peer {0} connection timeout.", peerAddress.Endpoint);
                    peerAddress.SetHandshakeAttempted(this.dateTimeProvider.GetUtcNow());
                    peer?.Disconnect("Connection timeout");
                }
            }
            catch (NBitcoin.Protocol.ProtocolException)
            {
                this.logger.LogDebug("Handshake rejected by peer '{0}'.", peerAddress.Endpoint);
                peerAddress.SetHandshakeAttempted(this.dateTimeProvider.GetUtcNow());
                peer?.Disconnect("Error while handshaking");
            }
            catch (Exception exception)
            {
                this.logger.LogTrace("Exception occurred while connecting: {0}", exception.ToString());
                peerAddress.SetHandshakeAttempted(this.dateTimeProvider.GetUtcNow());
                peer?.Disconnect("Error while connecting", exception);
            }
        }
Example #25
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, this.GetOrCreateAsyncProvider()))
            {
                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.
                        }
                    }
                }
            }
        }
        async Task OnMessageReceivedAsync(INetworkPeer peer, IncomingMessage message)
        {
            try
            {
                if ((this.Mode & PeerAddressManagerBehaviourMode.Advertise) != 0)
                {
                    if (message.Message.Payload is GetAddrPayload)
                    {
                        if (!peer.Inbound)
                        {
                            this.logger.LogDebug("Outbound peer sent {0}. Not replying to avoid fingerprinting attack.",
                                                 nameof(GetAddrPayload));
                            return;
                        }

                        if (this.addrPayloadSent)
                        {
                            this.logger.LogDebug(
                                "Multiple GetAddr requests from peer. Not replying to avoid fingerprinting attack.");
                            return;
                        }

                        var endPoints = this.peerAddressManager.PeerSelector
                                        .SelectPeersForGetAddrPayload(MaxAddressesPerAddrPayload).Select(p => p.Endpoint);
                        var addressPayload = new AddrPayload(endPoints.Select(p => new NetworkAddress(p)).ToArray());

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

                        this.logger.LogDebug("Sent address payload following GetAddr request.");

                        this.addrPayloadSent = true;
                    }

                    if (message.Message.Payload is PingPayload || message.Message.Payload is PongPayload)
                    {
                        if (peer.State == NetworkPeerState.HandShaked)
                        {
                            this.peerAddressManager.PeerSeen(peer.PeerEndPoint, this.dateTimeProvider.GetUtcNow());
                        }
                    }
                }

                if ((this.Mode & PeerAddressManagerBehaviourMode.Discover) != 0)
                {
                    if (message.Message.Payload is AddrPayload addr)
                    {
                        if (addr.Addresses.Length > MaxAddressesPerAddrPayload)
                        {
                            // Not respecting the protocol.
                            this.peerBanning.BanAndDisconnectPeer(peer.PeerEndPoint,
                                                                  $"Protocol violation: addr payload size is limited by {MaxAddressesPerAddrPayload} entries.");

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

                        this.peerAddressManager.AddPeers(addr.Addresses.Select(a => a.Endpoint),
                                                         peer.RemoteSocketAddress);
                    }
                }
            }
            catch (OperationCanceledException)
            {
            }
        }
Example #27
0
 /// <summary>
 /// Adds the peer to the collection of connected peers.
 /// </summary>
 /// <param name="peer">The peer to add.</param>
 public void AddPeer(INetworkPeer peer)
 {
     this.connectedPeers.TryAdd(peer.Connection.Id, peer);
 }
Example #28
0
 protected override (bool accept, string reason) OnConnectingCheck(INetworkPeer peer, CConnectRequest packet)
 {
     return(base.OnConnectingCheck(peer, packet));
 }
        public void MustNotAnnounceABlock_WhenNotInBestChain()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                CoreNode stratisNodeSync = builder.CreateStratisPowNode(this.network).WithReadyBlockchainData(ReadyBlockchain.BitcoinRegTest10Miner).Start();
                CoreNode stratisNode1    = builder.CreateStratisPowNode(this.network).WithReadyBlockchainData(ReadyBlockchain.BitcoinRegTest10Listener).Start();
                CoreNode stratisNode2    = builder.CreateStratisPowNode(this.network).WithReadyBlockchainData(ReadyBlockchain.BitcoinRegTest10NoWallet).Start();

                // Store block 1 of chain0 for later usage
                ChainedHeader firstBlock = null;
                foreach (ChainedHeader chainedHeader in stratisNodeSync.FullNode.Chain.EnumerateToTip(this.network.GenesisHash))
                {
                    if (chainedHeader.Height == 1)
                    {
                        firstBlock = chainedHeader;
                    }
                }

                Assert.NotNull(firstBlock);

                // Mine longer chain1 using node1
                TestHelper.MineBlocks(stratisNode1, 15);

                IConnectionManager node1ConnectionManager = stratisNode1.FullNode.NodeService <IConnectionManager>();
                node1ConnectionManager.Parameters.TemplateBehaviors.Add(new TestBehavior());

                IConnectionManager node2ConnectionManager = stratisNode2.FullNode.NodeService <IConnectionManager>();
                node2ConnectionManager.Parameters.TemplateBehaviors.Add(new TestBehavior());

                // Connect node0 and node1
                TestHelper.Connect(stratisNode1, stratisNodeSync);

                INetworkPeer connectedPeer = node1ConnectionManager.ConnectedPeers.FindByEndpoint(stratisNodeSync.Endpoint);
                TestBehavior testBehavior  = connectedPeer.Behavior <TestBehavior>();

                // We expect that node0 will abandon the 10 block chain and use the 15 block chain from node1
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisNode1, stratisNodeSync));

                // Connect all nodes together
                TestHelper.Connect(stratisNode2, stratisNodeSync);
                TestHelper.Connect(stratisNode1, stratisNode2);

                INetworkPeer connectedPeer2 = node2ConnectionManager.ConnectedPeers.FindByEndpoint(stratisNodeSync.Endpoint);
                TestBehavior testBehavior2  = connectedPeer2.Behavior <TestBehavior>();

                // Wait for node2 to sync; it should have the 15 block chain
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisNode2, stratisNodeSync));

                // Insert block 1 from chain0 into node1's announce queue
                BlockStoreSignaled node1BlockStoreSignaled = stratisNode1.FullNode.NodeService <BlockStoreSignaled>();

                AsyncQueue <ChainedHeader> node1BlocksToAnnounce = (AsyncQueue <ChainedHeader>)node1BlockStoreSignaled.GetMemberValue("blocksToAnnounce");

                Queue <ChainedHeader> node1QueueItems = (Queue <ChainedHeader>)node1BlocksToAnnounce.GetMemberValue("items");

                TestHelper.WaitLoop(() => node1QueueItems.Count == 0);

                // Check that node2 does not have block 1 in test behaviour advertised list
                foreach (IncomingMessage message in testBehavior2.receivedMessageTracker["headers"])
                {
                    if (message.Message.Payload is HeadersPayload)
                    {
                        foreach (BlockHeader header in ((HeadersPayload)message.Message.Payload).Headers)
                        {
                            if (header.GetHash() == firstBlock.Header.GetHash())
                            {
                                throw new Exception("Should not have received payload announcing block from wrong chain");
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Implements loop accepting connections from newly connected clients.
        /// </summary>
        private async Task AcceptClientsAsync()
        {
            this.logger.LogTrace("()");

            this.logger.LogTrace("Accepting incoming connections.");

            try
            {
                while (!this.serverCancel.IsCancellationRequested)
                {
                    // Used to record any errors occurring in the thread pool task.
                    Exception error = null;

                    TcpClient tcpClient = await Task.Run(() =>
                    {
                        try
                        {
                            Task <TcpClient> acceptClientTask = this.tcpListener.AcceptTcpClientAsync();
                            acceptClientTask.Wait(this.serverCancel.Token);
                            return(acceptClientTask.Result);
                        }
                        catch (Exception exception)
                        {
                            // Record the error.
                            error = exception;
                            return(null);
                        }
                    }).ConfigureAwait(false);

                    // Raise the error.
                    if (error != null)
                    {
                        throw error;
                    }

                    if (this.peersById.Count >= MaxConnectionThreshold)
                    {
                        this.logger.LogTrace("Maximum connection threshold [{0}] reached, closing the client.", MaxConnectionThreshold);
                        tcpClient.Close();
                        continue;
                    }

                    this.logger.LogTrace("Connection accepted from client '{0}'.", tcpClient.Client.RemoteEndPoint);

                    INetworkPeer networkPeer = this.networkPeerFactory.CreateNetworkPeer(tcpClient, this.CreateNetworkPeerConnectionParameters());

                    this.peersById.TryAdd(networkPeer.Connection.Id, networkPeer);

                    networkPeer.StateChanged.Register(this.OnStateChangedAsync);
                }
            }
            catch (OperationCanceledException)
            {
                this.logger.LogDebug("Shutdown detected, stop accepting connections.");
            }
            catch (Exception e)
            {
                this.logger.LogDebug("Exception occurred: {0}", e.ToString());
            }

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