/// <summary> /// Sends the specified network message to the given address, if it is known and connected among clients. /// </summary> /// <param name="address">Ixian Wallet Address - the recipient of the message</param> /// <param name="code">Type of the network message to send</param> /// <param name="message">Byte-field with the required data, as specified by `code`.</param> /// <returns>True, if the message was delivered.</returns> public static bool forwardMessage(byte[] address, ProtocolMessageCode code, byte[] message) { if (address == null) { Logging.warn("Cannot forward message to null address."); return(false); } Logging.info(String.Format(">>>> Preparing to forward to {0}", Base58Check.Base58CheckEncoding.EncodePlain(address))); lock (connectedClients) { foreach (RemoteEndpoint endpoint in connectedClients) { // Skip connections without presence information if (endpoint.presence == null) { continue; } byte[] client_wallet = endpoint.presence.wallet; if (client_wallet != null && address.SequenceEqual(client_wallet)) { Logging.info(">>>> Forwarding message"); endpoint.sendData(code, message); } } } // TODO: broadcast to network if no connect clients found? return(false); }
/// <summary> /// Sends the specified network message to all connected clients /// </summary> /// <param name="address">Ixian Wallet Address - the recipient of the message</param> /// <param name="code">Type of the network message to send</param> /// <param name="message">Byte-field with the required data, as specified by `code`.</param> /// <returns>True, if the message was delivered.</returns> public static bool forwardMessage(ProtocolMessageCode code, byte[] message, byte[] exclude_address = null) { Logging.info(String.Format(">>>> Preparing to forward to everyone")); QueueMessage queue_message = RemoteEndpoint.getQueueMessage(code, message, null); lock (connectedClients) { foreach (RemoteEndpoint endpoint in connectedClients) { // Skip connections without presence information if (endpoint.presence == null) { continue; } byte[] client_wallet = endpoint.presence.wallet; if (client_wallet != null) { if (exclude_address != null && client_wallet.SequenceEqual(exclude_address)) { continue; } Logging.info(">>>> Forwarding message"); endpoint.sendData(queue_message); } } } return(false); }
// Send data to all connected nodes // Returns true if the data was sent to at least one client public static bool broadcastData(ProtocolMessageCode code, byte[] data, RemoteEndpoint skipEndpoint = null) { bool result = false; lock (streamClients) { foreach (NetworkClient client in streamClients) { if (client.isConnected()) { if (skipEndpoint != null) { if (client == skipEndpoint) { continue; } } if (client.helloReceived == false) { continue; } client.sendData(code, data); result = true; } } } return(result); }
public static void parseProtocolMessage(ProtocolMessageCode code, byte[] data, RemoteEndpoint endpoint) { if (handlerClass == null) { throw new Exception("Handler Class must be specified in IxianHandler Class"); } handlerClass.parseProtocolMessage(code, data, endpoint); }
private static byte[] extractHelperData(ProtocolMessageCode code, byte[] data) { if (code == ProtocolMessageCode.blockData || code == ProtocolMessageCode.newBlock) { return(data.Take(8).ToArray()); } return(null); }
/// <summary> /// Reads a protocol message from the specified byte-field and calls appropriate methods to process this message. /// </summary> /// <remarks> /// This function checks all applicable checksums and validates that the message is complete before calling one of the specialized /// methods to handle actual decoding and processing. /// </remarks> /// <param name="recv_buffer">Byte-field with an Ixian protocol message.</param> /// <param name="endpoint">Remote endpoint from where the message was received.</param> public static void readProtocolMessage(QueueMessageRaw raw_message, MessagePriority priority, RemoteEndpoint endpoint) { if (endpoint == null) { Logging.error("Endpoint was null. readProtocolMessage"); return; } ProtocolMessageCode code = raw_message.code; // Filter messages if (endpoint.presence == null) { // Check for presence and only accept hello and bye messages if there is no presence. if (code != ProtocolMessageCode.hello && code != ProtocolMessageCode.helloData && code != ProtocolMessageCode.bye) { return; } } if (raw_message.legacyChecksum != null) { // Compute checksum of received data byte[] local_checksum = Crypto.sha512sqTrunc(raw_message.data, 0, 0, 32); // Verify the checksum before proceeding if (local_checksum.SequenceEqual(raw_message.legacyChecksum) == false) { Logging.error("Dropped message (invalid legacy checksum)"); return; } } else { // Compute checksum of received data uint local_checksum = Crc32CAlgorithm.Compute(raw_message.data); // Verify the checksum before proceeding if (local_checksum != raw_message.checksum) { Logging.error("Dropped message (invalid checksum)"); return; } } // Can proceed to parse the data parameter based on the protocol message code. // Data can contain multiple elements. //parseProtocolMessage(code, data, socket, endpoint); NetworkQueue.receiveProtocolMessage(code, raw_message.data, Crc32CAlgorithm.Compute(raw_message.data), priority, endpoint); }
/// <summary> /// Sends the specified event to all connected clients. /// The information is only sent to those clients who have previously subscribed to this event type /// </summary> /// <param name="type">Types of the event that has occurred.</param> /// <param name="code">Type of the protocol message being sent.</param> /// <param name="data">Byte-field of the data, appropriate for the specific `code` used.</param> /// <param name="address">Ixian Wallet Address which triggered the event</param> /// <param name="helper_data">Optional, additional data to transmit after `data`.</param> /// <param name="skipEndpoint">If given, the message will not be sent to this remote endpoint. This prevents echoing the message to the originating node.</param> /// <returns>True, if at least one message was sent to at least one client.</returns> public static bool broadcastEventData(NetworkEvents.Type type, ProtocolMessageCode code, byte[] data, byte[] address, byte[] helper_data, RemoteEndpoint skipEndpoint = null) { bool result = false; try { QueueMessage queue_message = RemoteEndpoint.getQueueMessage(code, data, helper_data); lock (connectedClients) { foreach (RemoteEndpoint endpoint in connectedClients) { if (skipEndpoint != null) { if (endpoint == skipEndpoint) { continue; } } if (!endpoint.isConnected()) { continue; } if (endpoint.helloReceived == false) { continue; } if (endpoint.presenceAddress == null || (endpoint.presenceAddress.type != 'C' && endpoint.presenceAddress.type != 'R')) { continue; } // Finally, check if the endpoint is subscribed to this event and address if (endpoint.isSubscribedToAddress(type, address)) { endpoint.sendData(queue_message); result = true; } } } }catch (Exception e) { Logging.error("Exception occured in NetworkServer.broadcastEventData: " + e); } return(result); }
/// <summary> /// Prepares (serializes) a protocol message from the given Ixian message code and appropriate data. Checksum can be supplied, but /// if it isn't, this function will calculate it using the default method. /// </summary> /// <remarks> /// This function can be used from the server and client side. /// Please note: This function does not validate that the payload `data` conforms to the expected message for `code`. It is the /// caller's job to ensure that. /// </remarks> /// <param name="code">Message code.</param> /// <param name="data">Payload for the message.</param> /// <param name="checksum">Optional checksum. If not supplied, or if null, this function will calculate it with the default method.</param> /// <returns>Serialized message as a byte-field</returns> public static byte[] prepareProtocolMessage(ProtocolMessageCode code, byte[] data, byte[] checksum = null) { byte[] result = null; // Prepare the protocol sections int data_length = data.Length; if (data_length > CoreConfig.maxMessageSize) { Logging.error(String.Format("Tried to send data bigger than max allowed message size - {0} with code {1}.", data_length, code)); return(null); } byte[] data_checksum = checksum; if (checksum == null) { data_checksum = Crypto.sha512sqTrunc(data, 0, 0, 32); } using (MemoryStream m = new MemoryStream(4096)) { using (BinaryWriter writer = new BinaryWriter(m)) { // Protocol sections are code, length, checksum, data // Write each section in binary, in that specific order writer.Write((byte)'X'); writer.Write((int)code); writer.Write(data_length); writer.Write(data_checksum); writer.Flush(); m.Flush(); byte header_checksum = getHeaderChecksum(m.ToArray()); writer.Write(header_checksum); writer.Write((byte)'I'); writer.Write(data); #if TRACE_MEMSTREAM_SIZES Logging.info(String.Format("CoreProtocolMessage::prepareProtocolMessage: {0}", m.Length)); #endif } result = m.ToArray(); } return(result); }
/// <summary> /// Prepares and broadcasts an Ixian protocol message to all connected nodes, filtered by `types`. /// </summary> /// <remarks> /// The payload `data` should be properly formatted for the given `code` - this function will not ensure that this is so and /// the caller must provide a valid message to this function. /// The `skipEndpoint` parameter is useful when re-broadcasting a message received from a specific endpoint and do not wish to echo the same /// data back to the sender. /// </remarks> /// <param name="types">Types of nodes to send this message to.</param> /// <param name="code">Protocol code.</param> /// <param name="data">Message payload</param> /// <param name="helper_data">Additional information, as required by the protocol message</param> /// <param name="skipEndpoint">Remote endpoint which should be skipped (data should not be sent to it).</param> /// <returns>True, if at least one message was sent to at least one other node. False if no messages were sent.</returns> public static bool broadcastProtocolMessage(char[] types, ProtocolMessageCode code, byte[] data, byte[] helper_data, RemoteEndpoint skipEndpoint = null) { if (data == null) { Logging.warn(string.Format("Invalid protocol message data for {0}", code)); return(false); } bool c_result = NetworkClientManager.broadcastData(types, code, data, helper_data, skipEndpoint); bool s_result = NetworkServer.broadcastData(types, code, data, helper_data, skipEndpoint); if (!c_result && !s_result) { return(false); } return(true); }
// Sends data over the network public static void sendData(ProtocolMessageCode code, byte[] data) { byte[] ba = CoreProtocolMessage.prepareProtocolMessage(code, data); NetDump.Instance.appendSent(tcpClient.Client, ba, ba.Length); try { tcpClient.Client.Send(ba, SocketFlags.None); if (tcpClient.Client.Connected == false) { Logging.error("Failed senddata to client. Reconnecting."); } } catch (Exception e) { Logging.error(String.Format("CLN: Socket exception, attempting to reconnect {0}", e)); } //Console.WriteLine("sendData done"); }
// Internal function that sends data through the socket protected void sendDataInternal(ProtocolMessageCode code, byte[] data, byte[] checksum) { byte[] ba = CoreProtocolMessage.prepareProtocolMessage(code, data, checksum); NetDump.Instance.appendSent(clientSocket, ba, ba.Length); try { for (int sentBytes = 0; sentBytes < ba.Length && running;) { int bytesToSendCount = ba.Length - sentBytes; if (bytesToSendCount > 8000) { bytesToSendCount = 8000; } int curSentBytes = clientSocket.Send(ba, sentBytes, bytesToSendCount, SocketFlags.None); lastDataSentTime = Clock.getTimestamp(); // Sleep a bit to allow other threads to do their thing Thread.Yield(); sentBytes += curSentBytes; // TODO TODO TODO timeout } if (clientSocket.Connected == false) { if (running) { Logging.warn(String.Format("sendRE: Failed senddata to remote endpoint {0}, Closing.", getFullAddress())); } state = RemoteEndpointState.Closed; } } catch (Exception e) { if (running) { Logging.warn(String.Format("sendRE: Socket exception for {0}, closing. {1}", getFullAddress(), e)); } state = RemoteEndpointState.Closed; } }
/// <summary> /// Sends the specified network message to the given address, if it is known and connected among clients. /// </summary> /// <param name="address">Ixian Wallet Address - the recipient of the message</param> /// <param name="code">Type of the network message to send</param> /// <param name="message">Byte-field with the required data, as specified by `code`.</param> /// <returns>True, if the message was delivered.</returns> public static bool forwardMessage(byte[] address, ProtocolMessageCode code, byte[] message) { if (address == null) { Logging.warn("Cannot forward message to null address."); return(false); } Logging.info(String.Format(">>>> Preparing to forward to {0}", Base58Check.Base58CheckEncoding.EncodePlain(address))); QueueMessage queue_message = RemoteEndpoint.getQueueMessage(code, message, null); lock (connectedClients) { foreach (RemoteEndpoint endpoint in connectedClients) { // Skip connections without presence information if (endpoint.presence == null) { continue; } // SKip disconnected endpoints if (!endpoint.isConnected()) { continue; } byte[] client_wallet = endpoint.presence.wallet; if (client_wallet != null && address.SequenceEqual(client_wallet)) { Logging.info(">>>> Forwarding message"); endpoint.sendData(queue_message); return(true); } } } return(false); }
// Send data to all connected nodes // Returns true if the data was sent to at least one client public static bool broadcastData(char[] types, ProtocolMessageCode code, byte[] data, byte[] helper_data, RemoteEndpoint skipEndpoint = null) { bool result = false; QueueMessage queue_message = RemoteEndpoint.getQueueMessage(code, data, helper_data); lock (networkClients) { foreach (NetworkClient client in networkClients) { if (skipEndpoint != null) { if (client == skipEndpoint) { continue; } } if (!client.isConnected()) { continue; } if (client.helloReceived == false) { continue; } if (types != null) { if (client.presenceAddress == null || !types.Contains(client.presenceAddress.type)) { continue; } } client.sendData(queue_message); result = true; } } return(result); }
// Sends event data to all subscribed clients public static bool broadcastEventData(NetworkEvents.Type type, ProtocolMessageCode code, byte[] data, byte[] address, byte[] helper_data, RemoteEndpoint skipEndpoint = null) { bool result = false; lock (connectedClients) { foreach (RemoteEndpoint endpoint in connectedClients) { if (skipEndpoint != null) { if (endpoint == skipEndpoint) { continue; } } if (!endpoint.isConnected()) { continue; } if (endpoint.helloReceived == false) { continue; } if (endpoint.presenceAddress == null || endpoint.presenceAddress.type != 'C') { continue; } // Finally, check if the endpoint is subscribed to this event and address if (endpoint.isSubscribedToEvent(type, address)) { endpoint.sendData(code, data, helper_data); result = true; } } } return(result); }
/// <summary> /// Sends the given data to all appropriate connected clients. /// </summary> /// <param name="types">Types of clients to which the data should be sent.</param> /// <param name="code">Type of the protocol message being sent.</param> /// <param name="data">Byte-field of the data, appropriate for the specific `code` used.</param> /// <param name="helper_data">Optional, additional data to transmit after `data`.</param> /// <param name="skipEndpoint">If given, the message will not be sent to this remote endpoint. This prevents echoing the message to the originating node.</param> /// <returns>True, if at least one message was sent to at least one client.</returns> public static bool broadcastData(char[] types, ProtocolMessageCode code, byte[] data, byte[] helper_data, RemoteEndpoint skipEndpoint = null) { bool result = false; lock (connectedClients) { foreach (RemoteEndpoint endpoint in connectedClients) { if (skipEndpoint != null) { if (endpoint == skipEndpoint) { continue; } } if (!endpoint.isConnected()) { continue; } if (endpoint.helloReceived == false) { continue; } if (types != null) { if (endpoint.presenceAddress == null || !types.Contains(endpoint.presenceAddress.type)) { continue; } } endpoint.sendData(code, data, helper_data); result = true; } } return(result); }
// Sends data over the network public void sendData(ProtocolMessageCode code, byte[] data, byte[] helper_data = null) { if (data == null) { Logging.warn(string.Format("Invalid protocol message data for {0}", code)); return; } QueueMessage message = new QueueMessage(); message.code = code; message.data = data; message.checksum = Crypto.sha512sqTrunc(data, 0, 0, 32); message.skipEndpoint = null; message.helperData = helper_data; if (code == ProtocolMessageCode.bye || code == ProtocolMessageCode.keepAlivePresence || code == ProtocolMessageCode.getPresence || code == ProtocolMessageCode.updatePresence) { lock (sendQueueMessagesHighPriority) { addMessageToSendQueue(sendQueueMessagesHighPriority, message); } } else if (code != ProtocolMessageCode.transactionData && code != ProtocolMessageCode.newTransaction) { lock (sendQueueMessagesNormalPriority) { addMessageToSendQueue(sendQueueMessagesNormalPriority, message); } } else { lock (sendQueueMessagesLowPriority) { addMessageToSendQueue(sendQueueMessagesLowPriority, message); } } }
/// <summary> /// Sends the protocol message to the specified neighbor node, given as a Hostname or IP address and port. /// </summary> /// <param name="neighbor">IP address or hostname and port for the neighbor.</param> /// <param name="code">Type of the protocol message</param> /// <param name="data">Data required by the protocol message `code`.</param> /// <param name="helper_data">Optional, additional data to transmit after `data`.</param> /// <returns>True if the data was sent to the specified neighbor.</returns> public static bool sendToClient(string neighbor, ProtocolMessageCode code, byte[] data, byte[] helper_data) { RemoteEndpoint client = null; lock (connectedClients) { foreach (RemoteEndpoint ep in connectedClients) { if (ep.getFullAddress() == neighbor) { client = ep; break; } } } if (client != null) { client.sendData(code, data, helper_data); return(true); } return(false); }
public static bool sendToClient(string neighbor, ProtocolMessageCode code, byte[] data, byte[] helper_data) { NetworkClient client = null; lock (networkClients) { foreach (NetworkClient c in networkClients) { if (c.getFullAddress() == neighbor) { client = c; break; } } } if (client != null) { client.sendData(code, data, helper_data); return(true); } return(false); }
public static bool addToInventory(char[] types, InventoryItem item, RemoteEndpoint skip_endpoint, ProtocolMessageCode code, byte[] data, byte[] helper) { QueueMessage queue_message = RemoteEndpoint.getQueueMessage(code, data, helper); lock (connectedClients) { foreach (var client in connectedClients) { try { if (!client.isConnected() || !client.helloReceived) { continue; } if (client == skip_endpoint) { continue; } if (!types.Contains(client.presenceAddress.type)) { continue; } if (client.version > 5) { client.addInventoryItem(item); } else { // TODO legacy, can be removed after network upgrades client.sendData(queue_message); } }catch (Exception e) { Logging.error("Exception occured in NetworkServer.addToInventory: " + e); } } } return(true); }
// Unified protocol message parsing public static void parseProtocolMessage(ProtocolMessageCode code, byte[] data, RemoteEndpoint endpoint) { if (endpoint == null) { Logging.error("Endpoint was null. parseProtocolMessage"); return; } try { switch (code) { case ProtocolMessageCode.hello: using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { if (CoreProtocolMessage.processHelloMessage(endpoint, reader)) { byte[] challenge_response = null; int challenge_len = reader.ReadInt32(); byte[] challenge = reader.ReadBytes(challenge_len); challenge_response = CryptoManager.lib.getSignature(challenge, Node.walletStorage.getPrimaryPrivateKey()); CoreProtocolMessage.sendHelloMessage(endpoint, true, challenge_response); endpoint.helloReceived = true; return; } } } break; case ProtocolMessageCode.helloData: using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { if (CoreProtocolMessage.processHelloMessage(endpoint, reader)) { char node_type = endpoint.presenceAddress.type; if (node_type != 'M' && node_type != 'H') { CoreProtocolMessage.sendBye(endpoint, ProtocolByeCode.expectingMaster, string.Format("Expecting master node."), "", true); return; } ulong last_block_num = reader.ReadUInt64(); int bcLen = reader.ReadInt32(); byte[] block_checksum = reader.ReadBytes(bcLen); int wsLen = reader.ReadInt32(); byte[] walletstate_checksum = reader.ReadBytes(wsLen); int consensus = reader.ReadInt32(); endpoint.blockHeight = last_block_num; int block_version = reader.ReadInt32(); // Check for legacy level ulong legacy_level = reader.ReadUInt64(); // deprecated int challenge_response_len = reader.ReadInt32(); byte[] challenge_response = reader.ReadBytes(challenge_response_len); if (!CryptoManager.lib.verifySignature(endpoint.challenge, endpoint.serverPubKey, challenge_response)) { CoreProtocolMessage.sendBye(endpoint, ProtocolByeCode.authFailed, string.Format("Invalid challenge response."), "", true); return; } // Process the hello data endpoint.helloReceived = true; NetworkClientManager.recalculateLocalTimeDifference(); if (endpoint.presenceAddress.type == 'M') { Node.setNetworkBlock(last_block_num, block_checksum, block_version); // Get random presences endpoint.sendData(ProtocolMessageCode.getRandomPresences, new byte[1] { (byte)'M' }); CoreProtocolMessage.subscribeToEvents(endpoint); } } } } break; case ProtocolMessageCode.balance: { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { int address_length = reader.ReadInt32(); byte[] address = reader.ReadBytes(address_length); // Retrieve the latest balance IxiNumber balance = reader.ReadString(); if (address.SequenceEqual(Node.walletStorage.getPrimaryAddress())) { // Retrieve the blockheight for the balance ulong block_height = reader.ReadUInt64(); if (block_height > Node.balance.blockHeight && (Node.balance.balance != balance || Node.balance.blockHeight == 0)) { byte[] block_checksum = reader.ReadBytes(reader.ReadInt32()); Node.balance.address = address; Node.balance.balance = balance; Node.balance.blockHeight = block_height; Node.balance.blockChecksum = block_checksum; Node.balance.verified = false; } } } } } break; case ProtocolMessageCode.updatePresence: { // Parse the data and update entries in the presence list PresenceList.updateFromBytes(data); } break; case ProtocolMessageCode.blockHeaders: { // Forward the block headers to the TIV handler Node.tiv.receivedBlockHeaders(data, endpoint); } break; case ProtocolMessageCode.pitData: { Node.tiv.receivedPIT(data, endpoint); } break; case ProtocolMessageCode.newTransaction: case ProtocolMessageCode.transactionData: { Transaction tx = new Transaction(data, true); PendingTransactions.increaseReceivedCount(tx.id); Node.tiv.receivedNewTransaction(tx); Console.WriteLine("Received new transaction {0}", tx.id); } break; default: break; } } catch (Exception e) { Logging.error(string.Format("Error parsing network message. Details: {0}", e.ToString())); } if (waitingFor == code) { blocked = false; } }
// Unified protocol message parsing public static void parseProtocolMessage(ProtocolMessageCode code, byte[] data, RemoteEndpoint endpoint) { if (endpoint == null) { Logging.error("Endpoint was null. parseProtocolMessage"); return; } try { switch (code) { case ProtocolMessageCode.hello: { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { CoreProtocolMessage.processHelloMessageV6(endpoint, reader); } } } break; case ProtocolMessageCode.helloData: using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { if (!CoreProtocolMessage.processHelloMessageV6(endpoint, reader)) { return; } ulong last_block_num = reader.ReadIxiVarUInt(); int bcLen = (int)reader.ReadIxiVarUInt(); byte[] block_checksum = reader.ReadBytes(bcLen); endpoint.blockHeight = last_block_num; int block_version = (int)reader.ReadIxiVarUInt(); if (endpoint.presenceAddress.type != 'C') { ulong highest_block_height = IxianHandler.getHighestKnownNetworkBlockHeight(); if (last_block_num + 10 < highest_block_height) { CoreProtocolMessage.sendBye(endpoint, ProtocolByeCode.tooFarBehind, string.Format("Your node is too far behind, your block height is {0}, highest network block height is {1}.", last_block_num, highest_block_height), highest_block_height.ToString(), true); return; } } // Process the hello data endpoint.helloReceived = true; NetworkClientManager.recalculateLocalTimeDifference(); if (endpoint.presenceAddress.type == 'R') { string[] connected_servers = StreamClientManager.getConnectedClients(true); if (connected_servers.Count() == 1 || !connected_servers.Contains(StreamClientManager.primaryS2Address)) { if (StreamClientManager.primaryS2Address == "") { FriendList.requestAllFriendsPresences(); } // TODO set the primary s2 host more efficiently, perhaps allow for multiple s2 primary hosts StreamClientManager.primaryS2Address = endpoint.getFullAddress(true); // TODO TODO do not set if directly connectable IxianHandler.publicIP = endpoint.address; IxianHandler.publicPort = endpoint.incomingPort; PresenceList.forceSendKeepAlive = true; Logging.info("Forcing KA from networkprotocol"); } } else if (endpoint.presenceAddress.type == 'C') { Friend f = FriendList.getFriend(endpoint.presence.wallet); if (f != null && f.bot) { StreamProcessor.sendGetBotInfo(f); } } if (endpoint.presenceAddress.type == 'M' || endpoint.presenceAddress.type == 'H') { Node.setNetworkBlock(last_block_num, block_checksum, block_version); // Get random presences endpoint.sendData(ProtocolMessageCode.getRandomPresences, new byte[1] { (byte)'R' }); endpoint.sendData(ProtocolMessageCode.getRandomPresences, new byte[1] { (byte)'M' }); endpoint.sendData(ProtocolMessageCode.getRandomPresences, new byte[1] { (byte)'H' }); subscribeToEvents(endpoint); } } } break; case ProtocolMessageCode.s2data: { StreamProcessor.receiveData(data, endpoint); } break; case ProtocolMessageCode.updatePresence: { Logging.info("NET: Receiving presence list update"); // Parse the data and update entries in the presence list Presence p = PresenceList.updateFromBytes(data); if (p == null) { return; } Friend f = FriendList.getFriend(p.wallet); if (f != null) { f.relayIP = p.addresses[0].address; } } break; case ProtocolMessageCode.keepAlivePresence: { byte[] address = null; long last_seen = 0; byte[] device_id = null; bool updated = PresenceList.receiveKeepAlive(data, out address, out last_seen, out device_id, endpoint); Presence p = PresenceList.getPresenceByAddress(address); if (p == null) { return; } Friend f = FriendList.getFriend(p.wallet); if (f != null) { f.relayIP = p.addresses[0].address; } } break; case ProtocolMessageCode.getPresence: { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { int walletLen = reader.ReadInt32(); byte[] wallet = reader.ReadBytes(walletLen); Presence p = PresenceList.getPresenceByAddress(wallet); if (p != null) { lock (p) { byte[][] presence_chunks = p.getByteChunks(); foreach (byte[] presence_chunk in presence_chunks) { endpoint.sendData(ProtocolMessageCode.updatePresence, presence_chunk, null); } } } else { // TODO blacklisting point Logging.warn(string.Format("Node has requested presence information about {0} that is not in our PL.", Base58Check.Base58CheckEncoding.EncodePlain(wallet))); } } } } break; case ProtocolMessageCode.getPresence2: { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { int walletLen = (int)reader.ReadIxiVarUInt(); byte[] wallet = reader.ReadBytes(walletLen); Presence p = PresenceList.getPresenceByAddress(wallet); if (p != null) { lock (p) { byte[][] presence_chunks = p.getByteChunks(); foreach (byte[] presence_chunk in presence_chunks) { endpoint.sendData(ProtocolMessageCode.updatePresence, presence_chunk, null); } } } else { // TODO blacklisting point Logging.warn(string.Format("Node has requested presence information about {0} that is not in our PL.", Base58Check.Base58CheckEncoding.EncodePlain(wallet))); } } } } break; case ProtocolMessageCode.balance: { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { int address_length = reader.ReadInt32(); byte[] address = reader.ReadBytes(address_length); // Retrieve the latest balance IxiNumber balance = reader.ReadString(); if (address.SequenceEqual(Node.walletStorage.getPrimaryAddress())) { // Retrieve the blockheight for the balance ulong block_height = reader.ReadUInt64(); if (block_height > Node.balance.blockHeight && (Node.balance.balance != balance || Node.balance.blockHeight == 0)) { byte[] block_checksum = reader.ReadBytes(reader.ReadInt32()); Node.balance.address = address; Node.balance.balance = balance; Node.balance.blockHeight = block_height; Node.balance.blockChecksum = block_checksum; Node.balance.verified = false; } Node.balance.lastUpdate = Clock.getTimestamp(); } } } } break; case ProtocolMessageCode.balance2: { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { int address_length = (int)reader.ReadIxiVarUInt(); byte[] address = reader.ReadBytes(address_length); // Retrieve the latest balance IxiNumber balance = new IxiNumber(new BigInteger(reader.ReadBytes((int)reader.ReadIxiVarUInt()))); if (address.SequenceEqual(Node.walletStorage.getPrimaryAddress())) { // Retrieve the blockheight for the balance ulong block_height = reader.ReadIxiVarUInt(); if (block_height > Node.balance.blockHeight && (Node.balance.balance != balance || Node.balance.blockHeight == 0)) { byte[] block_checksum = reader.ReadBytes((int)reader.ReadIxiVarUInt()); Node.balance.address = address; Node.balance.balance = balance; Node.balance.blockHeight = block_height; Node.balance.blockChecksum = block_checksum; Node.balance.verified = false; } Node.balance.lastUpdate = Clock.getTimestamp(); } } } } break; case ProtocolMessageCode.newTransaction: case ProtocolMessageCode.transactionData: { // TODO: check for errors/exceptions Transaction transaction = new Transaction(data, true); if (endpoint.presenceAddress.type == 'M' || endpoint.presenceAddress.type == 'H') { PendingTransactions.increaseReceivedCount(transaction.id, endpoint.presence.wallet); } TransactionCache.addUnconfirmedTransaction(transaction); Node.tiv.receivedNewTransaction(transaction); } break; case ProtocolMessageCode.bye: CoreProtocolMessage.processBye(data, endpoint); break; case ProtocolMessageCode.blockHeaders2: { // Forward the block headers to the TIV handler Node.tiv.receivedBlockHeaders2(data, endpoint); } break; case ProtocolMessageCode.pitData2: { Node.tiv.receivedPIT2(data, endpoint); } break; default: break; } } catch (Exception e) { Logging.error("Error parsing network message. Details: {0}", e.ToString()); } }
// Unified protocol message parsing public static void parseProtocolMessage(ProtocolMessageCode code, byte[] data, RemoteEndpoint endpoint) { if (endpoint == null) { Logging.error("Endpoint was null. parseProtocolMessage"); return; } try { switch (code) { case ProtocolMessageCode.hello: using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { bool processed = false; processed = CoreProtocolMessage.processHelloMessageV6(endpoint, reader, false); if (!processed || (Config.whiteList.Count > 0 && !Config.whiteList.Contains(endpoint.presence.wallet, new ByteArrayComparer()))) { CoreProtocolMessage.sendBye(endpoint, ProtocolByeCode.bye, string.Format("Access denied."), "", true); return; } endpoint.helloReceived = true; } } break; case ProtocolMessageCode.helloData: using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { if (CoreProtocolMessage.processHelloMessageV6(endpoint, reader)) { char node_type = endpoint.presenceAddress.type; if (node_type != 'M' && node_type != 'H') { CoreProtocolMessage.sendBye(endpoint, ProtocolByeCode.expectingMaster, string.Format("Expecting master node."), "", true); return; } ulong last_block_num = reader.ReadIxiVarUInt(); int bcLen = (int)reader.ReadIxiVarUInt(); byte[] block_checksum = reader.ReadBytes(bcLen); endpoint.blockHeight = last_block_num; int block_version = (int)reader.ReadIxiVarUInt(); try { string public_ip = reader.ReadString(); ((NetworkClient)endpoint).myAddress = public_ip; } catch (Exception) { } string address = NetworkClientManager.getMyAddress(); if (address != null) { if (IxianHandler.publicIP != address) { Logging.info("Setting public IP to " + address); IxianHandler.publicIP = address; } } // Process the hello data endpoint.helloReceived = true; NetworkClientManager.recalculateLocalTimeDifference(); Node.setNetworkBlock(last_block_num, block_checksum, block_version); // Get random presences endpoint.sendData(ProtocolMessageCode.getRandomPresences, new byte[1] { (byte)'M' }); endpoint.sendData(ProtocolMessageCode.getRandomPresences, new byte[1] { (byte)'H' }); CoreProtocolMessage.subscribeToEvents(endpoint); } } } break; case ProtocolMessageCode.s2data: { StreamProcessor.receiveData(data, endpoint); } break; case ProtocolMessageCode.s2failed: { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { Logging.error("Failed to send s2 data"); } } } break; case ProtocolMessageCode.transactionData: { Transaction tx = new Transaction(data, true); if (endpoint.presenceAddress.type == 'M' || endpoint.presenceAddress.type == 'H') { PendingTransactions.increaseReceivedCount(tx.id, endpoint.presence.wallet); } Node.tiv.receivedNewTransaction(tx); Logging.info("Received new transaction {0}", tx.id); Node.addTransactionToActivityStorage(tx); } break; case ProtocolMessageCode.updatePresence: { // Parse the data and update entries in the presence list PresenceList.updateFromBytes(data, 0); } break; case ProtocolMessageCode.keepAlivePresence: { byte[] address = null; long last_seen = 0; byte[] device_id = null; bool updated = PresenceList.receiveKeepAlive(data, out address, out last_seen, out device_id, endpoint); } break; case ProtocolMessageCode.getPresence: { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { int walletLen = reader.ReadInt32(); byte[] wallet = reader.ReadBytes(walletLen); Presence p = PresenceList.getPresenceByAddress(wallet); if (p != null) { lock (p) { byte[][] presence_chunks = p.getByteChunks(); foreach (byte[] presence_chunk in presence_chunks) { endpoint.sendData(ProtocolMessageCode.updatePresence, presence_chunk, null); } } } else { // TODO blacklisting point Logging.warn(string.Format("Node has requested presence information about {0} that is not in our PL.", Base58Check.Base58CheckEncoding.EncodePlain(wallet))); } } } } break; case ProtocolMessageCode.getPresence2: { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { int walletLen = (int)reader.ReadIxiVarUInt(); byte[] wallet = reader.ReadBytes(walletLen); Presence p = PresenceList.getPresenceByAddress(wallet); if (p != null) { lock (p) { byte[][] presence_chunks = p.getByteChunks(); foreach (byte[] presence_chunk in presence_chunks) { endpoint.sendData(ProtocolMessageCode.updatePresence, presence_chunk, null); } } } else { // TODO blacklisting point Logging.warn(string.Format("Node has requested presence information about {0} that is not in our PL.", Base58Check.Base58CheckEncoding.EncodePlain(wallet))); } } } } break; case ProtocolMessageCode.balance: { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { int address_length = reader.ReadInt32(); byte[] address = reader.ReadBytes(address_length); // Retrieve the latest balance IxiNumber balance = reader.ReadString(); if (address.SequenceEqual(IxianHandler.getWalletStorage().getPrimaryAddress())) { // Retrieve the blockheight for the balance ulong block_height = reader.ReadUInt64(); if (block_height > Node.balance.blockHeight && (Node.balance.balance != balance || Node.balance.blockHeight == 0)) { byte[] block_checksum = reader.ReadBytes(reader.ReadInt32()); Node.balance.address = address; Node.balance.balance = balance; Node.balance.blockHeight = block_height; Node.balance.blockChecksum = block_checksum; Node.balance.lastUpdate = Clock.getTimestamp(); Node.balance.verified = false; } } } } } break; case ProtocolMessageCode.balance2: { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { int address_length = (int)reader.ReadIxiVarUInt(); byte[] address = reader.ReadBytes(address_length); // Retrieve the latest balance IxiNumber balance = new IxiNumber(new BigInteger(reader.ReadBytes((int)reader.ReadIxiVarUInt()))); if (address.SequenceEqual(IxianHandler.getWalletStorage().getPrimaryAddress())) { // Retrieve the blockheight for the balance ulong block_height = reader.ReadIxiVarUInt(); if (block_height > Node.balance.blockHeight && (Node.balance.balance != balance || Node.balance.blockHeight == 0)) { byte[] block_checksum = reader.ReadBytes((int)reader.ReadIxiVarUInt()); Node.balance.address = address; Node.balance.balance = balance; Node.balance.blockHeight = block_height; Node.balance.blockChecksum = block_checksum; Node.balance.lastUpdate = Clock.getTimestamp(); Node.balance.verified = false; } } } } } break; case ProtocolMessageCode.bye: CoreProtocolMessage.processBye(data, endpoint); break; case ProtocolMessageCode.blockHeaders2: { // Forward the block headers to the TIV handler Node.tiv.receivedBlockHeaders2(data, endpoint); } break; case ProtocolMessageCode.pitData2: { Node.tiv.receivedPIT2(data, endpoint); } break; default: break; } } catch (Exception e) { Logging.error(string.Format("Error parsing network message. Details: {0}", e.ToString())); } }
public static void parseProtocolMessage(ProtocolMessageCode code, byte[] data, RemoteEndpoint endpoint) { verifyHandler(); handlerClass.parseProtocolMessage(code, data, endpoint); }
// Unified protocol message parsing public static void parseProtocolMessage(ProtocolMessageCode code, byte[] data, RemoteEndpoint endpoint) { if (endpoint == null) { Logging.error("Endpoint was null. parseProtocolMessage"); return; } try { switch (code) { case ProtocolMessageCode.hello: handleHello(data, endpoint); break; case ProtocolMessageCode.helloData: handleHelloData(data, endpoint); break; case ProtocolMessageCode.s2data: StreamProcessor.receiveData(data, endpoint); break; case ProtocolMessageCode.s2failed: Logging.error("Failed to send s2 data"); break; case ProtocolMessageCode.s2signature: StreamProcessor.receivedTransactionSignature(data, endpoint); break; case ProtocolMessageCode.newTransaction: case ProtocolMessageCode.transactionData: { Transaction tx = new Transaction(data, true); if (endpoint.presenceAddress.type == 'M' || endpoint.presenceAddress.type == 'H') { PendingTransactions.increaseReceivedCount(tx.id, endpoint.presence.wallet); } Node.tiv.receivedNewTransaction(tx); Logging.info("Received new transaction {0}", tx.id); Node.addTransactionToActivityStorage(tx); } break; case ProtocolMessageCode.updatePresence: // Parse the data and update entries in the presence list PresenceList.updateFromBytes(data); break; case ProtocolMessageCode.keepAlivePresence: byte[] address = null; long last_seen = 0; byte[] device_id = null; bool updated = PresenceList.receiveKeepAlive(data, out address, out last_seen, out device_id, endpoint); break; case ProtocolMessageCode.getPresence: handleGetPresence(data, endpoint); break; case ProtocolMessageCode.getPresence2: handleGetPresence2(data, endpoint); break; case ProtocolMessageCode.balance: handleBalance(data, endpoint); break; case ProtocolMessageCode.balance2: handleBalance2(data, endpoint); break; case ProtocolMessageCode.bye: CoreProtocolMessage.processBye(data, endpoint); break; case ProtocolMessageCode.blockHeaders2: // Forward the block headers to the TIV handler Node.tiv.receivedBlockHeaders2(data, endpoint); break; case ProtocolMessageCode.pitData2: Node.tiv.receivedPIT2(data, endpoint); break; default: break; } } catch (Exception e) { Logging.error(string.Format("Error parsing network message. Details: {0}", e.ToString())); } }
public override void parseProtocolMessage(ProtocolMessageCode code, byte[] data, RemoteEndpoint endpoint) { ProtocolMessage.parseProtocolMessage(code, data, endpoint); }
public static void setWaitFor(ProtocolMessageCode value) { waitingFor = value; blocked = true; }
public abstract void parseProtocolMessage(ProtocolMessageCode code, byte[] data, RemoteEndpoint endpoint);
// Broadcasts protocol message to a single random node with block height higher than the one specified with parameter block_num /// <summary> /// Sends the specified protocol message to one of the connected remote endpoints, chosen randomly. /// </summary> /// <remarks> /// The payload `data` should be properly formatted for the given `code` - this function will not ensure that this is so and /// the caller must provide a valid message to this function. /// The `skipEndpoint` parameter is useful when re-broadcasting a message received from a specific endpoint and do not wish to echo the same /// data back to the sender. /// The `block_num` parameter is used to filter the remote endpoints based on their latest known block height. /// </remarks> /// <param name="types">Types of the nodes where the message should be sent.</param> /// <param name="code">Ixian protocol code.</param> /// <param name="data">Payload data.</param> /// <param name="block_num">Block which should be searched for the endpoint addresses.</param> /// <param name="skipEndpoint">Minimum block height for endpoints which should receive this message.</param> /// <returns>True, if at least one message was sent to at least one remote endpoint. False if no messages were sent.</returns> public static bool broadcastProtocolMessageToSingleRandomNode(char[] types, ProtocolMessageCode code, byte[] data, ulong block_num, RemoteEndpoint skipEndpoint = null) { if (data == null) { Logging.warn(string.Format("Invalid protocol message data for {0}", code)); return(false); } lock (NetworkClientManager.networkClients) { lock (NetworkServer.connectedClients) { int serverCount = 0; int clientCount = 0; List <NetworkClient> servers = null; List <RemoteEndpoint> clients = null; if (types == null) { servers = NetworkClientManager.networkClients.FindAll(x => x.blockHeight > block_num && x.isConnected() && x.helloReceived); clients = NetworkServer.connectedClients.FindAll(x => x.blockHeight > block_num && x.isConnected() && x.helloReceived); serverCount = servers.Count(); clientCount = clients.Count(); if (serverCount == 0 && clientCount == 0) { servers = NetworkClientManager.networkClients.FindAll(x => x.blockHeight == block_num && x.isConnected() && x.helloReceived); clients = NetworkServer.connectedClients.FindAll(x => x.blockHeight == block_num && x.isConnected() && x.helloReceived); } } else { servers = NetworkClientManager.networkClients.FindAll(x => x.blockHeight > block_num && x.presenceAddress != null && types.Contains(x.presenceAddress.type) && x.isConnected() && x.helloReceived); clients = NetworkServer.connectedClients.FindAll(x => x.blockHeight > block_num && x.presenceAddress != null && types.Contains(x.presenceAddress.type) && x.isConnected() && x.helloReceived); serverCount = servers.Count(); clientCount = clients.Count(); if (serverCount == 0 && clientCount == 0) { servers = NetworkClientManager.networkClients.FindAll(x => x.blockHeight == block_num && x.presenceAddress != null && types.Contains(x.presenceAddress.type) && x.isConnected() && x.helloReceived); clients = NetworkServer.connectedClients.FindAll(x => x.blockHeight == block_num && x.presenceAddress != null && types.Contains(x.presenceAddress.type) && x.isConnected() && x.helloReceived); } } serverCount = servers.Count(); clientCount = clients.Count(); if (serverCount == 0 && clientCount == 0) { return(false); } Random r = new Random(); int rIdx = r.Next(serverCount + clientCount); RemoteEndpoint re = null; if (rIdx < serverCount) { re = servers[rIdx]; } else { re = clients[rIdx - serverCount]; } if (re == skipEndpoint && serverCount + clientCount > 1) { if (rIdx + 1 < serverCount) { re = servers[rIdx + 1]; } else if (rIdx + 1 < serverCount + clientCount) { re = clients[rIdx + 1 - serverCount]; } else if (serverCount > 0) { re = servers[0]; } else if (clientCount > 0) { re = clients[0]; } } if (re != null && re.isConnected()) { re.sendData(code, data); return(true); } return(false); } } }
// Unified protocol message parsing public static void parseProtocolMessage(ProtocolMessageCode code, byte[] data, RemoteEndpoint endpoint) { if (endpoint == null) { Logging.error("Endpoint was null. parseProtocolMessage"); return; } try { switch (code) { case ProtocolMessageCode.hello: using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { if (CoreProtocolMessage.processHelloMessage(endpoint, reader)) { int challenge_len = reader.ReadInt32(); byte[] challenge = reader.ReadBytes(challenge_len); byte[] challenge_response = CryptoManager.lib.getSignature(challenge, Node.walletStorage.getPrimaryPrivateKey()); CoreProtocolMessage.sendHelloMessage(endpoint, true, challenge_response); endpoint.helloReceived = true; return; } } } break; case ProtocolMessageCode.helloData: using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { if (CoreProtocolMessage.processHelloMessage(endpoint, reader)) { char node_type = endpoint.presenceAddress.type; if (node_type != 'M' && node_type != 'H') { CoreProtocolMessage.sendBye(endpoint, ProtocolByeCode.expectingMaster, string.Format("Expecting master node."), "", true); return; } ulong last_block_num = reader.ReadUInt64(); int bcLen = reader.ReadInt32(); byte[] block_checksum = reader.ReadBytes(bcLen); int wsLen = reader.ReadInt32(); byte[] walletstate_checksum = reader.ReadBytes(wsLen); int consensus = reader.ReadInt32(); endpoint.blockHeight = last_block_num; int block_version = reader.ReadInt32(); Node.setLastBlock(last_block_num, block_checksum, walletstate_checksum, block_version); // Check for legacy level ulong legacy_level = reader.ReadUInt64(); // deprecated int challenge_response_len = reader.ReadInt32(); byte[] challenge_response = reader.ReadBytes(challenge_response_len); if (!CryptoManager.lib.verifySignature(endpoint.challenge, endpoint.serverPubKey, challenge_response)) { CoreProtocolMessage.sendBye(endpoint, ProtocolByeCode.authFailed, string.Format("Invalid challenge response."), "", true); return; } // Process the hello data endpoint.helloReceived = true; NetworkClientManager.recalculateLocalTimeDifference(); } } } break; case ProtocolMessageCode.s2data: { StreamProcessor.receiveData(data, endpoint); } break; case ProtocolMessageCode.s2failed: { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { Logging.error("Failed to send s2 data"); } } } break; case ProtocolMessageCode.s2signature: { StreamProcessor.receivedTransactionSignature(data, endpoint); } break; case ProtocolMessageCode.newTransaction: { // Forward the new transaction message to the DLT network CoreProtocolMessage.broadcastProtocolMessage(new char[] { 'M', 'H' }, ProtocolMessageCode.newTransaction, data, null); } break; case ProtocolMessageCode.presenceList: { Logging.info("Receiving complete presence list"); PresenceList.syncFromBytes(data); } break; case ProtocolMessageCode.updatePresence: { // Parse the data and update entries in the presence list PresenceList.updateFromBytes(data); } break; case ProtocolMessageCode.keepAlivePresence: { byte[] address = null; bool updated = PresenceList.receiveKeepAlive(data, out address, endpoint); // If a presence entry was updated, broadcast this message again if (updated) { CoreProtocolMessage.broadcastProtocolMessage(new char[] { 'M', 'R', 'H', 'W' }, ProtocolMessageCode.keepAlivePresence, data, address, endpoint); // Send this keepalive message to all connected clients CoreProtocolMessage.broadcastEventDataMessage(NetworkEvents.Type.keepAlive, address, ProtocolMessageCode.keepAlivePresence, data, address, endpoint); } } break; case ProtocolMessageCode.getPresence: { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { int walletLen = reader.ReadInt32(); byte[] wallet = reader.ReadBytes(walletLen); Presence p = PresenceList.getPresenceByAddress(wallet); if (p != null) { lock (p) { byte[][] presence_chunks = p.getByteChunks(); foreach (byte[] presence_chunk in presence_chunks) { endpoint.sendData(ProtocolMessageCode.updatePresence, presence_chunk, null); } } } else { // TODO blacklisting point Logging.warn(string.Format("Node has requested presence information about {0} that is not in our PL.", Base58Check.Base58CheckEncoding.EncodePlain(wallet))); } } } } break; case ProtocolMessageCode.balance: { // TODO: make sure this is received from a DLT node only. using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { int address_length = reader.ReadInt32(); byte[] address = reader.ReadBytes(address_length); // Retrieve the latest balance IxiNumber balance = reader.ReadString(); if (address.SequenceEqual(Node.walletStorage.getPrimaryAddress())) { Node.balance = balance; } // Retrieve the blockheight for the balance ulong blockheight = reader.ReadUInt64(); Node.blockHeight = blockheight; } } } break; case ProtocolMessageCode.bye: { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { endpoint.stop(); bool byeV1 = false; try { ProtocolByeCode byeCode = (ProtocolByeCode)reader.ReadInt32(); string byeMessage = reader.ReadString(); string byeData = reader.ReadString(); byeV1 = true; switch (byeCode) { case ProtocolByeCode.bye: // all good break; case ProtocolByeCode.forked: // forked node disconnected Logging.info(string.Format("Disconnected with message: {0} {1}", byeMessage, byeData)); break; case ProtocolByeCode.deprecated: // deprecated node disconnected Logging.info(string.Format("Disconnected with message: {0} {1}", byeMessage, byeData)); break; case ProtocolByeCode.incorrectIp: // incorrect IP if (IxiUtils.validateIPv4(byeData)) { if (NetworkClientManager.getConnectedClients(true).Length < 2) { IxianHandler.publicIP = byeData; Logging.info("Changed internal IP Address to " + byeData + ", reconnecting"); } } break; case ProtocolByeCode.notConnectable: // not connectable from the internet Logging.error("This node must be connectable from the internet, to connect to the network."); Logging.error("Please setup uPNP and/or port forwarding on your router for port " + IxianHandler.publicPort + "."); NetworkServer.connectable = false; break; case ProtocolByeCode.insufficientFunds: break; default: Logging.warn(string.Format("Disconnected with message: {0} {1}", byeMessage, byeData)); break; } } catch (Exception) { } if (byeV1) { return; } reader.BaseStream.Seek(0, SeekOrigin.Begin); // Retrieve the message string message = reader.ReadString(); if (message.Length > 0) { Logging.info(string.Format("Disconnected with message: {0}", message)); } else { Logging.info("Disconnected"); } } } } break; case ProtocolMessageCode.extend: { if (Config.isTestClient) { TestClientNode.handleExtendProtocol(data); } } break; default: break; } } catch (Exception e) { Logging.error(string.Format("Error parsing network message. Details: {0}", e.ToString())); } }
public static void receiveProtocolMessage(ProtocolMessageCode code, byte[] data, uint checksum, MessagePriority priority, RemoteEndpoint endpoint) { QueueMessageRecv message = new QueueMessageRecv { code = code, data = data, length = data.Length, checksum = checksum, endpoint = endpoint, helperData = extractHelperData(code, data) }; if (priority == MessagePriority.medium) { lock (queueMediumPriority) { if (message.helperData != null) { if (queueMediumPriority.Exists(x => x.code == message.code && x.helperData.SequenceEqual(message.helperData))) { int msg_index = queueMediumPriority.FindIndex(x => x.code == message.code && message.helperData.SequenceEqual(x.helperData)); if (queueMediumPriority[msg_index].length < message.length) { queueMediumPriority[msg_index] = message; } return; } } if (queueMediumPriority.Exists(x => x.code == message.code && x.checksum == message.checksum)) { Logging.trace("Attempting to add a duplicate message (code: {0}) to the network queue", code); return; } queueMediumPriority.Add(message); } return; } lock (queueLowPriority) { // Move block related messages to txqueue bool found_get_request = false; bool found_tx_request = false; switch (code) { #pragma warning disable CS0618 // Type or member is obsolete case ProtocolMessageCode.getTransaction: case ProtocolMessageCode.getTransaction2: case ProtocolMessageCode.getTransaction3: case ProtocolMessageCode.getTransactions: case ProtocolMessageCode.getTransactions2: case ProtocolMessageCode.getBlock: case ProtocolMessageCode.getBlock2: case ProtocolMessageCode.getBlock3: case ProtocolMessageCode.getBlockHeaders: case ProtocolMessageCode.getBlockHeaders2: case ProtocolMessageCode.getSignatures: case ProtocolMessageCode.getBlockSignatures2: case ProtocolMessageCode.getPIT: case ProtocolMessageCode.getPIT2: #pragma warning restore CS0618 // Type or member is obsolete found_get_request = true; found_tx_request = true; break; #pragma warning disable CS0618 // Type or member is obsolete case ProtocolMessageCode.transactionsChunk: case ProtocolMessageCode.transactionsChunk2: case ProtocolMessageCode.newTransaction: case ProtocolMessageCode.transactionData: case ProtocolMessageCode.blockTransactionsChunk: case ProtocolMessageCode.blockHeaders: case ProtocolMessageCode.blockHeaders2: case ProtocolMessageCode.newBlock: case ProtocolMessageCode.blockData: case ProtocolMessageCode.pitData: case ProtocolMessageCode.pitData2: case ProtocolMessageCode.inventory: case ProtocolMessageCode.inventory2: #pragma warning restore CS0618 // Type or member is obsolete found_get_request = false; found_tx_request = true; break; } if (found_tx_request) { if (found_get_request) { if (message.helperData != null) { if (queueLowPriority.Exists(x => x.code == message.code && x.helperData.SequenceEqual(message.helperData) && x.endpoint == message.endpoint)) { int msg_index = queueLowPriority.FindIndex(x => x.code == message.code && message.helperData.SequenceEqual(x.helperData)); if (queueLowPriority[msg_index].length < message.length) { queueLowPriority[msg_index] = message; } return; } } if (queueLowPriority.Exists(x => x.code == message.code && x.checksum == message.checksum && x.endpoint == message.endpoint)) { Logging.trace("Attempting to add a duplicate message (code: {0}) to the network queue", code); return; } } else { if (message.helperData != null) { if (queueLowPriority.Exists(x => x.code == message.code && x.helperData.SequenceEqual(message.helperData))) { int msg_index = queueLowPriority.FindIndex(x => x.code == message.code && message.helperData.SequenceEqual(x.helperData)); if (queueLowPriority[msg_index].length < message.length) { queueLowPriority[msg_index] = message; } return; } } if (queueLowPriority.Exists(x => x.code == message.code && x.checksum == message.checksum)) { Logging.trace("Attempting to add a duplicate message (code: {0}) to the network queue", code); return; } } bool add = true; if (queueLowPriority.Count > 20) { switch (code) { #pragma warning disable CS0618 // Type or member is obsolete case ProtocolMessageCode.getTransaction: case ProtocolMessageCode.getTransaction2: case ProtocolMessageCode.getTransaction3: case ProtocolMessageCode.getTransactions: case ProtocolMessageCode.getTransactions2: case ProtocolMessageCode.getBlock: case ProtocolMessageCode.getBlock2: case ProtocolMessageCode.getBlock3: case ProtocolMessageCode.getBlockHeaders: case ProtocolMessageCode.getBlockHeaders2: case ProtocolMessageCode.newBlock: case ProtocolMessageCode.blockData: case ProtocolMessageCode.getSignatures: case ProtocolMessageCode.getBlockSignatures2: case ProtocolMessageCode.getPIT: case ProtocolMessageCode.getPIT2: case ProtocolMessageCode.inventory: case ProtocolMessageCode.inventory2: #pragma warning restore CS0618 // Type or member is obsolete { queueLowPriority.Insert(5, message); add = false; break; } } } if (add) { // Add it to the tx queue queueLowPriority.Add(message); } return; } } lock (queueHighPriority) { // ignore duplicates if (queueHighPriority.Exists(x => x.code == message.code && x.checksum == message.checksum && x.endpoint == message.endpoint)) { Logging.trace("Attempting to add a duplicate message (code: {0}) to the network queue", code); return; } // Handle normal messages, but prioritize block-related messages switch (code) { case ProtocolMessageCode.bye: case ProtocolMessageCode.hello: case ProtocolMessageCode.helloData: queueHighPriority.Insert(0, message); return; case ProtocolMessageCode.keepAlivePresence: case ProtocolMessageCode.getPresence: case ProtocolMessageCode.getPresence2: case ProtocolMessageCode.updatePresence: // Prioritize if queue is large if (queueHighPriority.Count > 10) { queueHighPriority.Insert(5, message); return; } break; } // Add it to the normal queue queueHighPriority.Add(message); } }