// Generate an initial presence list public static void generatePresenceList(string initial_ip, char type = 'M') { Logging.info("Generating presence list."); // Initialize with the default presence state curNodePresenceAddress = new PresenceAddress(Config.device_id, string.Format("{0}:{1}", initial_ip, Config.serverPort), type, Config.version, 0, null); curNodePresence = new Presence(Node.walletStorage.getPrimaryAddress(), Node.walletStorage.getPrimaryPublicKey(), null, curNodePresenceAddress); }
public Presence(byte[] wallet_address, byte[] node_pubkey, byte[] node_meta, PresenceAddress node_address) { version = 0; wallet = wallet_address; pubkey = node_pubkey; metadata = node_meta; addresses = new List <PresenceAddress> { }; addresses.Add(node_address); owner = " "; }
public Presence(byte[] wallet_address, byte[] node_pubkey, string node_ip, char node_type, string node_version) { version = 0; wallet = wallet_address; pubkey = node_pubkey; metadata = null; addresses = new List <PresenceAddress> { }; // Generate a device id string deviceId = Guid.NewGuid().ToString(); PresenceAddress address = new PresenceAddress(deviceId, node_ip, node_type, node_version, 0, null); addresses.Add(address); owner = " "; }
public void start(Socket socket = null) { if (fullyStopped) { Logging.error("Can't start a fully stopped RemoteEndpoint"); return; } if (running) { return; } if (socket != null) { clientSocket = socket; } if (clientSocket == null) { Logging.error("Could not start NetworkRemoteEndpoint, socket is null"); return; } prepareSocket(clientSocket); remoteIP = (IPEndPoint)clientSocket.RemoteEndPoint; address = remoteIP.Address.ToString(); fullAddress = address + ":" + remoteIP.Port; presence = null; presenceAddress = null; lock (subscribedAddresses) { subscribedAddresses.Clear(); } lastDataReceivedTime = Clock.getTimestamp(); lastDataSentTime = Clock.getTimestamp(); state = RemoteEndpointState.Established; timeDifference = 0; timeSyncComplete = false; timeSyncs.Clear(); running = true; // Abort all related threads if (recvThread != null) { recvThread.Abort(); recvThread = null; } if (sendThread != null) { sendThread.Abort(); sendThread = null; } if (parseThread != null) { parseThread.Abort(); parseThread = null; } try { TLC = new ThreadLiveCheck(); // Start receive thread recvThread = new Thread(new ThreadStart(recvLoop)); recvThread.Name = "Network_Remote_Endpoint_Receive_Thread"; recvThread.Start(); // Start send thread sendThread = new Thread(new ThreadStart(sendLoop)); sendThread.Name = "Network_Remote_Endpoint_Send_Thread"; sendThread.Start(); // Start parse thread parseThread = new Thread(new ThreadStart(parseLoop)); parseThread.Name = "Network_Remote_Endpoint_Parse_Thread"; parseThread.Start(); } catch (Exception e) { Logging.error("Exception start remote endpoint: {0}", e.Message); } }
// 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, RemoteEndpoint skipEndpoint = null) { if (presence == null) { return(null); } bool entryUpdated = false; Presence return_presence = null; lock (presences) { Presence pr = presences.Find(x => x.wallet.SequenceEqual(presence.wallet)); if (pr != null) { entryUpdated = false; // Go through all addresses and add any missing ones foreach (PresenceAddress local_addr in presence.addresses) { long currentTime = Core.getCurrentTimestamp(); long lTimestamp = local_addr.lastSeenTime; // Check for tampering. Includes a +300, -30 second synchronization zone if ((currentTime - lTimestamp) > 300 || (currentTime - lTimestamp) < -30) { Logging.warn(string.Format("[PL] Potential KEEPALIVE tampering for {0} {1}. Skipping; {2} - {3}", Crypto.hashToString(pr.wallet), local_addr.address, currentTime, lTimestamp)); continue; } bool addressfound = false; PresenceAddress addr = pr.addresses.Find(x => x.device == local_addr.device); if (addr != null) { addressfound = true; if (addr.address != local_addr.address) { addr.address = local_addr.address; addr.lastSeenTime = local_addr.lastSeenTime; entryUpdated = true; } else if (addr.lastSeenTime < local_addr.lastSeenTime) { addr.lastSeenTime = local_addr.lastSeenTime; entryUpdated = true; //Console.WriteLine("[PL] Last time updated for {0}", addr.device); } } // Add the address if it's not found if (addressfound == false && entryUpdated == false) { //Logging.info("[PL] Adding new address for {0}", presence.wallet); pr.addresses.Add(local_addr); entryUpdated = true; lock (presenceCount) { if (!presenceCount.ContainsKey(local_addr.type)) { presenceCount.Add(local_addr.type, 0); } presenceCount[local_addr.type]++; } } } // Check if the entry was updated if (entryUpdated == false) { return(pr); } // Return the stored presence list entity return_presence = pr; } else { // Insert a new entry //Console.WriteLine("[PL] Adding new entry for {0}", presence.wallet); presences.Add(presence); lock (presenceCount) { foreach (PresenceAddress pa in presence.addresses) { if (!presenceCount.ContainsKey(pa.type)) { presenceCount.Add(pa.type, 0); } presenceCount[pa.type]++; } } return_presence = presence; } } return(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) { address = null; // Get the current timestamp long currentTime = Core.getCurrentTimestamp(); 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'; if (keepAliveVersion > 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') { // check balance if (Node.walletState.getWalletBalance(wallet) < CoreConfig.minimumMasterNodeFunds) { return(false); } } else if (node_type != '0') { // reject everything else return(false); } lock (presences) { Presence listEntry = presences.Find(x => x.wallet.SequenceEqual(wallet)); if (listEntry == null && wallet.SequenceEqual(Node.walletStorage.getPrimaryAddress())) { Logging.error(string.Format("My entry was removed from local PL, readding.")); 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); CoreProtocolMessage.broadcastProtocolMessage(new char[] { 'M', 'R' }, ProtocolMessageCode.getPresence, mw.ToArray(), null); } } return(false); } if (keepAliveVersion == 0) { // Verify the signature if (CryptoManager.lib.verifySignature(Encoding.UTF8.GetBytes(CoreConfig.ixianChecksumLockString + "-" + deviceid + "-" + timestamp + "-" + hostname), listEntry.pubkey, signature) == false) { Logging.warn(string.Format("[PL] KEEPALIVE tampering for {0} {1}, incorrect Sig.", Base58Check.Base58CheckEncoding.EncodePlain(listEntry.wallet), hostname)); return(false); } } else { // 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); } // Check for tampering. Includes a +300, -30 second synchronization zone if ((currentTime - timestamp) > 300 || (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; if (node_type != '0') { 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; } if (pa.type == 'M' || pa.type == 'H') { PeerStorage.addPeerToPeerList(hostname, wallet); } //Console.WriteLine("[PL] LASTSEEN for {0} - {1} set to {2}", hostname, deviceid, pa.lastSeenTime); return(true); } } else { if (wallet.SequenceEqual(Node.walletStorage.getPrimaryAddress())) { updateEntry(curNodePresence); return(true); } else { using (MemoryStream mw = new MemoryStream()) { using (BinaryWriter writer = new BinaryWriter(mw)) { writer.Write(wallet.Length); writer.Write(wallet); CoreProtocolMessage.broadcastProtocolMessage(new char[] { 'M', 'R' }, ProtocolMessageCode.getPresence, mw.ToArray(), 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) { 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) { var addresses_to_remove = listEntry.addresses.FindAll(x => x == address); 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.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); }