private async Task<bool> ConnectAndHandshake(RemoteNode remoteNode, bool isIncoming) { // wire node WireNode(remoteNode); // connect await remoteNode.ConnectAsync(); if (remoteNode.IsConnected) { //TODO RemoteNode ignore; this.pendingPeers.TryRemove(remoteNode.RemoteEndPoint, out ignore); this.connectedPeers.TryAdd(remoteNode.RemoteEndPoint, remoteNode); // setup task to wait for verack var verAckTask = remoteNode.Receiver.WaitForMessage(x => x.Command == "verack", HANDSHAKE_TIMEOUT_MS); // setup task to wait for version var versionTask = remoteNode.Receiver.WaitForMessage(x => x.Command == "version", HANDSHAKE_TIMEOUT_MS); // start listening for messages after tasks have been setup remoteNode.Receiver.Listen(); // send our local version var nodeId = (((UInt64)random.Next()) << 32) + (UInt64)random.Next(); //TODO should be generated and verified on version message var currentBlockchainLocal = this.blockchainDaemon.CurrentBlockchain; var currentHeight = !currentBlockchainLocal.IsDefault ? (UInt32)currentBlockchainLocal.Height : 0; await remoteNode.Sender.SendVersion(Messaging.GetExternalIPEndPoint(), remoteNode.RemoteEndPoint, nodeId, currentHeight); // wait for our local version to be acknowledged by the remote peer // wait for remote peer to send their version await Task.WhenAll(verAckTask, versionTask); //TODO shouldn't have to decode again var versionMessage = versionTask.Result; var versionPayload = NetworkEncoder.DecodeVersionPayload(versionMessage.Payload.ToArray().ToMemoryStream(), versionMessage.Payload.Length); var remoteAddressWithTime = new NetworkAddressWithTime ( Time: DateTime.UtcNow.ToUnixTime(), NetworkAddress: new NetworkAddress ( Services: versionPayload.LocalAddress.Services, IPv6Address: versionPayload.LocalAddress.IPv6Address, Port: versionPayload.LocalAddress.Port ) ); if (!isIncoming) this.knownAddressCache.UpdateValue(remoteAddressWithTime.GetKey(), remoteAddressWithTime); // acknowledge their version await remoteNode.Sender.SendVersionAcknowledge(); return true; } else { return false; } }
private void OnPing(RemoteNode remoteNode, ImmutableArray<byte> payload) { remoteNode.Sender.SendMessageAsync(Messaging.ConstructMessage("pong", payload.ToArray())).Wait(); }
private void OnDisconnect(RemoteNode remoteNode) { DisconnectPeer(remoteNode.RemoteEndPoint, null); }
private void OnGetBlocks(RemoteNode remoteNode, GetBlocksPayload payload) { //TODO }
private void OnGetHeaders(RemoteNode remoteNode, GetBlocksPayload payload) { // if in comparison mode, synchronize all work before returning current headers if (this.Type == LocalClientType.ComparisonToolTestNet) this.blockchainDaemon.WaitForFullUpdate(); var currentBlockchainLocal = this.blockchainDaemon.CurrentBlockchain; var blockHeaders = new List<BlockHeader>(currentBlockchainLocal.BlockCount); foreach (var chainedBlock in currentBlockchainLocal.BlockList) { BlockHeader blockHeader; if (this.blockchainDaemon.CacheContext.BlockHeaderCache.TryGetValue(chainedBlock.BlockHash, out blockHeader)) { blockHeaders.Add(blockHeader); } else { Debugger.Break(); Debug.WriteLine("Couldn't generate getheaders response"); return; } } var payloadStream = new MemoryStream(); using (var payloadWriter = new BinaryWriter(payloadStream)) { payloadWriter.WriteVarInt((UInt64)blockHeaders.Count); foreach (var blockHeader in blockHeaders) { NetworkEncoder.EncodeBlockHeader(payloadStream, blockHeader); payloadWriter.WriteVarInt(0); } } remoteNode.Sender.SendMessageAsync(Messaging.ConstructMessage("headers", payloadStream.ToArray())).Wait(); }
private void UnwireNode(RemoteNode remoteNode) { remoteNode.Receiver.OnMessage -= OnMessage; remoteNode.Receiver.OnInventoryVectors -= OnInventoryVectors; remoteNode.Receiver.OnBlock -= OnBlock; remoteNode.Receiver.OnBlockHeader -= OnBlockHeader; remoteNode.Receiver.OnReceivedAddresses -= OnReceivedAddresses; remoteNode.OnGetBlocks -= OnGetBlocks; remoteNode.OnGetHeaders -= OnGetHeaders; remoteNode.OnPing -= OnPing; remoteNode.OnDisconnect -= OnDisconnect; }
private Task RequestBlock(RemoteNode remoteNode, UInt256 blockHash) { if (this.blockchainDaemon.CacheContext.BlockCache.ContainsKey(blockHash)) return null; var now = DateTime.UtcNow; var newRequestTime = Tuple.Create(now, (DateTime?)null); // check if block has already been requested if (this.requestedBlockTimes.TryAdd(blockHash, newRequestTime)) { this.requestedBlocks.TryAdd(blockHash); var invVectors = ImmutableArray.Create<InventoryVector>(new InventoryVector(InventoryVector.TYPE_MESSAGE_BLOCK, blockHash)); return remoteNode.Sender.SendGetData(invVectors); } else { // if block has already been requested, check if the request is old enough to send again Tuple<DateTime, DateTime?> lastRequestTime; if (this.requestedBlockTimes.TryGetValue(blockHash, out lastRequestTime)) { if ((now - lastRequestTime.Item1) > TimeSpan.FromSeconds(15)) { this.requestedBlocks.TryAdd(blockHash); this.requestedBlockTimes.AddOrUpdate(blockHash, newRequestTime, (existingKey, existingValue) => newRequestTime); var invVectors = ImmutableArray.Create<InventoryVector>(new InventoryVector(InventoryVector.TYPE_MESSAGE_BLOCK, blockHash)); return remoteNode.Sender.SendGetData(invVectors); } } } return null; }
private void WireNode(RemoteNode remoteNode) { remoteNode.Receiver.OnMessage += OnMessage; remoteNode.Receiver.OnInventoryVectors += OnInventoryVectors; remoteNode.Receiver.OnBlock += OnBlock; remoteNode.Receiver.OnBlockHeader += OnBlockHeader; remoteNode.Receiver.OnReceivedAddresses += OnReceivedAddresses; remoteNode.OnGetBlocks += OnGetBlocks; remoteNode.OnGetHeaders += OnGetHeaders; remoteNode.OnPing += OnPing; remoteNode.OnDisconnect += OnDisconnect; }
private async Task<RemoteNode> ConnectToPeer(IPEndPoint remoteEndPoint) { try { var remoteNode = new RemoteNode(remoteEndPoint); this.unconnectedPeers.TryRemove(remoteEndPoint); this.pendingPeers.TryAdd(remoteNode.RemoteEndPoint, remoteNode); var success = await ConnectAndHandshake(remoteNode, isIncoming: false); if (success) { await PeerStartup(remoteNode); return remoteNode; } else { DisconnectPeer(remoteEndPoint, null); return null; } } catch (Exception e) { //Debug.WriteLine(string.Format("Could not connect to {0}: {1}", remoteEndpoint, e.Message)); DisconnectPeer(remoteEndPoint, e); return null; } }
private async Task SendGetBlocks(RemoteNode remoteNode) { var blockLocatorHashes = CalculateBlockLocatorHashes(this.blockchainDaemon.WinningBlockchain); await remoteNode.Sender.SendGetBlocks(blockLocatorHashes, hashStop: 0); }
private async Task PeerStartup(RemoteNode remoteNode) { await remoteNode.Sender.RequestKnownAddressesAsync(); }
private async void StartListening() { switch (this.Type) { case LocalClientType.MainNet: case LocalClientType.TestNet3: var externalIPAddress = Messaging.GetExternalIPAddress(); var localhost = Dns.GetHostEntry(Dns.GetHostName()); this.listenSocket = new Socket(externalIPAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); this.listenSocket.Bind(new IPEndPoint(localhost.AddressList.Where(x => x.AddressFamily == externalIPAddress.AddressFamily).First(), Messaging.Port)); this.listenSocket.Listen(SERVER_BACKLOG); break; case LocalClientType.ComparisonToolTestNet: this.listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); this.listenSocket.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), Messaging.Port)); this.listenSocket.Listen(SERVER_BACKLOG); break; } try { while (true) { // cooperative loop this.shutdownToken.Token.ThrowIfCancellationRequested(); try { var newSocket = await Task.Factory.FromAsync<Socket>(this.listenSocket.BeginAccept(null, null), this.listenSocket.EndAccept); Task.Run(async () => { var remoteNode = new RemoteNode(newSocket); try { if (await ConnectAndHandshake(remoteNode, isIncoming: true)) { Interlocked.Increment(ref this.incomingCount); } else { DisconnectPeer(remoteNode.RemoteEndPoint, null); } } catch (Exception e) { if (remoteNode.RemoteEndPoint != null) DisconnectPeer(remoteNode.RemoteEndPoint, e); } }).Forget(); } catch (Exception e) { Debug.WriteLine(e.Message); } } } catch (OperationCanceledException) { } this.listenSocket.Dispose(); }