/// <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); }
/// <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); }
public static bool IsWhitelisted(this INetworkPeer peer) { return(peer.Behavior <IConnectionManagerBehavior>()?.Whitelisted == true); }
/// <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); }
/// <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; } }
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("(-)"); }
private bool PingVersion() { INetworkPeer peer = this.AttachedPeer; return((peer != null) && (peer.Version > NBitcoin.Protocol.ProtocolVersion.BIP0031_VERSION)); }
public INetworkConnection(INetworkPeer peer, INetworkObject gobject) { this.ParentPeer = peer; this.GameObjectController = gobject; }
/// <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); } }
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); } }
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); } } }
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); } }
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) { } }
/// <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); }
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("(-)"); }