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); } }
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])); }
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? }
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) { }
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); } }
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); }