/// <summary> /// Verifies that the given remote endpoint is reachable by connecting to it and sending a short message. /// </summary> /// <remarks> /// This function is used to ensure that the remote endpoing has listed the correct IP and port information for their `PresenceList` entry. /// </remarks> /// <param name="endpoint">Target endpoint to verify for connectivity.</param> /// <returns>True, if the endpoing is connectable.</returns> public static bool checkNodeConnectivity(RemoteEndpoint endpoint) { // TODO TODO TODO TODO we should put this in a separate thread string hostname = endpoint.getFullAddress(true); if (CoreNetworkUtils.PingAddressReachable(hostname) == false) { Logging.warn("Node {0} was not reachable on the advertised address.", hostname); CoreProtocolMessage.sendBye(endpoint, ProtocolByeCode.notConnectable, "External " + hostname + " not reachable!", ""); return(false); } 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)) { 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()); } }
/// <summary> /// Processes a Hello Ixian protocol message and updates the `PresenceList` as appropriate. /// </summary> /// <remarks> /// This function should normally be called from `NetworkProtocol.parseProtocolMessage()` /// </remarks> /// <param name="endpoint">Remote endpoint from which the message was received.</param> /// <param name="reader">Reader object placed at the beginning of the hello message data.</param> /// <returns>True if the message was formatted properly and accepted.</returns> public static bool processHelloMessage(RemoteEndpoint endpoint, BinaryReader reader) { // Node already has a presence if (endpoint.presence != null) { // Ignore the hello message in this case return(false); } // Another layer to catch any incompatible node exceptions for the hello message try { int protocol_version = reader.ReadInt32(); Logging.info(string.Format("Received Hello: Node version {0}", protocol_version)); // Check for incompatible nodes if (protocol_version < CoreConfig.protocolVersion) { Logging.warn(String.Format("Hello: Connected node version ({0}) is too old! Upgrade the node.", protocol_version)); sendBye(endpoint, ProtocolByeCode.deprecated, string.Format("Your node version is too old. Should be at least {0} is {1}", CoreConfig.protocolVersion, protocol_version), CoreConfig.protocolVersion.ToString(), true); return(false); } int addrLen = reader.ReadInt32(); byte[] addr = reader.ReadBytes(addrLen); bool test_net = reader.ReadBoolean(); char node_type = reader.ReadChar(); string node_version = reader.ReadString(); string device_id = reader.ReadString(); int pkLen = reader.ReadInt32(); byte[] pubkey = null; if (pkLen > 0) { pubkey = reader.ReadBytes(pkLen); } endpoint.serverPubKey = pubkey; int port = reader.ReadInt32(); long timestamp = reader.ReadInt64(); int sigLen = reader.ReadInt32(); byte[] signature = reader.ReadBytes(sigLen); // Check the testnet designator and disconnect on mismatch if (test_net != CoreConfig.isTestNet) { Logging.warn(string.Format("Rejected node {0} due to incorrect testnet designator: {1}", endpoint.fullAddress, test_net)); sendBye(endpoint, ProtocolByeCode.incorrectNetworkType, string.Format("Incorrect testnet designator: {0}. Should be {1}", test_net, CoreConfig.isTestNet), test_net.ToString(), true); return(false); } // Check the address and pubkey and disconnect on mismatch if (!addr.SequenceEqual((new Address(pubkey)).address)) { Logging.warn(string.Format("Pubkey and address do not match.")); sendBye(endpoint, ProtocolByeCode.authFailed, "Pubkey and address do not match.", "", true); return(false); } endpoint.incomingPort = port; // Verify the signature if (node_type == 'C') { // TODO: verify if the client is connectable, then if connectable, check if signature verifies /*if (CryptoManager.lib.verifySignature(Encoding.UTF8.GetBytes(ConsensusConfig.ixianChecksumLockString + "-" + device_id + "-" + timestamp + "-" + endpoint.getFullAddress(true)), pubkey, signature) == false) * { * CoreProtocolMessage.sendBye(endpoint, ProtocolByeCode.incorrectIp, "Verify signature failed in hello message, likely an incorrect IP was specified. Detected IP:", endpoint.address); * Logging.warn(string.Format("Connected node used an incorrect signature in hello message, likely an incorrect IP was specified. Detected IP: {0}", endpoint.address)); * return false; * }*/ // TODO store the full address if connectable // Store the presence address for this remote endpoint endpoint.presenceAddress = new PresenceAddress(device_id, "", node_type, node_version, Clock.getNetworkTimestamp() - CoreConfig.clientKeepAliveInterval, null); } else { if (!CryptoManager.lib.verifySignature(Encoding.UTF8.GetBytes(ConsensusConfig.ixianChecksumLockString + "-" + device_id + "-" + timestamp + "-" + endpoint.getFullAddress(true)), pubkey, signature)) { CoreProtocolMessage.sendBye(endpoint, ProtocolByeCode.incorrectIp, "Verify signature failed in hello message, likely an incorrect IP was specified. Detected IP:", endpoint.address); Logging.warn(string.Format("Connected node used an incorrect signature in hello message, likely an incorrect IP was specified. Detected IP: {0}", endpoint.address)); return(false); } // Store the presence address for this remote endpoint endpoint.presenceAddress = new PresenceAddress(device_id, endpoint.getFullAddress(true), node_type, node_version, Clock.getNetworkTimestamp() - CoreConfig.serverKeepAliveInterval, null); } // if we're a client update the network time difference if (endpoint.GetType() == typeof(NetworkClient)) { long timeDiff = endpoint.calculateTimeDifference(); // amortize +- 2 seconds if (timeDiff >= -2 && timeDiff <= 2) { timeDiff = 0; } ((NetworkClient)endpoint).timeDifference = timeDiff; // Check the address and local address and disconnect on mismatch if (endpoint.serverWalletAddress != null && !addr.SequenceEqual(endpoint.serverWalletAddress)) { Logging.warn(string.Format("Local address mismatch, possible Man-in-the-middle attack.")); sendBye(endpoint, ProtocolByeCode.addressMismatch, "Local address mismatch.", "", true); return(false); } } // Create a temporary presence with the client's address and device id Presence presence = new Presence(addr, pubkey, null, endpoint.presenceAddress); if (endpoint.GetType() != typeof(NetworkClient)) { // we're the server if (node_type == 'M' || node_type == 'H' || node_type == 'R') { if (node_type != 'R') { // Check the wallet balance for the minimum amount of coins IxiNumber balance = IxianHandler.getWalletBalance(addr); if (balance < ConsensusConfig.minimumMasterNodeFunds) { Logging.warn(string.Format("Rejected master node {0} due to insufficient funds: {1}", endpoint.getFullAddress(), balance.ToString())); sendBye(endpoint, ProtocolByeCode.insufficientFunds, string.Format("Insufficient funds. Minimum is {0}", ConsensusConfig.minimumMasterNodeFunds), balance.ToString(), true); return(false); } } // Limit to one IP per masternode // TODO TODO TODO - think about this and do it properly /*string[] hostname_split = hostname.Split(':'); * if (PresenceList.containsIP(hostname_split[0], 'M')) * { * using (MemoryStream m2 = new MemoryStream()) * { * using (BinaryWriter writer = new BinaryWriter(m2)) * { * writer.Write(string.Format("This IP address ( {0} ) already has a masternode connected.", hostname_split[0])); * Logging.info(string.Format("Rejected master node {0} due to duplicate IP address", hostname)); * socket.Send(prepareProtocolMessage(ProtocolMessageCode.bye, m2.ToArray()), SocketFlags.None); * socket.Disconnect(true); * return; * } * } * }*/ if (!checkNodeConnectivity(endpoint)) { return(false); } } } // Retrieve the final presence entry from the list (or create a fresh one) endpoint.presence = PresenceList.updateEntry(presence); } catch (Exception e) { // Disconnect the node in case of any reading errors Logging.warn(string.Format("Exception occured in Hello Message {0}", e.ToString())); sendBye(endpoint, ProtocolByeCode.deprecated, "Something went wrong during hello, make sure you're running the latest version of Ixian DLT.", ""); return(false); } if (NetworkClientManager.getConnectedClients().Count() == 1) { PresenceList.forceSendKeepAlive = true; } return(true); }
/// <summary> /// Prepares and sends the disconnect message to the specified remote endpoint. /// </summary> /// <param name="endpoint">Remote client.</param> /// <param name="code">Disconnection reason.</param> /// <param name="message">Optional text message for the user of the remote client.</param> /// <param name="data">Optional payload to further explain the disconnection reason.</param> /// <param name="removeAddressEntry">If true, the remote address will be removed from the `PresenceList`.</param> public static void sendBye(RemoteEndpoint endpoint, ProtocolByeCode code, string message, string data, bool removeAddressEntry = true) { using (MemoryStream m2 = new MemoryStream()) { using (BinaryWriter writer = new BinaryWriter(m2)) { writer.Write((int)code); writer.Write(message); writer.Write(data); #if TRACE_MEMSTREAM_SIZES Logging.info(String.Format("CoreProtocolMessage::sendBye: {0}", m2.Length)); #endif endpoint.sendData(ProtocolMessageCode.bye, m2.ToArray()); Logging.info("Sending bye to {0} with message '{1}' and data '{2}'", endpoint.getFullAddress(), message, data); } } if (removeAddressEntry) { if (endpoint.presence != null && endpoint.presence.wallet != null && endpoint.presenceAddress != null) { PresenceList.removeAddressEntry(endpoint.presence.wallet, endpoint.presenceAddress); } //PeerStorage.removePeer(endpoint.getFullAddress(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, IxianHandler.getWalletStorage().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)) { 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(); // deprecated 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; } 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.sendGetMessages(f); } } if (endpoint.presenceAddress.type == 'M') { 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); } break; case ProtocolMessageCode.keepAlivePresence: { byte[] address = null; bool updated = PresenceList.receiveKeepAlive(data, out 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: { 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())) { Node.balance = balance; } // Retrieve the blockheight for the balance ulong blockheight = reader.ReadUInt64(); Node.blockHeight = blockheight; } } } break; case ProtocolMessageCode.transactionData: { // TODO: check for errors/exceptions Transaction transaction = new Transaction(data, true); TransactionCache.addTransaction(transaction); } break; case ProtocolMessageCode.newTransaction: { Transaction transaction = new Transaction(data, true); TransactionCache.addTransaction(transaction); } 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) { // TODO TODO do not set if not directly connectable 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; 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; default: break; } } catch (Exception e) { Logging.error(string.Format("Error parsing network message. Details: {0}", e.ToString())); } if (waitingFor == code) { blocked = false; } }
/// <summary> /// Prepares and sends an Ixian protocol 'Hello' message to the specified remote endpoint. /// </summary> /// <remarks> /// A valid Ixian 'Hello' message includes certain Node data, verified by a public-key signature, which this function prepares using /// the primary wallet's keypair. If this message is a reply to the other endpoint's hello message, then /// </remarks> /// <param name="endpoint">Remote endpoint to send the message to.</param> /// <param name="sendHelloData">True if the message is the first hello sent to the remote node, false if it is a reply to the challenge.</param> /// <param name="challenge_response">Response byte-field to the other node's hello challenge</param> public static void sendHelloMessageV6(RemoteEndpoint endpoint, bool sendHelloData, int challenge) { using (MemoryStream m = new MemoryStream(1856)) { using (BinaryWriter writer = new BinaryWriter(m)) { string publicHostname = IxianHandler.getFullPublicAddress(); // Send the node version writer.WriteIxiVarInt(6); // Send the public node address byte[] address = IxianHandler.getWalletStorage().getPrimaryAddress(); writer.WriteIxiVarInt(address.Length); writer.Write(address); // Send the testnet designator writer.Write(IxianHandler.isTestNet); char node_type = PresenceList.myPresenceType; writer.Write(node_type); // Send the version writer.Write(CoreConfig.productVersion); // Send the node device id writer.WriteIxiVarInt(CoreConfig.device_id.Length); writer.Write(CoreConfig.device_id); // Send the wallet public key writer.WriteIxiVarInt(IxianHandler.getWalletStorage().getPrimaryPublicKey().Length); writer.Write(IxianHandler.getWalletStorage().getPrimaryPublicKey()); // Send listening port writer.WriteIxiVarInt(IxianHandler.publicPort); // Send timestamp long timestamp = Clock.getTimestamp() + endpoint.calculateTimeDifference(); writer.WriteIxiVarInt(timestamp); // generate signature using (MemoryStream mSig = new MemoryStream(1024)) { using (BinaryWriter sigWriter = new BinaryWriter(mSig)) { sigWriter.Write(ConsensusConfig.ixianChecksumLock); sigWriter.Write(CoreConfig.device_id); sigWriter.Write(timestamp); sigWriter.Write(publicHostname); sigWriter.Write(challenge); } byte[] signature = CryptoManager.lib.getSignature(mSig.ToArray(), IxianHandler.getWalletStorage().getPrimaryPrivateKey()); writer.WriteIxiVarInt(signature.Length); writer.Write(signature); } if (sendHelloData) { Block block = IxianHandler.getLastBlock(); if (block == null) { Logging.warn("Clients are connecting, but we have no blocks yet to send them!"); sendBye(endpoint, ProtocolByeCode.notReady, string.Format("The node isn't ready yet, please try again later."), "", true); return; } writer.WriteIxiVarInt(block.blockNum); writer.WriteIxiVarInt(block.blockChecksum.Length); writer.Write(block.blockChecksum); writer.WriteIxiVarInt(block.version); writer.Write(endpoint.getFullAddress(true)); #if TRACE_MEMSTREAM_SIZES Logging.info(String.Format("CoreProtocolMessage::sendHelloMessage: {0}", m.Length)); #endif endpoint.sendData(ProtocolMessageCode.helloData, m.ToArray()); } else { byte[] challenge_bytes = IxiVarInt.GetIxiVarIntBytes(challenge); endpoint.challenge = BitConverter.GetBytes(challenge); writer.Write(challenge_bytes); #if TRACE_MEMSTREAM_SIZES Logging.info(String.Format("CoreProtocolMessage::sendHelloMessage: {0}", m.Length)); #endif endpoint.sendData(ProtocolMessageCode.hello, m.ToArray()); } } } }