static public void stop() { if (!running) { return; } running = false; customAppManager.stop(); // Stop TIV tiv.stop(); // Stop the transfer manager TransferManager.stop(); // Stop the keepalive thread PresenceList.stopKeepAlive(); // Stop the loop timer if (mainLoopTimer != null) { mainLoopTimer.Stop(); mainLoopTimer = null; } // Stop the network queue NetworkQueue.stop(); NetworkClientManager.stop(); StreamClientManager.stop(); // Stop the stream processor StreamProcessor.uninitialize(); }
static public void connectToNetwork() { // Start the network client manager NetworkClientManager.start(2); // Start the s2 client manager StreamClientManager.start(); }
static public void stop() { // Stop the keepalive thread PresenceList.stopKeepAlive(); // Stop the loop timer mainLoopTimer.Stop(); // Stop the network queue NetworkQueue.stop(); NetworkClientManager.stop(); StreamClientManager.stop(); // Stop the stream processor StreamProcessor.uninitialize(); }
static public void stop() { if (!running) { Logging.stop(); IxianHandler.status = NodeStatus.stopped; return; } Logging.info("Stopping node..."); running = false; // Stop the stream processor StreamProcessor.uninitialize(); localStorage.stop(); customAppManager.stop(); // Stop TIV tiv.stop(); // Stop the transfer manager TransferManager.stop(); // Stop the keepalive thread PresenceList.stopKeepAlive(); // Stop the network queue NetworkQueue.stop(); NetworkClientManager.stop(); StreamClientManager.stop(); UpdateVerify.stop(); IxianHandler.status = NodeStatus.stopped; Logging.info("Node stopped"); Logging.stop(); }
private bool sendMessage(Friend friend, PendingMessage pending_message, bool add_to_pending_messages = true) { StreamMessage msg = pending_message.streamMessage; bool send_to_server = pending_message.sendToServer; bool send_push_notification = pending_message.sendPushNotification; // TODO this function has to be improved and node's wallet address has to be added if (friend.publicKey != null || (msg.encryptionType != StreamMessageEncryptionCode.rsa && friend.aesKey != null && friend.chachaKey != null)) { if (msg.encryptionType == StreamMessageEncryptionCode.none) { if (friend.aesKey != null && friend.chachaKey != null) { // upgrade encryption type msg.encryptionType = StreamMessageEncryptionCode.spixi1; } else if (!friend.bot) { // upgrade encryption type msg.encryptionType = StreamMessageEncryptionCode.rsa; } } if (msg.encryptionType != StreamMessageEncryptionCode.none) { if (msg.version == 0 && msg.encryptionType == StreamMessageEncryptionCode.rsa && !msg.encrypted) { msg.sign(IxianHandler.getWalletStorage().getPrimaryPrivateKey()); } if (!msg.encrypt(friend.publicKey, friend.aesKey, friend.chachaKey)) { Logging.warn("Could not encrypt message for {0}!", Base58Check.Base58CheckEncoding.EncodePlain(msg.recipient)); return(false); } if (msg.version > 0 && msg.encryptionType == StreamMessageEncryptionCode.rsa) { msg.sign(IxianHandler.getWalletStorage().getPrimaryPrivateKey()); } } } else if (msg.encryptionType != StreamMessageEncryptionCode.none) { if (friend.publicKey == null) { byte[] pub_k = FriendList.findContactPubkey(friend.walletAddress); friend.setPublicKey(pub_k); } if (!friend.bot) { Logging.warn("Could not send message to {0}, due to missing encryption keys!", Base58Check.Base58CheckEncoding.EncodePlain(msg.recipient)); // Return true in case it has other messages in the queue that need to be processed and aren't encrypted return(true); } else { // TODO TODO TODO perhaps it would be better to discard such message and notify the user Logging.warn("Tried sending encrypted message of type {0} without encryption keys to {1}, which is a bot, changing message encryption type to none!", msg.type, Base58Check.Base58CheckEncoding.EncodePlain(msg.recipient)); msg.encryptionType = StreamMessageEncryptionCode.none; } } bool sent = false; string hostname = friend.searchForRelay(); // TODO cuckoo filter should be used instead, need to check the performance when PL is big if (friend.online) { if (hostname != "" && hostname != null) { StreamClientManager.connectTo(hostname, null); // TODO replace null with node address sent = StreamClientManager.sendToClient(hostname, ProtocolMessageCode.s2data, msg.getBytes(), msg.id); if (sent && pending_message.removeAfterSending) { removeMessage(friend, pending_message.streamMessage.id); } } } if (friend.forcePush || !friend.online || !sent) { if (send_to_server) { send_to_server = Config.enablePushNotifications; if (friend.bot) { send_to_server = false; send_push_notification = false; } } if (send_to_server) { if (OfflinePushMessages.sendPushMessage(msg, send_push_notification)) { pending_message.sendToServer = false; if (add_to_pending_messages) { pending_message.save(storagePath); } PendingMessageHeader tmp_msg_header = getPendingMessageHeader(friend, pending_message.streamMessage.id); if (tmp_msg_header != null) { tmp_msg_header.sendToServer = false; } if (pending_message.removeAfterSending) { removeMessage(friend, pending_message.streamMessage.id); } return(true); } } return(false); } return(true); /* string pub_k = FriendList.findContactPubkey(msg.recipientAddress); * if (pub_k.Length < 1) * { * Console.WriteLine("Contact {0} not found, adding to offline queue!", msg.recipientAddress); * addOfflineMessage(msg); * return; * } * * * // Create a new IXIAN transaction * // byte[] checksum = Crypto.sha256(encrypted_message); * Transaction transaction = new Transaction(0, msg.recipientAddress, Node.walletStorage.address); * // transaction.data = Encoding.UTF8.GetString(checksum); * msg.transactionID = transaction.id; * //ProtocolMessage.broadcastProtocolMessage(ProtocolMessageCode.newTransaction, transaction.getBytes()); * * // Add message to the queue * messages.Add(msg); * * // Request a new keypair from the S2 Node * if(hostname == null) * ProtocolMessage.broadcastProtocolMessage(ProtocolMessageCode.s2generateKeys, Encoding.UTF8.GetBytes(msg.getID())); * else * { * NetworkClientManager.sendData(ProtocolMessageCode.s2generateKeys, Encoding.UTF8.GetBytes(msg.getID()), hostname); * }*/ }
// 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)) { 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; } }