public void TestClient()
        {
            //todo: test ping deadlock

            using (BitcoinEndpoint endpoint = new BitcoinEndpoint(LoggingMessageHandler))
            {
                endpoint.Connect("localhost", 8333);

                //endpoint.WriteMessage(new GetBlocksMessage(endpoint.ProtocolVersion, new byte[][] {blockHash0}, blockHash2));
                endpoint.WriteMessage(new GetBlocksMessage(endpoint.ProtocolVersion, new byte[][] {new byte[32]}, new byte[32]));
                Thread.Sleep(500);
                endpoint.WriteMessage(new GetDataMessage(new InventoryVector[] {new InventoryVector(InventoryVectorType.MsgBlock, blockHash1)}));

                Thread.Sleep(30000);
            }
        }
        public void TestDeadlock()
        {
            AutoResetEvent finishedEvent = new AutoResetEvent(false);
            using (BitcoinConnectionListener server = new BitcoinConnectionListener(IPAddress.Loopback, 28333, DeadlockTestConnectionHandler))
            {
                server.Start();

                Exception threadException = null;

                Thread thread = new Thread(() =>
                {
                    try
                    {
                        using (BitcoinEndpoint client = new BitcoinEndpoint(DeadlockTestMessageHandler))
                        {
                            client.Connect("localhost", 28333);
                            SendDeadlockTestSequence(client, "client");
                            //let other peer to send remaining data
                            Thread.Sleep(1000);
                        }
                    }
                    catch (Exception ex)
                    {
                        threadException = ex;
                    }
                    finally
                    {
                        finishedEvent.Set();
                    }
                });
                thread.Name = "Test Sending Thread";
                thread.IsBackground = true;
                thread.Start();
                Assert.That(finishedEvent.WaitOne(5000), Is.True);
                Assert.That(threadException, Is.Null);
            }
        }
        private void DeadlockTestConnectionHandler(BitcoinConnection connection)
        {
            using (BitcoinEndpoint endpoint = new BitcoinEndpoint(DeadlockTestMessageHandler))
            {
                endpoint.Connect(connection);

                SendDeadlockTestSequence(endpoint, "server");

                //let other peer to send remaining data
                Thread.Sleep(1000);
            }
        }
        private void ConnectionHandler(BitcoinConnection connection)
        {
            using (BitcoinEndpoint endpoint = new BitcoinEndpoint(LoggingMessageHandler))
            {
                endpoint.Connect(connection);

                Thread.Sleep(1000);
                endpoint.WriteMessage(new BitcoinMessage(InvMessage.Command, new byte[0]));
            }
        }
        public void ConnectTo(NodeAddress address)
        {
            if (cancellationTokenSource.IsCancellationRequested)
            {
                return;
            }

            //todo: The construction of BitcoinEndpoint and BitcoinConnection is confusing. Both can use host and port.
            BitcoinEndpoint endpoint = new BitcoinEndpoint(HandleMessage);
            try
            {
                endpoint.Connect(address.Address.ToString(), address.Port);
            }
            catch (BitcoinNetworkException)
            {
                addressCollection.Reject(address);
                //todo: log error?
                return;
            }

            //todo: check nonce to avoid connection to self
            //todo: check if peer is banned
            //todo: send ping messages to check if connection is alive

            if ((endpoint.PeerInfo.VersionMessage.Services & VersionMessage.ServiceNodeNetwork) == 0)
            {
                // outgoing connections ignore non-full nodes
                addressCollection.Reject(address);
                endpoint.Dispose();
                return;
            }

            addressCollection.Confirm(address);

            if (!connectionCollection.Add(NodeConnectionDirection.Outgoing, endpoint))
            {
                endpoint.Dispose();
                return;
            }

            services.OnNodeConnected(endpoint);
            //todo: make sure that OnNodeConnected is always called before OnNodeDisconnected and before message processing
            endpoint.Disconnected += () => services.OnNodeDisconnected(endpoint);
        }
        private void HandleIncomingConnection(BitcoinConnection connection)
        {
            BitcoinEndpoint endpoint = new BitcoinEndpoint(HandleMessage);
            endpoint.Connect(connection);

            if (!connectionCollection.Add(NodeConnectionDirection.Incoming, endpoint))
            {
                endpoint.Dispose();
                return;
            }

            services.OnNodeConnected(endpoint);
            //todo: make sure that OnNodeConnected is always called before OnNodeDisconnected and before message processing
            endpoint.Disconnected += () => services.OnNodeDisconnected(endpoint);
        }