public Presence(byte[] wallet_address, byte[] node_pubkey, byte[] node_meta, PresenceAddress node_address) { wallet = wallet_address; pubkey = node_pubkey; metadata = node_meta; addresses = new List <PresenceAddress> { }; addresses.Add(node_address); }
// Generate an initial presence list public static void init(string initial_ip, int port, char type) { Logging.info("Generating presence list."); _myPublicAddress = string.Format("{0}:{1}", initial_ip, port); _myPresenceType = type; // Initialize with the default presence state curNodePresenceAddress = new PresenceAddress(CoreConfig.device_id, _myPublicAddress, type, CoreConfig.productVersion, 0, null); curNodePresence = new Presence(IxianHandler.getWalletStorage().getPrimaryAddress(), IxianHandler.getWalletStorage().getPrimaryPublicKey(), null, curNodePresenceAddress); }
// 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); } } } }
// 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); }
public static bool removeAddressEntry(byte[] wallet_address, PresenceAddress address = null) { lock (presences) { //Console.WriteLine("[PL] Received removal for {0} : {1}", wallet_address, address.address); Presence listEntry = presences.Find(x => x.wallet.SequenceEqual(wallet_address)); // Check if there is such an entry in the presence list if (listEntry != null) { lock (listEntry) { List <PresenceAddress> addresses_to_remove = null; if (address != null) { addresses_to_remove = listEntry.addresses.FindAll(x => x == address); } else { addresses_to_remove = new List <PresenceAddress>(listEntry.addresses); } foreach (var addr in addresses_to_remove) { lock (presenceCount) { if (presenceCount.ContainsKey(addr.type)) { presenceCount[addr.type]--; } } listEntry.addresses.Remove(addr); } int address_count = listEntry.addresses.Count; //Console.WriteLine("[PL] --->> Addresses: {0}", address_count); if (address_count == 0) { // Remove it from the list presences.Remove(listEntry); } // If presence address is a relay node, remove all other presences with matching ip:port // TODO: find a better way to handle this while preventing modify-during-enumeration issues if (address != null && address.type == 'R') { // Retrieve the ip+port of the relay address string relay_address = address.address; // Store a copy of the presence list to allow safe modifications while enumerating List <Presence> safe_presences = new List <Presence>(presences); // Go through the entire presence list foreach (Presence pr in safe_presences) { // Store a list of presence addresses that correspond to the relay node we're removing List <PresenceAddress> relayClients = new List <PresenceAddress>(); foreach (PresenceAddress pa in pr.addresses) { if (pa.address.SequenceEqual(relay_address)) { // Check if it's a client node if (pa.type == 'C') { relayClients.Add(pa); } } } // Check if the presence contains at least one relay client if (relayClients.Count > 0) { // Go through each relay client and safely remove it's address entry // Note that it also propagates network messages foreach (PresenceAddress par in relayClients) { removeAddressEntry(pr.wallet, par); } relayClients.Clear(); } } // Clear the safe list of presences safe_presences.Clear(); } return(true); } } } return(false); }