Esempio n. 1
0
 public StreamMessage()
 {
     id             = Guid.NewGuid().ToByteArray(); // Generate a new unique id
     type           = StreamMessageCode.info;
     sender         = null;
     recipient      = null;
     data           = null;
     encryptionType = StreamMessageEncryptionCode.spixi1;
     timestamp      = Clock.getNetworkTimestamp();
 }
Esempio n. 2
0
        private static byte[] keepAlive_v2()
        {
            // Prepare the keepalive message
            using (MemoryStream m = new MemoryStream(640))
            {
                using (BinaryWriter writer = new BinaryWriter(m))
                {
                    writer.WriteIxiVarInt(2); // version

                    byte[] wallet = IxianHandler.getWalletStorage().getPrimaryAddress();
                    writer.WriteIxiVarInt(wallet.Length);
                    writer.Write(wallet);

                    writer.WriteIxiVarInt(CoreConfig.device_id.Length);
                    writer.Write(CoreConfig.device_id);

                    // Add the unix timestamp
                    long timestamp = Clock.getNetworkTimestamp();
                    writer.WriteIxiVarInt(timestamp);

                    string hostname = curNodePresenceAddress.address;
                    writer.Write(hostname);
                    writer.Write(PresenceList.curNodePresenceAddress.type);

                    // Add a verifiable signature
                    byte[] private_key = IxianHandler.getWalletStorage().getPrimaryPrivateKey();

                    byte[] checksum           = Crypto.sha512sq(m.ToArray());
                    byte[] checksum_with_lock = new byte[ConsensusConfig.ixianChecksumLock.Length + checksum.Length];
                    Array.Copy(ConsensusConfig.ixianChecksumLock, checksum_with_lock, ConsensusConfig.ixianChecksumLock.Length);
                    Array.Copy(checksum, 0, checksum_with_lock, ConsensusConfig.ixianChecksumLock.Length, checksum.Length);
                    byte[] signature = CryptoManager.lib.getSignature(checksum_with_lock, private_key);
                    writer.WriteIxiVarInt(signature.Length);
                    writer.Write(signature);

                    PresenceList.curNodePresenceAddress.lastSeenTime = timestamp;
                    PresenceList.curNodePresenceAddress.signature    = signature;

#if TRACE_MEMSTREAM_SIZES
                    Logging.info(String.Format("PresenceList::keepAlive_v1: {0}", m.Length));
#endif
                }

                return(m.ToArray());
            }
        }
Esempio n. 3
0
        private static byte[] keepAlive_v1()
        {
            // Prepare the keepalive message
            using (MemoryStream m = new MemoryStream(640))
            {
                using (BinaryWriter writer = new BinaryWriter(m))
                {
                    writer.Write(1); // version

                    byte[] wallet = IxianHandler.getWalletStorage().getPrimaryAddress();
                    writer.Write(wallet.Length);
                    writer.Write(wallet);

                    writer.Write(new System.Guid(CoreConfig.device_id).ToString());

                    // Add the unix timestamp
                    long timestamp = Clock.getNetworkTimestamp();
                    writer.Write(timestamp);

                    string hostname = curNodePresenceAddress.address;
                    writer.Write(hostname);
                    writer.Write(PresenceList.curNodePresenceAddress.type);

                    // Add a verifiable signature
                    byte[] private_key = IxianHandler.getWalletStorage().getPrimaryPrivateKey();
                    byte[] signature   = CryptoManager.lib.getSignature(m.ToArray(), private_key);
                    writer.Write(signature.Length);
                    writer.Write(signature);

                    PresenceList.curNodePresenceAddress.lastSeenTime = timestamp;
                    PresenceList.curNodePresenceAddress.signature    = signature;

#if TRACE_MEMSTREAM_SIZES
                    Logging.info(String.Format("PresenceList::keepAlive_v1: {0}", m.Length));
#endif
                }

                return(m.ToArray());
            }
        }
Esempio n. 4
0
        // Perform routine PL cleanup
        public static bool performCleanup()
        {
            // Get the current timestamp
            long currentTime = Clock.getNetworkTimestamp();

            lock (presences)
            {
                // Store a copy of the presence list to allow safe modifications while enumerating
                List <Presence> safe_presences = new List <Presence>(presences);

                foreach (Presence pr in safe_presences)
                {
                    lock (pr)
                    {
                        if (pr.addresses.Count == 0)
                        {
                            presences.Remove(pr);
                            continue;
                        }

                        List <PresenceAddress> safe_addresses = new List <PresenceAddress>(pr.addresses);

                        foreach (PresenceAddress pa in safe_addresses)
                        {
                            // Don't remove self address from presence list

                            /*if(pa == curNodePresenceAddress)
                             * {
                             *  continue;
                             * }*/

                            try
                            {
                                int expiration_time = CoreConfig.serverPresenceExpiration;

                                if (pa.type == 'C')
                                {
                                    expiration_time = CoreConfig.clientPresenceExpiration;
                                }

                                // Check if timestamp is older than 300 seconds
                                if ((currentTime - pa.lastSeenTime) > expiration_time)
                                {
                                    Logging.info(string.Format("Expired lastseen for {0} / {1}", pa.address, pa.device));
                                    removeAddressEntry(pr.wallet, pa);
                                }
                                else if ((currentTime - pa.lastSeenTime) < -30) // future time + 30 seconds amortization
                                {
                                    Logging.info(string.Format("Expired future lastseen for {0} / {1}", pa.address, pa.device));
                                    removeAddressEntry(pr.wallet, pa);
                                }
                            }
                            catch (Exception e)
                            {
                                // Ignore this entry for now
                                Logging.error("Exception occured in PL performCleanup: " + e);
                                continue;
                            }
                        }

                        // Clear the safe list of addresses
                        safe_addresses.Clear();
                    }
                }

                // Clear the safe list of presences
                safe_presences.Clear();
            }

            return(true);
        }
Esempio n. 5
0
        // Update a presence entry. If the wallet address is not found, it creates a new entry in the Presence List.
        // If the wallet address is found in the presence list, it adds any new addresses from the specified presence.
        public static Presence updateEntry(Presence presence, bool return_presence_only_if_updated = false)
        {
            if (presence == null)
            {
                return(null);
            }

            bool updated = false;

            long currentTime = Clock.getNetworkTimestamp();

            lock (presences)
            {
                Presence pr = presences.Find(x => x.wallet.SequenceEqual(presence.wallet));
                if (pr != null)
                {
                    lock (pr)
                    {
                        // Go through all addresses and add any missing ones
                        foreach (PresenceAddress local_addr in presence.addresses)
                        {
                            long lTimestamp = local_addr.lastSeenTime;

                            int expiration_time = CoreConfig.serverPresenceExpiration;

                            if (local_addr.type == 'C')
                            {
                                expiration_time = CoreConfig.clientPresenceExpiration;
                            }

                            // Check for tampering. Includes a +300, -30 second synchronization zone
                            if ((currentTime - lTimestamp) > expiration_time)
                            {
                                Logging.warn(string.Format("[PL] Received expired presence for {0} {1}. Skipping; {2} - {3}", Crypto.hashToString(pr.wallet), local_addr.address, currentTime, lTimestamp));
                                continue;
                            }

                            if ((currentTime - lTimestamp) < -30)
                            {
                                Logging.warn(string.Format("[PL] Potential presence tampering for {0} {1}. Skipping; {2} - {3}", Crypto.hashToString(pr.wallet), local_addr.address, currentTime, lTimestamp));
                                continue;
                            }


                            PresenceAddress addr = pr.addresses.Find(x => x.device == local_addr.device);
                            if (addr != null)
                            {
                                if (addr.lastSeenTime < local_addr.lastSeenTime)
                                {
                                    if (local_addr.signature != null)
                                    {
                                        addr.version      = local_addr.version;
                                        addr.address      = local_addr.address;
                                        addr.lastSeenTime = local_addr.lastSeenTime;
                                        addr.signature    = local_addr.signature;
                                        updated           = true;
                                    }

                                    if (addr.type == 'M' || addr.type == 'H')
                                    {
                                        PeerStorage.addPeerToPeerList(addr.address, presence.wallet);
                                    }

                                    //Console.WriteLine("[PL] Last time updated for {0}", addr.device);
                                }
                            }
                            else
                            {
                                // Add the address if it's not found
                                //Logging.info("[PL] Adding new address for {0}", presence.wallet);
                                pr.addresses.Add(local_addr);

                                if (local_addr.type == 'M' || local_addr.type == 'H')
                                {
                                    PeerStorage.addPeerToPeerList(local_addr.address, presence.wallet);
                                }

                                lock (presenceCount)
                                {
                                    if (!presenceCount.ContainsKey(local_addr.type))
                                    {
                                        presenceCount.Add(local_addr.type, 0);
                                    }
                                    presenceCount[local_addr.type]++;
                                }

                                updated = true;
                            }
                        }

                        if (!updated && return_presence_only_if_updated)
                        {
                            return(null);
                        }
                        else
                        {
                            return(pr);
                        }
                    }
                }
                else
                {
                    // Insert a new entry
                    //Console.WriteLine("[PL] Adding new entry for {0}", presence.wallet);

                    foreach (PresenceAddress pa in presence.addresses)
                    {
                        if (pa.type == 'M' || pa.type == 'H')
                        {
                            PeerStorage.addPeerToPeerList(pa.address, presence.wallet);
                        }

                        lock (presenceCount)
                        {
                            if (!presenceCount.ContainsKey(pa.type))
                            {
                                presenceCount.Add(pa.type, 0);
                            }
                            presenceCount[pa.type]++;
                        }

                        updated = true;
                    }

                    if (updated)
                    {
                        presences.Add(presence);
                    }

                    if (!updated && return_presence_only_if_updated)
                    {
                        return(null);
                    }
                    else
                    {
                        return(presence);
                    }
                }
            }
        }
Esempio n. 6
0
        // Called when receiving a keepalive network message. The PresenceList will update the appropriate entry based on the timestamp.
        // Returns TRUE if it updated an entry in the PL
        // Sets the out address parameter to be the KA wallet's address or null if an error occured
        public static bool receiveKeepAlive(byte[] bytes, out byte[] address, RemoteEndpoint endpoint)
        {
            address = null;

            // Get the current timestamp
            long currentTime = Clock.getNetworkTimestamp();

            try
            {
                using (MemoryStream m = new MemoryStream(bytes))
                {
                    using (BinaryReader reader = new BinaryReader(m))
                    {
                        int    keepAliveVersion = reader.ReadInt32();
                        int    walletLen        = reader.ReadInt32();
                        byte[] wallet           = reader.ReadBytes(walletLen);

                        // Assign the out address parameter
                        address = wallet;

                        string deviceid  = reader.ReadString();
                        long   timestamp = reader.ReadInt64();
                        string hostname  = reader.ReadString();
                        char   node_type = '0';

                        node_type = reader.ReadChar();

                        int    sigLen    = reader.ReadInt32();
                        byte[] signature = reader.ReadBytes(sigLen);
                        //Logging.info(String.Format("[PL] KEEPALIVE request from {0}", hostname));

                        if (node_type == 'C' || node_type == 'R')
                        {
                            // all good, continue
                        }
                        else if (node_type == 'M' || node_type == 'H')
                        {
                            if (myPresenceType == 'M' || myPresenceType == 'H')
                            {
                                // check balance
                                if (IxianHandler.getWalletBalance(wallet) < ConsensusConfig.minimumMasterNodeFunds)
                                {
                                    return(false);
                                }
                            }
                        }
                        else
                        {
                            // reject everything else
                            return(false);
                        }

                        lock (presences)
                        {
                            Presence listEntry = presences.Find(x => x.wallet.SequenceEqual(wallet));
                            if (listEntry == null && wallet.SequenceEqual(IxianHandler.getWalletStorage().getPrimaryAddress()))
                            {
                                Logging.warn("My entry was removed from local PL, readding.");
                                curNodePresence.addresses.Clear();
                                curNodePresence.addresses.Add(curNodePresenceAddress);
                                updateEntry(curNodePresence);
                                listEntry = presences.Find(x => x.wallet.SequenceEqual(wallet));
                            }

                            // Check if no such wallet found in presence list
                            if (listEntry == null)
                            {
                                // request for additional data
                                using (MemoryStream mw = new MemoryStream())
                                {
                                    using (BinaryWriter writer = new BinaryWriter(mw))
                                    {
                                        writer.Write(wallet.Length);
                                        writer.Write(wallet);

                                        if (endpoint != null && endpoint.isConnected())
                                        {
                                            endpoint.sendData(ProtocolMessageCode.getPresence, mw.ToArray(), wallet);
                                        }
                                        else
                                        {
                                            CoreProtocolMessage.broadcastProtocolMessageToSingleRandomNode(new char[] { 'M', 'R', 'H' }, ProtocolMessageCode.getPresence, mw.ToArray(), 0, null);
                                        }
                                    }
                                }
                                return(false);
                            }

                            // Verify the signature
                            if (CryptoManager.lib.verifySignature(bytes.Take(bytes.Length - sigLen - 4).ToArray(), listEntry.pubkey, signature) == false)
                            {
                                Logging.warn(string.Format("[PL] KEEPALIVE tampering for {0} {1}, incorrect Sig.", Base58Check.Base58CheckEncoding.EncodePlain(listEntry.wallet), hostname));
                                return(false);
                            }

                            PresenceAddress pa = listEntry.addresses.Find(x => x.address == hostname && x.device == deviceid);

                            if (pa != null)
                            {
                                // Check the node type
                                if (pa.lastSeenTime != timestamp)
                                {
                                    // Check for outdated timestamp
                                    if (timestamp < pa.lastSeenTime)
                                    {
                                        // We already have a newer timestamp for this entry
                                        return(false);
                                    }

                                    int expiration_time = CoreConfig.serverPresenceExpiration;

                                    if (pa.type == 'C')
                                    {
                                        expiration_time = CoreConfig.clientPresenceExpiration;
                                    }

                                    // Check for tampering. Includes a +300, -30 second synchronization zone
                                    if ((currentTime - timestamp) > expiration_time)
                                    {
                                        Logging.warn(string.Format("[PL] Received expired KEEPALIVE for {0} {1}. Timestamp {2}", Base58Check.Base58CheckEncoding.EncodePlain(listEntry.wallet), pa.address, timestamp));
                                        return(false);
                                    }

                                    if ((currentTime - timestamp) < -30)
                                    {
                                        Logging.warn(string.Format("[PL] Potential KEEPALIVE tampering for {0} {1}. Timestamp {2}", Base58Check.Base58CheckEncoding.EncodePlain(listEntry.wallet), pa.address, timestamp));
                                        return(false);
                                    }

                                    // Update the timestamp
                                    pa.lastSeenTime = timestamp;
                                    pa.signature    = signature;
                                    pa.version      = keepAliveVersion;
                                    if (pa.type != node_type)
                                    {
                                        lock (presenceCount)
                                        {
                                            presenceCount[pa.type]--;
                                            if (!presenceCount.ContainsKey(node_type))
                                            {
                                                presenceCount.Add(node_type, 0);
                                            }
                                            presenceCount[node_type]++;
                                        }
                                    }
                                    pa.type = node_type;

                                    //Console.WriteLine("[PL] LASTSEEN for {0} - {1} set to {2}", hostname, deviceid, pa.lastSeenTime);
                                    return(true);
                                }
                            }
                            else
                            {
                                if (wallet.SequenceEqual(IxianHandler.getWalletStorage().getPrimaryAddress()))
                                {
                                    curNodePresence.addresses.Clear();
                                    curNodePresence.addresses.Add(curNodePresenceAddress);
                                    updateEntry(curNodePresence);
                                    return(true);
                                }
                                else
                                {
                                    using (MemoryStream mw = new MemoryStream())
                                    {
                                        using (BinaryWriter writer = new BinaryWriter(mw))
                                        {
                                            writer.Write(wallet.Length);
                                            writer.Write(wallet);

                                            if (endpoint != null && endpoint.isConnected())
                                            {
                                                endpoint.sendData(ProtocolMessageCode.getPresence, mw.ToArray(), wallet);
                                            }
                                            else
                                            {
                                                CoreProtocolMessage.broadcastProtocolMessageToSingleRandomNode(new char[] { 'M', 'R', 'H' }, ProtocolMessageCode.getPresence, mw.ToArray(), 0, null);
                                            }
                                        }
                                    }
                                    return(false);
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Logging.error("Exception occured in receiveKeepAlive: " + e);
                return(false);
            }

            return(false);
        }
Esempio n. 7
0
        /*public static bool syncFromBytes(byte[] bytes)
         * {
         *  using (MemoryStream m = new MemoryStream(bytes))
         *  {
         *      using (BinaryReader reader = new BinaryReader(m))
         *      {
         *          // Read the number of presences
         *          int num_presences = reader.ReadInt32();
         *          if (num_presences < 0)
         *              return false;
         *
         *          try
         *          {
         *              for (int i = 0; i < num_presences; i++)
         *              {
         *                  int presence_data_size = reader.ReadInt32();
         *                  if (presence_data_size < 1)
         *                      continue;
         *                  byte[] presence_bytes = reader.ReadBytes(presence_data_size);
         *                  Presence new_presence = new Presence(presence_bytes);
         *                  lock (presences)
         *                  {
         *                      presences.Add(new_presence);
         *                  }
         *                  lock (presenceCount)
         *                  {
         *                      foreach (PresenceAddress pa in new_presence.addresses)
         *                      {
         *                          if (!presenceCount.ContainsKey(pa.type))
         *                          {
         *                              presenceCount.Add(pa.type, 0);
         *                          }
         *                          presenceCount[pa.type]++;
         *                      }
         *                  }
         *
         *              }
         *          }
         *          catch (Exception e)
         *          {
         *              Logging.error(string.Format("Error reading presence list: {0}", e.ToString()));
         *              return false;
         *          }
         *
         *      }
         *  }
         *
         *  return true;
         * }*/

        public static bool verifyPresence(Presence presence)
        {
            if (presence.wallet.Length > 128 && presence.wallet.Length < 4)
            {
                return(false);
            }

            if (presence.pubkey == null || presence.pubkey.Length < 32 || presence.pubkey.Length > 2500)
            {
                return(false);
            }

            List <PresenceAddress> valid_addresses = new List <PresenceAddress>();

            long currentTime = Clock.getNetworkTimestamp();

            foreach (var entry in presence.addresses)
            {
                if (entry.device.Length > 64)
                {
                    continue;
                }

                if (entry.nodeVersion.Length > 64)
                {
                    continue;
                }

                if (entry.address.Length > 24 && entry.address.Length < 9)
                {
                    continue;
                }

                long lTimestamp = entry.lastSeenTime;

                int expiration_time = CoreConfig.serverPresenceExpiration;

                if (entry.type == 'C')
                {
                    expiration_time = CoreConfig.clientPresenceExpiration;
                }

                // Check for tampering. Includes a +300, -30 second synchronization zone
                if ((currentTime - lTimestamp) > expiration_time)
                {
                    Logging.warn(string.Format("[PL] Received expired presence for {0} {1}. Skipping; {2} - {3}", Crypto.hashToString(presence.wallet), entry.address, currentTime, lTimestamp));
                    continue;
                }

                if ((currentTime - lTimestamp) < -30)
                {
                    Logging.warn(string.Format("[PL] Potential presence tampering for {0} {1}. Skipping; {2} - {3}", Crypto.hashToString(presence.wallet), entry.address, currentTime, lTimestamp));
                    continue;
                }

                if (!entry.verifySignature(presence.wallet, presence.pubkey))
                {
                    Logging.warn("Invalid presence address received in verifyPresence, signature verification failed for {0}.", Base58Check.Base58CheckEncoding.EncodePlain(presence.wallet));
                    continue;
                }

                valid_addresses.Add(entry);
            }

            if (valid_addresses.Count > 0)
            {
                presence.addresses = valid_addresses;
                return(true);
            }

            return(false);
        }
Esempio n. 8
0
        /// <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);
        }