/// <summary> /// Helper method that catches new messages (and handles connection exceptions). /// When an exception occurs, we will disconnect with the peer. /// </summary> /// <param name="node">The node to listen to</param> /// <param name="timeout">The message timeout to maintain. Disconnect when timeout has been exceeded.</param> /// <returns>The message that we received from the node</returns> public async Task <Message> ListenForNewMessage(NetworkNode node, TimeSpan timeout) { try { var msg = await node.ReceiveMessageAsync(timeout); /* * var delaySec = 0; * Random rnd = new Random(); * int delayChance = rnd.Next(1, 100); * if (delayChance > 55 && delayChance < 86) * { * delaySec = rnd.Next(1, 3); * } * else if (delayChance > 85 && delayChance < 96) * { * delaySec = rnd.Next(3, 6); * } * else if (delayChance > 95 && delayChance < 101) * { * delaySec = rnd.Next(7, 13); * } * * await Task.Delay(delaySec * 1000); */ return(msg); } catch (Exception) { await node?.Disconnect(); } return(null); }
public override async Task HandleMessage(NetworkNode node, Message msg) { if (node.HandshakeIsCompleted) { return; } var blockchain = _blockchainRepo.GetChainByNetId(_netId); try { if (node.ConnectionType == ConnectionType.Inbound) { await HandleInboundHandshakeMessage(node, msg, blockchain); } else { await HandleOutboundHandshakeMessage(node, msg, blockchain); } } catch (Exception) { node?.Disconnect(); } }
public void Disconnect() { OnDisconnect?.Invoke(); NetworkNode.Disconnect(NetworkAddress); }
private static void Main(String[] args) { string servers; if (args.Length == 3) { _port = int.Parse(args[0]); servers = args[1]; _mesh = bool.Parse(args[2]); } else { Console.Write("Input a port to run on: "); _port = int.Parse(Console.ReadLine()); Console.Write("Input a comma separated list of servers to connect to: "); servers = Console.ReadLine(); Console.Write("Is this a mesh network or chord network? (\"mesh\" for mesh and \"chord\" for chord): "); string networkType = Console.ReadLine(); if (networkType == "mesh") { _mesh = true; } else if (networkType == "chord") { _mesh = false; } else { Console.WriteLine("Unknown network type."); return; } } if (_mesh) { _node = new MeshNetworkNode("MeshNetworkTester" + _port + ".log", LogLevels.Warning); } else { _node = new ChordNetworkNode("MeshNetworkTester" + _port + ".log", LogLevels.Warning); } _node.ReceivedMessage += node_ReceivedMessage; _node.ConnectToNetwork(_port, servers.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(e => new NodeProperties(e)) .ToList()); var thisMachine = new NodeProperties("localhost", _port); Console.WriteLine("Please enter a command, or type \"help\" for help."); bool running = true; while (running) { Console.Write(" > "); string command = Console.ReadLine(); switch (command) { case "custom": Console.WriteLine("Node to send data to: "); string node = Console.ReadLine(); Console.WriteLine("Data to send: "); string data = Console.ReadLine(); _node.SendMessage(new NodeProperties(node), data); break; case "exit": Console.WriteLine("Shutting down node..."); running = false; _node.Disconnect(); break; case "help": Console.WriteLine("exit: Exits the program\nstatus: Displays the status of the program\nread: Reads a value from the database\nwrite: Writes a value to the database\ncustom: Sends a custom message (warning: this will also output the message on the receiver's side immediately upon receiving it)"); break; case "read": Console.Write("What is the key (integer): "); int key = int.Parse(Console.ReadLine()); string value = null; // If the value is local, read it, otherwise go get it. if (database.ContainsKey(key)) { value = database[key]; } else { if (_node is MeshNetworkNode) { // Search every node for the value. List <MessageResponseResult> results = new List <MessageResponseResult>(); foreach (var neighbor in _node.GetNeighbors()) { results.Add(_node.SendMessageResponse(neighbor, "read" + key)); } foreach (var result in results) { if (result.SendResult == SendResults.Success && result.ResponseResult == ResponseResults.Success) { if (result.ResponseMessage.Data.StartsWith("y")) { value = result.ResponseMessage.Data.Substring(1); break; } } } } else { // Search for the node that contains the value. var result = ((ChordNetworkNode)_node).SendChordMessageResponse(key, "read" + key); if (result.SendResult == SendResults.Success && result.ResponseResult == ResponseResults.Success) { if (result.ResponseMessage.Data.StartsWith("y")) { value = result.ResponseMessage.Data.Substring(1); } } } } if (value == null) { Console.WriteLine("Could not find a value associated with the key " + key + "."); } else { Console.WriteLine("Key: " + key + " Value: " + value); } break; case "status": Console.WriteLine("Currently running on " + thisMachine.IpAddress + ":" + thisMachine.Port); Console.WriteLine(); if (_node is MeshNetworkNode) { var neighbors = _node.GetNeighbors(); Console.WriteLine("Connected Nodes: "); foreach (var neighbor in neighbors) { Console.WriteLine(neighbor.IpAddress + ":" + neighbor.Port); } Console.WriteLine("Currently connected to " + neighbors.Count + " nodes."); } else { var chordNode = (ChordNetworkNode)_node; Console.WriteLine("id: " + chordNode.Id + " Predecessor: " + chordNode.Predecessor + " Successor: " + (chordNode.Successor == null ? "self" : chordNode.Successor.ToString())); Console.WriteLine("Fingers:"); foreach (var finger in chordNode.GetFingers()) { Console.WriteLine(finger.IpAddress + ":" + finger.Port); } } break; case "write": Console.Write("What is the key (integer): "); key = int.Parse(Console.ReadLine()); Console.Write("What is the value (string): "); value = Console.ReadLine(); if (_node is MeshNetworkNode) { // If this is a mesh network, write the value locally. SetValue(key, value); } else { // Find the node that should contain the value and write it there. var dataNode = ((ChordNetworkNode)_node).GetNodeContainingId(key); if (dataNode == null || dataNode.Equals(thisMachine)) { SetValue(key, value); } else { ((ChordNetworkNode)_node).SendChordMessage(key, "write" + key + " " + value); } } break; default: Console.WriteLine("Unknown command."); break; } } }
// todo Chain of Responsibility pattern, make XXMessageHandler class for each command type // and refactor abstractmessagehandler to a regular MessageHandlerHelper public override async Task HandleMessage(NetworkNode node, Message msg) { try { if (msg.Command == NetworkCommand.CloseConn.ToString()) { await node.Disconnect(); } else if (msg.Command == NetworkCommand.GetAddr.ToString()) { // Send our known peers var addresses = new AddrPayload(_nodePool.GetAllRemoteListenEndpoints()); await SendMessageToNode(node, NetworkCommand.Addr, addresses); } else if (msg.Command == NetworkCommand.Addr.ToString()) { // Connect to all neighbors that the other node knows var payload = (AddrPayload)msg.Payload; foreach (IPEndPoint endpoint in payload.Endpoints) { await _networkManager.ConnectToPeer(endpoint); } } else if (msg.Command == NetworkCommand.GetHeaders.ToString()) { // Send our headers var payload = (GetHeadersPayload)msg.Payload; try { var headers = GetBlocksFromHash(payload.HighestHeightHash, payload.StoppingHash, false).Select(b => b.Header); var headersPayload = new HeadersPayload(headers); await SendMessageToNode(node, NetworkCommand.Headers, headersPayload); } catch (KeyNotFoundException) { // Send empty payload await SendMessageToNode(node, NetworkCommand.NotFound, null); } } else if (msg.Command == NetworkCommand.GetBlocks.ToString()) { var payload = (GetBlocksPayload)msg.Payload; var blocksPayload = new StateBlocksPayload(); if (payload.Headers.Count() > 0) { blocksPayload = new StateBlocksPayload(GetBlocksFromHash(payload.Headers.First(), payload.Headers.Last(), true)); } await SendMessageToNode(node, NetworkCommand.Blocks, blocksPayload); } else if (msg.Command == NetworkCommand.Headers.ToString() && node.IsSyncingWithNode) { node.SetSyncStatus(SyncStatus.InProgress); var headersPayload = (HeadersPayload)msg.Payload; if (headersPayload.Headers.Count() == 0) { _logger.LogInformation("Successfully synced with remote node."); node.SetSyncStatus(SyncStatus.Succeeded); return; } // Request these blocks var getBlocksPayload = new GetBlocksPayload(headersPayload.Headers.Select(h => h.Hash)); await SendMessageToNode(node, NetworkCommand.GetBlocks, getBlocksPayload); } else if (msg.Command == NetworkCommand.NotFound.ToString() && node.IsSyncingWithNode) { node.SetSyncStatus(SyncStatus.Failed); // Restart the syncing process with another node. } else if (msg.Command == NetworkCommand.Blocks.ToString() && node.IsSyncingWithNode) { var blocksPayload = (StateBlocksPayload)msg.Payload; // Todo rewrite this code to support multithreaded 'Blocks' messages. Combine all gathered blocks // until the process has completed and all blocks are downloaded. Then, grab a block that points to the // end of our chain and add it to our chain. Repeat that process until all blocks have been added. var blocksProcessed = 0; lock (_blockchain) { while (blocksPayload.Blocks.Where(b => b.Header.PreviousHash == _blockchain.Blocks.Last().Header.Hash).Any()) { var blockToProcess = blocksPayload.Blocks.Where(b => b.Header.PreviousHash == _blockchain.Blocks.Last().Header.Hash).First(); if (_blockchain.CurrentHeight % 5 == 0 && _blockchain.CurrentHeight > 0) { _difficulty = _difficultyCalculator.CalculateCurrentDifficulty(_blockchain); // todo use CalculateCurrentDifficulty when testing is done } if (_difficulty < 1) { _difficulty = 1; } var currentTarget = BlockchainConstants.MaximumTarget / _difficulty; // todo refactor these 3 lines. They are copy-pasted from the miner. _blockValidator.ValidateBlock(blockToProcess, currentTarget, _blockchain, false, true); // Rethrow when we have a Block- / TransactionRejectedException. We don't want to keep a connection with bad nodes. blocksProcessed++; } } _logger.LogDebug("Downloaded and added {0} new blocks from remote node", blocksProcessed); _logger.LogDebug("Current height: {0}", _blockchain.CurrentHeight); if (blocksProcessed != blocksPayload.Blocks.Count()) { _logger.LogError("Added {0} new blocks from remote node, but expected {1}. Sync failed.", blocksProcessed, blocksPayload.Blocks.Count()); node.SetSyncStatus(SyncStatus.Failed); return; } _blockchainRepo.Update(_blockchain); // Block batch processed. Keep on ask for more headers. var getHeadersPayload = new GetHeadersPayload(_blockchain.Blocks.Last().Header.Hash); await SendMessageToNode(node, NetworkCommand.GetHeaders, getHeadersPayload); } else if (msg.Command == NetworkCommand.GetTxPool.ToString()) { var txPoolPayload = new StateTransactionsPayload(_txPool.GetAllTransactions()); await SendMessageToNode(node, NetworkCommand.TxPool, txPoolPayload); } else if (msg.Command == NetworkCommand.TxPool.ToString()) { var txPoolPayload = (StateTransactionsPayload)msg.Payload; foreach (var tx in txPoolPayload.Transactions) { _txPool.AddTransaction(tx); } } else if (msg.Command == NetworkCommand.NewTransaction.ToString()) { var txPayload = (SingleStateTransactionPayload)msg.Payload; if (_networkManager.IsSyncing) { return; } var validationResult = EventPublisher.GetInstance().PublishUnvalidatedTransactionReceived(node, new TransactionReceivedEventArgs(txPayload.Transaction)); if (validationResult == false) { //await node.Disconnect(); // Dishonest node, buuh! } } else if (msg.Command == NetworkCommand.NewBlock.ToString()) { var blockPayload = (SingleStateBlockPayload)msg.Payload; if (_networkManager.IsSyncing) { return; } var validationResult = EventPublisher.GetInstance().PublishUnvalidatedBlockCreated(node, new BlockCreatedEventArgs(blockPayload.Block)); if (validationResult == false) { //await node.Disconnect(); // Dishonest node, buuh! } } } catch (Exception ex) { _logger.LogError("An {0} occurred during the process of handling a {1} message: {2}. Node will be disconnected.", ex.GetType().Name, msg.Command.ToString(), ex.Message); node?.Disconnect(); } }