/// <summary>
        /// Processes "getheaders" message received from the peer.
        /// </summary>
        /// <param name="peer">Peer from which the message was received.</param>
        /// <param name="getHeadersPayload">Payload of "getheaders" message to process.</param>
        /// <remarks>
        /// "getheaders" message is sent by the peer in response to "inv(block)" message
        /// after the connection is established, or in response to "headers" message
        /// until an empty array is returned.
        /// <para>
        /// This payload notifies peers of our current best validated height,
        /// which is held by consensus tip (not concurrent chain tip).
        /// </para>
        /// <para>
        /// If the peer is behind/equal to our best height an empty array is sent back.
        /// </para>
        /// </remarks>
        private async Task ProcessGetHeadersAsync(INetworkPeer peer, GetHeadersPayload getHeadersPayload)
        {
            this.logger.LogTrace("({0}:'{1}',{2}:'{3}')", nameof(peer), peer.RemoteSocketEndpoint, nameof(getHeadersPayload), getHeadersPayload);

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

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

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

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

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

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

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

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

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

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

            this.logger.LogTrace("(-)");
        }
        /// <inheritdoc/>
        public void NewPeerTipClaimed(INetworkPeer peer, ChainedHeader newTip)
        {
            lock (this.peerLock)
            {
                int peerId = peer.Connection.Id;

                if (this.pullerBehaviorsByPeerId.TryGetValue(peerId, out IBlockPullerBehavior behavior))
                {
                    behavior.Tip = newTip;
                    this.logger.LogDebug("Tip for peer with ID {0} was changed to '{1}'.", peerId, newTip);
                }
                else
                {
                    bool supportsRequirments = this.networkPeerRequirement.Check(peer.PeerVersion, peer.Inbound, out string reason);

                    if (supportsRequirments)
                    {
                        behavior     = peer.Behavior <IBlockPullerBehavior>();
                        behavior.Tip = newTip;
                        this.pullerBehaviorsByPeerId.Add(peerId, behavior);

                        this.logger.LogDebug("New peer with ID {0} and tip '{1}' was added.", peerId, newTip);
                    }
                    else
                    {
                        this.logger.LogDebug("Peer ID {0} was discarded since he doesn't support the requirements, reason: {1}", peerId, reason);
                    }
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Processes "getheaders" message received from the peer.
        /// </summary>
        /// <param name="peer">Peer from which the message was received.</param>
        /// <param name="getHeadersPayload">Payload of "getheaders" message to process.</param>
        /// <remarks>
        /// "getheaders" message is sent by the peer in response to "headers" message until an empty array is received.
        /// <para>
        /// This payload notifies peers of our current best validated height, which is held by consensus tip.
        /// </para>
        /// <para>
        /// If the peer is behind/equal to our best height an empty array is sent back.
        /// </para>
        /// </remarks>
        private async Task ProcessGetHeadersAsync(INetworkPeer peer, GetHeadersPayload getHeadersPayload)
        {
            this.logger.LogTrace("({0}:'{1}',{2}:'{3}')", nameof(peer), peer.RemoteSocketEndpoint, nameof(getHeadersPayload), getHeadersPayload);

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

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

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

                try
                {
                    this.BestSentHeader = lastHeader;

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

            this.logger.LogTrace("(-)");
        }
Beispiel #4
0
        /// <inheritdoc />
        public void BanPeer(IPEndPoint endpoint, int banTimeSeconds, string reason = null)
        {
            Guard.NotNull(endpoint, nameof(endpoint));
            this.logger.LogTrace("({0}:'{1}',{2}:'{3}')", nameof(endpoint), endpoint, nameof(reason), reason);

            reason = reason ?? "unknown";

            bool         banPeer = true;
            INetworkPeer peer    = this.connectionManager.ConnectedPeers.FindByEndpoint(endpoint);

            if (peer != null)
            {
                ConnectionManagerBehavior peerBehavior = peer.Behavior <ConnectionManagerBehavior>();
                if (!peerBehavior.Whitelisted)
                {
                    peer.Disconnect($"The peer was banned, reason: {reason}");
                }
                else
                {
                    banPeer = false;
                    this.logger.LogTrace("Peer '{0}' is whitelisted, for reason '{1}' it was not banned!", endpoint, reason);
                }
            }

            if (banPeer)
            {
                this.logger.LogDebug("Peer '{0}' banned for reason '{1}'.", endpoint, reason);
                this.banStore.BanPeer(endpoint, this.dateTimeProvider.GetUtcNow().AddSeconds(banTimeSeconds));
            }

            this.logger.LogTrace("(-)");
        }
        /// <inheritdoc />
        protected override void AttachCore()
        {
            this.logger.LogTrace("()");

            INetworkPeer peer         = this.AttachedPeer;
            var          peerBehavior = peer.Behavior <IConnectionManagerBehavior>();

            if (peer.State == NetworkPeerState.Connected && !peerBehavior.Whitelisted)
            {
                if (this.peerBanning.IsBanned(peer.RemoteSocketEndpoint))
                {
                    this.logger.LogDebug("Peer '{0}' was previously banned.", peer.RemoteSocketEndpoint);
                    peer.Disconnect("A banned node tried to connect.");
                    this.logger.LogTrace("(-)[PEER_BANNED]");
                    return;
                }
            }

            this.AttachedPeer.MessageReceived.Register(this.OnMessageReceivedAsync);
            this.chainHeadersBehavior      = this.AttachedPeer.Behaviors.Find <ChainHeadersBehavior>();
            this.connectionManagerBehavior = this.AttachedPeer.Behaviors.Find <IConnectionManagerBehavior>();
            this.eventHandlerRegistered    = true;

            this.logger.LogTrace("(-)");
        }
Beispiel #6
0
        /// <inheritdoc/>
        public void NewPeerTipClaimed(INetworkPeer peer, ChainedHeader newTip)
        {
            this.logger.LogTrace("({0}:{1},{2}:'{3}')", nameof(peer.Connection.Id), peer.Connection.Id, nameof(newTip), newTip);

            lock (this.peerLock)
            {
                int peerId = peer.Connection.Id;

                if (this.pullerBehaviorsByPeerId.TryGetValue(peerId, out IBlockPullerBehavior behavior))
                {
                    behavior.Tip = newTip;
                    this.logger.LogTrace("Tip for peer with ID {0} was changed to '{1}'.", peerId, newTip);
                }
                else
                {
                    bool supportsRequirments = this.networkPeerRequirement.Check(peer.PeerVersion);

                    if (supportsRequirments)
                    {
                        behavior     = peer.Behavior <IBlockPullerBehavior>();
                        behavior.Tip = newTip;
                        this.pullerBehaviorsByPeerId.Add(peerId, behavior);

                        this.logger.LogTrace("New peer with ID {0} and tip '{1}' was added.", peerId, newTip);
                    }
                    else
                    {
                        this.logger.LogTrace("Peer ID {0} was discarded since he doesn't support the requirements.", peerId);
                    }
                }
            }

            this.logger.LogTrace("(-)");
        }
        public void CheckBlocksAnnounced_AndQueueEmptiesOverTime()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                CoreNode stratisNodeSync = builder.CreateStratisPowNode(this.network).WithWallet().Start();
                CoreNode stratisNode1    = builder.CreateStratisPowNode(this.network).Start();

                TestHelper.MineBlocks(stratisNodeSync, 10);

                // Change the second node's list 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());

                // Connect node1 to initial node.
                TestHelper.Connect(stratisNode1, stratisNodeSync);

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

                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisNode1, 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 testBehavior.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: {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");

                TestHelper.WaitLoop(() => queueItems.Count == 0);
            }
        }
        protected override void AttachCore()
        {
            INetworkPeer peer         = this.AttachedPeer;
            var          peerBehavior = peer.Behavior <IConnectionManagerBehavior>();

            if (peer.State == NetworkPeerState.Connected && !peerBehavior.Whitelisted)
            {
                if (this.peerBanning.IsBanned(peer.RemoteSocketEndpoint))
                {
                    this.logger.LogDebug("Peer '{0}' was previously banned.", peer.RemoteSocketEndpoint);
                    peer.Disconnect("A banned node tried to connect.");
                    this.logger.LogTrace("(-)[PEER_BANNED]");
                    return;
                }
            }
        }
Beispiel #9
0
        private async Task ProcessFeeFilterAsync(INetworkPeer peer, FeeFilterPayload feeFilter)
        {
            if (peer.PeerVersion.Relay)
            {
                if (feeFilter.NewFeeFilter > 0)
                {
                    if (this.mempoolBehavior == null)
                    {
                        this.mempoolBehavior = peer.Behavior <MempoolBehavior>();
                    }

                    this.mempoolBehavior.MinFeeFilter = feeFilter.NewFeeFilter;

                    this.logger.LogDebug("Received feefilter of `{0}` from peer id: {1}", feeFilter.NewFeeFilter, peer.Connection.Id);
                }
            }
        }
Beispiel #10
0
        public IActionResult DisconnectPeer([FromBody] DisconnectPeerViewModel viewModel)
        {
            try
            {
                var          endpoint = viewModel.PeerAddress.ToIPEndPoint(this.network.DefaultPort);
                INetworkPeer peer     = this.connectionManager.ConnectedPeers.FindByEndpoint(endpoint);
                if (peer != null)
                {
                    var peerBehavior = peer.Behavior <IConnectionManagerBehavior>();
                    peer.Disconnect($"{endpoint} was disconnected via the API.");
                }

                return(this.Ok());
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
Beispiel #11
0
        public void CheckSegwitP2PSerialisationForWitnessNode()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                CoreNode node     = builder.CreateStratisPosNode(KnownNetworks.StraxRegTest).Start();
                CoreNode listener = builder.CreateStratisPosNode(KnownNetworks.StraxRegTest).Start();

                IConnectionManager listenerConnMan = listener.FullNode.NodeService <IConnectionManager>();
                listenerConnMan.Parameters.TemplateBehaviors.Add(new TestBehavior());

                // The listener node will have default settings, i.e. it should ask for witness data in P2P messages.
                Assert.True(listenerConnMan.Parameters.Services.HasFlag(NetworkPeerServices.NODE_WITNESS));

                TestHelper.Connect(listener, node);

                // Mine a Segwit block on the first node.
                var            script     = new Key().PubKey.WitHash.ScriptPubKey;
                var            miner      = node.FullNode.NodeService <IPowMining>() as PowMining;
                List <uint256> res        = miner.GenerateBlocks(new ReserveScript(script), 1, int.MaxValue);
                Block          block      = node.FullNode.ChainIndexer.GetHeader(res.First()).Block;
                Script         commitment = WitnessCommitmentsRule.GetWitnessCommitment(node.FullNode.Network, block);
                Assert.NotNull(commitment);

                var cancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token;
                TestBase.WaitLoop(() => listener.CreateRPCClient().GetBlockCount() >= 1, cancellationToken: cancellationToken);

                // We need to capture a message on the witness-enabled destination node and see that it contains a block serialised with witness data.
                INetworkPeer connectedPeer = listenerConnMan.ConnectedPeers.FindByEndpoint(node.Endpoint);
                TestBehavior testBehavior  = connectedPeer.Behavior <TestBehavior>();

                var blockMessages = testBehavior.receivedMessageTracker["block"];
                var blockReceived = blockMessages.First();

                var receivedBlock   = blockReceived.Message.Payload as BlockPayload;
                var parsedBlock     = receivedBlock.Obj;
                var nonWitnessBlock = parsedBlock.WithOptions(listener.FullNode.Network.Consensus.ConsensusFactory, TransactionOptions.None);

                Assert.True(parsedBlock.GetSerializedSize() > nonWitnessBlock.GetSerializedSize());
            }
        }
        /// <inheritdoc />
        public void BanPeer(IPEndPoint endpoint, int banTimeSeconds, string reason = null)
        {
            Guard.NotNull(endpoint, nameof(endpoint));
            this.logger.LogTrace("({0}:'{1}',{2}:'{3}')", nameof(endpoint), endpoint, nameof(reason), reason);

            reason = reason ?? "unknown";

            INetworkPeer peer = this.connectionManager.ConnectedPeers.FindByEndpoint(endpoint);

            if (peer != null)
            {
                ConnectionManagerBehavior peerBehavior = peer.Behavior <ConnectionManagerBehavior>();
                if (!peerBehavior.Whitelisted)
                {
                    peer.Disconnect($"The peer was banned, reason: {reason}");
                }
                else
                {
                    this.logger.LogTrace("(-)[WHITELISTED]");
                    return;
                }
            }

            PeerAddress peerAddress = this.peerAddressManager.FindPeer(endpoint);

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

            peerAddress.BanTimeStamp = this.dateTimeProvider.GetUtcNow();
            peerAddress.BanUntil     = this.dateTimeProvider.GetUtcNow().AddSeconds(banTimeSeconds);
            peerAddress.BanReason    = reason;

            this.logger.LogDebug("Peer '{0}' banned for reason '{1}', until {2}.", endpoint, reason, peerAddress.BanUntil.ToString());

            this.logger.LogTrace("(-)");
        }
Beispiel #13
0
        /// <summary>
        /// Processes "getheaders" message received from the peer.
        /// </summary>
        /// <param name="peer">Peer from which the message was received.</param>
        /// <param name="getHeadersPayload">Payload of "getheaders" message to process.</param>
        /// <remarks>
        /// "getheaders" message is sent by the peer in response to "headers" message until an empty array is received.
        /// <para>
        /// This payload notifies peers of our current best validated height, which is held by consensus tip.
        /// </para>
        /// <para>
        /// If the peer is behind/equal to our best height an empty array is sent back.
        /// </para>
        /// </remarks>
        protected async Task ProcessGetHeadersAsync(INetworkPeer peer, GetHeadersPayload getHeadersPayload)
        {
            if (getHeadersPayload.BlockLocator.Blocks.Count > BlockLocator.MaxLocatorSize)
            {
                this.logger.LogTrace("Peer '{0}' sent getheader with oversized locator, disconnecting.", peer.RemoteSocketEndpoint);

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

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

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

            Payload headersPayload = this.ConstructHeadersPayload(getHeadersPayload, out ChainedHeader lastHeader);

            if (headersPayload != null)
            {
                try
                {
                    this.BestSentHeader = lastHeader;

                    await peer.SendMessageAsync(headersPayload).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    this.logger.LogTrace("Unable to send headers message to peer '{0}'.", peer.RemoteSocketEndpoint);
                }
            }
        }
        /// <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 (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.connectionManager.ConnectionSettings.RelayTxes;

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

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

            var 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
            lock (this.lockObject)
            {
                foreach (InventoryVector inventoryVector in send.Inventory)
                {
                    this.filterInventoryKnown.Add(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>
 /// Determines whether the specified peer is Whitelisted.
 /// </summary>
 /// <param name="peer">The peer.</param>
 /// <returns>
 ///   <c>true</c> if the specified peer is Whitelisted; otherwise, <c>false</c>.
 /// </returns>
 private bool IsPeerWhitelisted(INetworkPeer peer)
 {
     return(peer.Behavior <IConnectionManagerBehavior>()?.Whitelisted == true);
 }
        public void MustNotAnnounceABlock_WhenNotInBestChain()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                CoreNode stratisNodeSync = builder.CreateStratisPowNode(this.network).WithWallet().Start();
                CoreNode stratisNode1    = builder.CreateStratisPowNode(this.network).WithWallet().Start();
                CoreNode stratisNode2    = builder.CreateStratisPowNode(this.network).Start();

                // Start up sync node and mine chain0
                TestHelper.MineBlocks(stratisNodeSync, 10);

                // 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");
                            }
                        }
                    }
                }
            }
        }
        public void CheckBlocksAnnounced_AndQueueEmptiesOverTime_ForMultiplePeers_WhenOneIsDisconnected()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                CoreNode stratisNodeSync = builder.CreateStratisPowNode(this.network).WithWallet().Start();

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

                TestHelper.MineBlocks(stratisNodeSync, 10);

                // 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);
            }
        }
Beispiel #18
0
        public void CheckSegwitP2PSerialisationForNonWitnessNode()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                // We have to name the networks differently because the NBitcoin network registration won't allow two identical networks to coexist otherwise.
                var network = new StraxRegTest();
                network.SetPrivatePropertyValue("Name", "StraxRegTestWithDeployments");
                Assert.NotNull(network.Consensus.BIP9Deployments[2]);

                var networkNoBIP9 = new StraxRegTest();
                networkNoBIP9.SetPrivatePropertyValue("Name", "StraxRegTestWithoutDeployments");
                Assert.NotNull(networkNoBIP9.Consensus.BIP9Deployments[2]);

                // Remove BIP9 deployments (i.e. segwit).
                for (int i = 0; i < networkNoBIP9.Consensus.BIP9Deployments.Length; i++)
                {
                    networkNoBIP9.Consensus.BIP9Deployments[i] = null;
                }

                // Ensure the workaround had the desired effect.
                Assert.Null(networkNoBIP9.Consensus.BIP9Deployments[2]);
                Assert.NotNull(network.Consensus.BIP9Deployments[2]);

                // Explicitly use new & separate instances of StratisRegTest because we modified the BIP9 deployments on one instance.
                CoreNode node     = builder.CreateStratisPosNode(network).Start();
                CoreNode listener = builder.CreateStratisPosNode(networkNoBIP9).Start();

                // Sanity check.
                Assert.Null(listener.FullNode.Network.Consensus.BIP9Deployments[2]);
                Assert.NotNull(node.FullNode.Network.Consensus.BIP9Deployments[2]);

                // By disabling Segwit on the listener node we also prevent the WitnessCommitments rule from rejecting the mining node's blocks once we modify the listener's peer services.

                IConnectionManager listenerConnMan = listener.FullNode.NodeService <IConnectionManager>();
                listenerConnMan.Parameters.TemplateBehaviors.Add(new TestBehavior());

                // Override the listener node's default settings, so that it will not ask for witness data in P2P messages.
                listenerConnMan.Parameters.Services &= ~NetworkPeerServices.NODE_WITNESS;

                TestHelper.Connect(listener, node);

                // Mine a Segwit block on the first node. It should have commitment data as its settings have not been modified.
                var            script     = new Key().PubKey.WitHash.ScriptPubKey;
                var            miner      = node.FullNode.NodeService <IPowMining>() as PowMining;
                List <uint256> res        = miner.GenerateBlocks(new ReserveScript(script), 1, int.MaxValue);
                Block          block      = node.FullNode.ChainIndexer.GetHeader(res.First()).Block;
                Script         commitment = WitnessCommitmentsRule.GetWitnessCommitment(node.FullNode.Network, block);
                Assert.NotNull(commitment);

                // The listener should sync the mined block without validation failures.
                var cancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token;
                TestBase.WaitLoop(() => listener.CreateRPCClient().GetBlockCount() >= 1, cancellationToken: cancellationToken);

                // We need to capture a message on the non-witness-enabled destination node and see that it contains a block serialised without witness data.
                INetworkPeer connectedPeer = listenerConnMan.ConnectedPeers.FindByEndpoint(node.Endpoint);
                TestBehavior testBehavior  = connectedPeer.Behavior <TestBehavior>();

                var blockMessages = testBehavior.receivedMessageTracker["block"];
                var blockReceived = blockMessages.First();

                var receivedBlock = blockReceived.Message.Payload as BlockPayload;
                var parsedBlock   = receivedBlock.Obj;

                // The block mined on the mining node (witness) should be bigger than the one received by the listener (no witness).
                Assert.True(block.GetSerializedSize() > parsedBlock.GetSerializedSize());

                // Reserialise the received block without witness data (this should have no effect on its size).
                var nonWitnessBlock = parsedBlock.WithOptions(listener.FullNode.Network.Consensus.ConsensusFactory, TransactionOptions.None);

                // We received a block without witness data in the first place.
                Assert.True(parsedBlock.GetSerializedSize() == nonWitnessBlock.GetSerializedSize());
            }
        }
Beispiel #19
0
 public static bool IsWhitelisted(this INetworkPeer peer)
 {
     return(peer.Behavior <IConnectionManagerBehavior>()?.Whitelisted == true);
 }
        /// <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)
        {
            this.logger.LogTrace("({0}:'{1}',{2}:'{3}')", nameof(peer), peer.RemoteSocketEndpoint, nameof(getBlocksPayload), getBlocksPayload);

            // 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.chain.GetBlock(this.blockRepository.BlockHash);

            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)
            {
                var           chainBehavior = peer.Behavior <ChainHeadersBehavior>();
                ChainedHeader peerTip       = chainBehavior.PendingTip;

                int peersHeight = peerTip != null ? peerTip.Height : 0;
                if (peersHeight < lastAddedChainedHeader.Height)
                {
                    this.logger.LogTrace("Setting peer's pending tip to '{0}'.", lastAddedChainedHeader);
                    chainBehavior.SetPendingTip(lastAddedChainedHeader);

                    // 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.");
            }

            this.logger.LogTrace("(-)");
        }
        /// <inheritdoc />
        public async Task AnnounceBlocksAsync(List <ChainedHeader> blocksToAnnounce)
        {
            Guard.NotNull(blocksToAnnounce, nameof(blocksToAnnounce));
            this.logger.LogTrace("({0}.{1}:{2})", nameof(blocksToAnnounce), nameof(blocksToAnnounce.Count), blocksToAnnounce.Count);

            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 > MAX_BLOCKS_TO_ANNOUNCE);

            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 <uint256>();

            try
            {
                var           chainBehavior = peer.Behavior <ChainHeadersBehavior>();
                ChainedHeader bestIndex     = null;
                if (!revertToInv)
                {
                    bool foundStartingHeader = false;
                    // 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 (chainBehavior.PendingTip?.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 (chainBehavior.PendingTip?.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);
                        }

                        chainBehavior.SetPendingTip(bestIndex);
                        await peer.SendMessageAsync(new HeadersPayload(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 ((chainBehavior.PendingTip == null) || (chainBehavior.PendingTip.GetAncestor(chainedHeader.Height) == null))
                            {
                                inventoryBlockToSend.Add(chainedHeader.HashBlock);
                                this.logger.LogDebug("Sending inventory hash '{0}' to peer '{1}'.", chainedHeader.HashBlock, peer.RemoteSocketEndpoint);
                            }
                        }
                    }
                }

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

                    this.logger.LogTrace("(-)[SEND_INVENTORY]");
                    return;
                }
            }
            catch (OperationCanceledException)
            {
                this.logger.LogTrace("(-)[CANCELED_EXCEPTION]");
                return;
            }

            this.logger.LogTrace("(-)");
        }
Beispiel #22
0
        public void SegwitWalletTransactionBuildingAndPropagationTest()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                CoreNode node     = builder.CreateStratisPosNode(KnownNetworks.StraxRegTest).WithWallet().Start();
                CoreNode listener = builder.CreateStratisPosNode(KnownNetworks.StraxRegTest).WithWallet().Start();

                IConnectionManager listenerConnMan = listener.FullNode.NodeService <IConnectionManager>();
                listenerConnMan.Parameters.TemplateBehaviors.Add(new TestBehavior());

                // The listener node will have default settings, i.e. it should ask for witness data in P2P messages.
                Assert.True(listenerConnMan.Parameters.Services.HasFlag(NetworkPeerServices.NODE_WITNESS));

                TestHelper.Connect(listener, node);

                var mineAddress = node.FullNode.WalletManager().GetUnusedAddress();

                var miner = node.FullNode.NodeService <IPowMining>() as PowMining;
                miner.GenerateBlocks(new ReserveScript(mineAddress.ScriptPubKey), (ulong)(node.FullNode.Network.Consensus.CoinbaseMaturity + 1), int.MaxValue);

                // Wait for listener to sync to the same block height so that it won't reject the coinbase spend as being premature.
                TestHelper.WaitForNodeToSync(node, listener);

                // Send a transaction from first node to itself so that it has a proper segwit input to spend.
                var destinationAddress = node.FullNode.WalletManager().GetUnusedAddress();
                var witAddress         = destinationAddress.Bech32Address;

                IActionResult transactionResult = node.FullNode.NodeController <WalletController>()
                                                  .BuildTransaction(new BuildTransactionRequest
                {
                    AccountName      = "account 0",
                    AllowUnconfirmed = true,
                    Recipients       = new List <RecipientModel> {
                        new RecipientModel {
                            DestinationAddress = witAddress, Amount = Money.Coins(1).ToString()
                        }
                    },
                    Password   = node.WalletPassword,
                    WalletName = node.WalletName,
                    FeeAmount  = Money.Coins(0.001m).ToString()
                }).GetAwaiter().GetResult();

                var walletBuildTransactionModel = (WalletBuildTransactionModel)(transactionResult as JsonResult)?.Value;

                node.FullNode.NodeController <WalletController>().SendTransaction(new SendTransactionRequest(walletBuildTransactionModel.Hex));

                Transaction witFunds = node.FullNode.Network.CreateTransaction(walletBuildTransactionModel.Hex);
                uint        witIndex = witFunds.Outputs.AsIndexedOutputs().First(o => o.TxOut.ScriptPubKey.IsScriptType(ScriptType.P2WPKH)).N;

                TestBase.WaitLoop(() => listener.CreateRPCClient().GetBlockCount() >= 1, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);
                TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);
                TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);

                INetworkPeer connectedPeer = listenerConnMan.ConnectedPeers.FindByEndpoint(node.Endpoint);
                TestBehavior testBehavior  = connectedPeer.Behavior <TestBehavior>();

                miner.GenerateBlocks(new ReserveScript(mineAddress.ScriptPubKey), 1, int.MaxValue);

                TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length == 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);
                TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length == 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);

                // Make sure wallet is synced.
                TestBase.WaitLoop(() => node.CreateRPCClient().GetBlockCount() == node.FullNode.WalletManager().LastBlockHeight(), cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);

                // We need to capture a message on the witness-enabled destination node and see that it contains a transaction serialised with witness data.
                // However, the first transaction has no witness data since it was only being sent to a segwit scriptPubKey (i.e. no witness input data).
                // So clear all messages for now.
                testBehavior.receivedMessageTracker.Clear();

                // Send a transaction that has a segwit input, to a segwit address.
                transactionResult = node.FullNode.NodeController <WalletController>()
                                    .BuildTransaction(new BuildTransactionRequest
                {
                    AccountName      = "account 0",
                    AllowUnconfirmed = true,
                    Outpoints        = new List <OutpointRequest>()
                    {
                        new OutpointRequest()
                        {
                            Index = (int)witIndex, TransactionId = witFunds.GetHash().ToString()
                        }
                    },
                    Recipients = new List <RecipientModel> {
                        new RecipientModel {
                            DestinationAddress = witAddress, Amount = Money.Coins(0.5m).ToString()
                        }
                    },
                    Password   = node.WalletPassword,
                    WalletName = node.WalletName,
                    FeeAmount  = Money.Coins(0.001m).ToString()
                }).GetAwaiter().GetResult();

                walletBuildTransactionModel = (WalletBuildTransactionModel)(transactionResult as JsonResult)?.Value;

                node.FullNode.NodeController <WalletController>().SendTransaction(new SendTransactionRequest(walletBuildTransactionModel.Hex));

                TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);
                TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);

                var txMessages = testBehavior.receivedMessageTracker["tx"];
                var txMessage  = txMessages.First();

                var receivedTransaction   = txMessage.Message.Payload as TxPayload;
                var parsedTransaction     = receivedTransaction.Obj;
                var nonWitnessTransaction = parsedTransaction.WithOptions(TransactionOptions.None, listener.FullNode.Network.Consensus.ConsensusFactory);

                Assert.True(parsedTransaction.GetSerializedSize() > nonWitnessTransaction.GetSerializedSize());

                miner.GenerateBlocks(new ReserveScript(mineAddress.ScriptPubKey), 1, int.MaxValue);

                TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length == 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);
                TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length == 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);
            }
        }