private void ProcessHeadersMessage(BitcoinEndpoint endpoint, HeadersMessage message)
        {
            List<StoredBlock> storedBlocks = node.Blockchain.AddHeaders(message.Headers);

            //todo: save blockLocator per node between requests?
            BlockLocator blockLocator = new BlockLocator();

            //todo: what should happen if message contains headers from different branchas?
            bool hasSavedHeaders = false;
            foreach (StoredBlock block in storedBlocks)
            {
                if (block.Height >= 0)
                {
                    //todo: this code assumes that blocks in storedBlocks collections are in order of ascending height and belong to one branch
                    blockLocator.AddHash(block.Height, block.Hash);
                    hasSavedHeaders = true;
                }
            }

            //todo: review this condition
            if (hasSavedHeaders)
            {
                RequestHeaders(endpoint, blockLocator);
            }
        }
        public void OnNodeConnected(BitcoinEndpoint endpoint)
        {
            BlockLocator locator = null;

            lock (lockObject)
            {
                endpoints.Add(endpoint, new EndpointState(endpoint));
                if (requiredBlocks.Any())
                {
                    int minRequiredBlockHeight = requiredBlocks.Values.Min(b => b.Height);
                    int locatorHeight = minRequiredBlockHeight - 1;
                    List<StoredBlock> parentBlocks = node.Blockchain.GetBlocksByHeight(BlockLocator.GetRequiredBlockHeights(locatorHeight));
                    locator = new BlockLocator();
                    foreach (StoredBlock block in parentBlocks)
                    {
                        locator.AddHash(block.Height, block.Hash);
                    }
                }
            }

            if (locator != null)
            {
                endpoint.WriteMessage(new GetBlocksMessage(endpoint.ProtocolVersion, locator.GetHashes(), new byte[32]));
            }
        }
 public void ProcessMessage(BitcoinEndpoint endpoint, IBitcoinMessage message)
 {
     HeadersMessage headersMessage = message as HeadersMessage;
     if (headersMessage != null)
     {
         ProcessHeadersMessage(endpoint, headersMessage);
     }
 }
        public void OnNodeDisconnected(BitcoinEndpoint endpoint)
        {
            //todo: add test and maybe catch exceptions

            //todo: make sure this method is called only after OnNodeConnected

            foreach (NodeServiceInfo serviceInfo in services)
            {
                serviceInfo.Service.OnNodeDisconnected(endpoint);
            }
        }
        public void OnNodeConnected(BitcoinEndpoint endpoint)
        {
            //todo: add test and maybe catch exceptions

            //todo: make sure this method is called before any messages are received by service

            foreach (NodeServiceInfo serviceInfo in services)
            {
                serviceInfo.Service.OnNodeConnected(endpoint);
            }
        }
        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 OnNodeDisconnected(BitcoinEndpoint endpoint)
 {
     lock (lockObject)
     {
         EndpointState endpointState;
         if (!endpoints.TryGetValue(endpoint, out endpointState))
         {
             return;
         }
         endpoints.Remove(endpoint);
         foreach (BlockRequest blockRequest in endpointState.BlockRequests.Values)
         {
             BlockState blockState = blockRequest.BlockState;
             blockState.Requests.RemoveAll(r => r.Endpoint == endpoint);
         }
     }
 }
        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);
            }
        }
Пример #9
0
 private void SendKnownAddresses(BitcoinEndpoint endpoint)
 {
     List<NetAddr> addresses = new List<NetAddr>();
     List<NodeConnection> currentConnections = connectionCollection.GetConnections();
     foreach (NodeConnection connection in currentConnections)
     {
         //todo: filter loopback addresses
         NetAddr addr = new NetAddr(
             //todo: use last message date instead
             (uint) connection.Endpoint.PeerInfo.VersionMessage.Timestamp,
             connection.Endpoint.PeerInfo.VersionMessage.Services,
             endpoint.PeerInfo.IpEndpoint.Address,
             (ushort) endpoint.PeerInfo.IpEndpoint.Port);
         addresses.Add(addr);
     }
     AddrMessage addrMessage = new AddrMessage(addresses.Take(AddrMessage.MaxAddressesPerMessage).ToArray());
     endpoint.WriteMessage(addrMessage);
 }
 private static void RequestHeaders(BitcoinEndpoint endpoint, BlockLocator blockLocator)
 {
     endpoint.WriteMessage(new GetHeadersMessage(endpoint.ProtocolVersion, blockLocator.GetHashes(), new byte[32]));
 }
Пример #11
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);
        }
 public void OnNodeConnected(BitcoinEndpoint endpoint)
 {
     BlockLocator blockLocator = node.Blockchain.GetBlockLocator();
     //todo: check if remote node provides this service?
     RequestHeaders(endpoint, blockLocator);
 }
 public void ProcessMessage(BitcoinEndpoint endpoint, IBitcoinMessage message)
 {
     //todo: add test for this method
 }
        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 EndpointState(BitcoinEndpoint endpoint)
 {
     Endpoint = endpoint;
     AdvertisedBlocks = new LinkedDictionary<byte[], DateTime>(ByteArrayComparer.Instance);
     BlockRequests = new Dictionary<byte[], BlockRequest>(ByteArrayComparer.Instance);
 }
 public void ProcessMessage(BitcoinEndpoint endpoint, IBitcoinMessage message)
 {
     //todo: process AddrMessage
 }
 public void OnNodeConnected(BitcoinEndpoint endpoint)
 {
     //todo: remember node?
 }
Пример #18
0
 public NodeConnection(NodeConnectionDirection direction, BitcoinEndpoint endpoint)
 {
     this.direction = direction;
     this.endpoint = endpoint;
 }
        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 SendDeadlockTestSequence(BitcoinEndpoint client, string peerName)
        {
            byte[][] locatorHashes = new byte[256][];
            for (int i = 0; i < locatorHashes.Length; i++)
            {
                locatorHashes[i] = new byte[32];
            }

            IBitcoinMessage pingMessage = new PingMessage(0);

            IBitcoinMessage bigMessage = new GetBlocksMessage(client.ProtocolVersion, locatorHashes, new byte[32]);
            byte[] messageText = BitcoinStreamWriter.GetBytes(bigMessage.Write);
            Console.WriteLine("{0}: message length is {1}", peerName, messageText.Length);

            Console.WriteLine("{0}: sequence started", peerName);
            for (int i = 0; i < 16; i++)
            {
                client.WriteMessage(bigMessage);
                client.WriteMessage(pingMessage);
            }
            Console.WriteLine("{0}: sequence finished", peerName);
        }
        private bool LoggingMessageHandler(BitcoinEndpoint endpoint, IBitcoinMessage message)
        {
            Console.WriteLine(">>command: {0}", message.Command);

            if (message is InvMessage)
            {
                InvMessage invMessage = (InvMessage) message;
                foreach (InventoryVector vector in invMessage.Inventory)
                {
                    Console.WriteLine("\t{0} {1}", vector.Type.ToString().PadRight(16), BitConverter.ToString(vector.Hash));
                }
            }

            if (message is GetHeadersMessage)
            {
                GetHeadersMessage getHeadersMessage = (GetHeadersMessage) message;

                Console.WriteLine("\tprotocol: {0}", getHeadersMessage.ProtocolVersion);
                foreach (byte[] hash in getHeadersMessage.LocatorHashes)
                {
                    Console.WriteLine("\t{0}", BitConverter.ToString(hash));
                }
                Console.WriteLine("\t{0} (stop)", BitConverter.ToString(getHeadersMessage.HashStop));

                endpoint.WriteMessage(new InvMessage(new InventoryVector[]
                {
                    new InventoryVector(InventoryVectorType.MsgBlock, blockHash1)
                }));
            }

            if (message is GetDataMessage)
            {
                GetDataMessage getDataMessage = (GetDataMessage) message;
                foreach (InventoryVector vector in getDataMessage.Inventory)
                {
                    Console.WriteLine("\t{0} {1}", vector.Type.ToString().PadRight(16), BitConverter.ToString(vector.Hash));
                }
            }

            if (message is BlockMessage)
            {
                BlockMessage blockMessage = (BlockMessage) message;
                foreach (Tx tx in blockMessage.Transactions)
                {
                    Console.WriteLine("\tInputs:");
                    foreach (TxIn input in tx.Inputs)
                    {
                        Console.WriteLine("\t\t{0} seq. {1:X}", BitConverter.ToString(input.PreviousOutput.Hash), input.Sequence);
                    }
                    Console.WriteLine("\tOutputs:");
                    foreach (TxOut output in tx.Outputs)
                    {
                        Console.WriteLine("\t\t{0}, script: {1}", output.Value, BitConverter.ToString(output.PubkeyScript));
                    }
                }
            }
            if (message is MerkleBlockMessage)
            {
                MerkleBlockMessage merkleBlockMessage = (MerkleBlockMessage) message;

                BitArray flagsBitArray = new BitArray(merkleBlockMessage.Flags);
                StringBuilder flagsSb = new StringBuilder();
                foreach (bool flag in flagsBitArray)
                {
                    flagsSb.Append(flag ? "1" : "0");
                }

                Console.WriteLine("\tTotalTransactions: {0}", merkleBlockMessage.TotalTransactions);
                Console.WriteLine("\tFlags:             {0}", flagsSb);

                foreach (byte[] hash in merkleBlockMessage.Hashes)
                {
                    Console.WriteLine("\t\t{0}", BitConverter.ToString(hash));
                }
            }

            if (message is BitcoinMessage)
            {
                BitcoinMessage rawMessage = (BitcoinMessage) message;
                Console.WriteLine(">>payload: {0}", BitConverter.ToString(rawMessage.Payload));
            }

            return true;
        }
 private bool DeadlockTestMessageHandler(BitcoinEndpoint endpoint, IBitcoinMessage message)
 {
     Thread.Sleep(100);
     return true;
 }
 public void OnNodeDisconnected(BitcoinEndpoint endpoint)
 {
 }
Пример #24
0
        private bool HandleMessage(BitcoinEndpoint endpoint, IBitcoinMessage message)
        {
            if (message is AddrMessage)
            {
                SaveReceivedAddresses((AddrMessage) message);
                return true;
            }
            //todo: send GetAddr message sometimes
            //todo: see 0.10.1 Change log: Ignore getaddr messages on Outbound connections.
            if (message is GetAddrMessage)
            {
                SendKnownAddresses(endpoint);
                return true;
            }

            services.ProcessMessage(endpoint, message);
            //todo: handle getaddr message
            //todo: implement
            return true;
        }
 public BlockRequest(BlockState blockState, BitcoinEndpoint endpoint, DateTime requestDate)
 {
     this.BlockState = blockState;
     this.Endpoint = endpoint;
     this.RequestDate = requestDate;
 }
 public void OnNodeDisconnected(BitcoinEndpoint endpoint)
 {
     //todo: test?
 }
 public void ProcessMessage(BitcoinEndpoint endpoint, IBitcoinMessage message)
 {
     //todo: add test and catch maybe catch other exceptions
     try
     {
         foreach (NodeServiceInfo serviceInfo in services)
         {
             serviceInfo.Service.ProcessMessage(endpoint, message);
         }
     }
     catch (BitcoinProtocolViolationException e)
     {
         logger.Error(e, "Remote node violated protecol rules ({0}).", endpoint.PeerInfo.IpEndpoint);
         //todo: is this a correct way to disconnect node?
         //todo: ban node ?
         endpoint.Dispose();
     }
 }
        /// <summary>
        /// Adds a connection to the collection.
        /// <para/>
        /// Can reject the connection if the cancellation token was cancelled or the maximum number of connections is reached.
        /// </summary>
        /// <param name="direction">The direction of the connection.</param>
        /// <param name="endpoint">The connection to a Bitcoin node.</param>
        /// <returns>true if the connection was accepted; otherwise false.</returns>
        public bool Add(NodeConnectionDirection direction, BitcoinEndpoint endpoint)
        {
            NodeConnection connection = new NodeConnection(direction, endpoint);

            lock (lockObject)
            {
                if (!CanRegister(connection))
                {
                    return false;
                }

                if (direction == NodeConnectionDirection.Incoming)
                {
                    incomingConnectionsCount++;
                }
                else if (direction == NodeConnectionDirection.Outgoing)
                {
                    outgoingConnectionsCount++;
                }
                else
                {
                    throw new InvalidOperationException($"Unexpected connection direction: {direction}.");
                }

                connections.Add(connection);
            }

            endpoint.CallWhenDisconnected(() => Remove(connection));

            Changed?.Invoke();

            return true;
        }
        public void ProcessMessage(BitcoinEndpoint endpoint, IBitcoinMessage message)
        {
            EndpointState endpointState;

            lock (lockObject)
            {
                if (!endpoints.TryGetValue(endpoint, out endpointState))
                {
                    //todo: if node still sends us a required block it should probably be saved
                    // endpoint is either disconnected or not not fit for this service
                    return;
                }
            }

            var invMessage = message as InvMessage;
            if (invMessage != null)
            {
                ProcessInvMessage(endpointState, invMessage);
            }

            var blockMessage = message as BlockMessage;
            if (blockMessage != null)
            {
                ProcessBlockMessage(blockMessage);
            }
        }
Пример #30
0
        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);
        }