예제 #1
0
        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();
        }
예제 #2
0
        static public void connectToNetwork()
        {
            // Start the network client manager
            NetworkClientManager.start(2);

            // Start the s2 client manager
            StreamClientManager.start();
        }
예제 #3
0
        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();
        }
예제 #4
0
파일: Node.cs 프로젝트: tomaszlt/Spixi
        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);
             *       }*/
        }
예제 #6
0
        // 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());
            }
        }
예제 #7
0
        // 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;
            }
        }