public static Peer getRandomMasterNodeAddress() { lock (peerList) { long curTime = Clock.getTimestamp(); Peer p = null; if (initialConnectionCount < 1) { var connectableList = peerList.FindAll(x => x.blacklisted == 0 && curTime - x.lastConnectAttempt > 30).OrderByDescending(x => x.lastConnected); if (connectableList.Count() > 0) { p = connectableList.First(); } } else { List <Peer> connectableList = peerList.FindAll(x => x.blacklisted == 0 && curTime - x.lastConnectAttempt > 30); if (connectableList.Count > 0) { Random rnd = new Random(); p = connectableList[rnd.Next(connectableList.Count)]; } } if (p != null) { p.lastConnectAttempt = curTime; return(p); } } return(null); }
/// <summary> /// Requests PIT for the specified block from a random connected neighbor node. /// Nominally, only the transactions included in `txids` need to be verifiable with the PIT, but /// due to how Cuckoo filtering works, some false positives will also be included. This helps with anonymization, if the false positive rate is high enough. /// </summary> /// <param name="block_num">Block number for which the PIT should be included.</param> /// <param name="txids">List of interesting transactions, which we wish to verify.</param> private void requestPITForBlock(ulong block_num, List <string> txids) { lock (pitCache) { long currentTime = Clock.getTimestamp(); // Request might already have been sent. In that case, we re-send it we have been waiting for too long. if (!pitCache.ContainsKey(block_num) || currentTime - pitCache[block_num].requestSent > pitRequestTimeout) { Cuckoo filter = new Cuckoo(txids.Count); foreach (var tx in txids) { filter.Add(Encoding.UTF8.GetBytes(tx)); } byte[] filter_bytes = filter.getFilterBytes(); MemoryStream m = new MemoryStream(filter_bytes.Length + 12); using (BinaryWriter w = new BinaryWriter(m, Encoding.UTF8, true)) { w.Write(block_num); w.Write(filter_bytes.Length); w.Write(filter_bytes); } CoreProtocolMessage.broadcastProtocolMessageToSingleRandomNode(new char[] { 'M' }, ProtocolMessageCode.getPIT, m.ToArray(), 0); PITCacheItem ci = new PITCacheItem() { pit = null, requestedForTXIDs = txids, requestSent = Clock.getTimestamp() }; pitCache.AddOrReplace(block_num, ci); } } }
private static void cleanupFileCache(bool force = false) { lock (fileCache) { long curTime = Clock.getTimestamp(); Dictionary <string, object[]> tmp_cache = new Dictionary <string, object[]>(fileCache); foreach (var entry in tmp_cache) { if (force == true || curTime - (long)entry.Value[1] > 60) { ((FileStream)entry.Value[0]).Close(); fileCache.Remove(entry.Key); // Fix for occasional locked database error GC.Collect(); GC.WaitForPendingFinalizers(); // End of fix } } } lock (blockHeaderCache) { blockHeaderCache.Clear(); } }
private static FileStream getStorageFile(string path, bool cache) { if (stopped) { return(null); } lock (fileCache) { if (fileCache.ContainsKey(path)) { if (cache) { fileCache[path][1] = Clock.getTimestamp(); cleanupFileCache(); } return((FileStream)fileCache[path][0]); } FileStream fs = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); if (cache) { fileCache.Add(path, new object[2] { fs, Clock.getTimestamp() }); } return(fs); } }
public static void addPendingLocalTransaction(Transaction t, byte[] message_id = null) { lock (pendingTransactions) { if (pendingTransactions.Find(x => x.transaction.id.SequenceEqual(t.id)) == null) { pendingTransactions.Add(new PendingTransaction(t, Clock.getTimestamp(), message_id)); } } }
public static void addPendingLocalTransaction(Transaction t) { lock (pendingTransactions) { if (!pendingTransactions.Exists(x => ((Transaction)x[0]).id.SequenceEqual(t.id))) { pendingTransactions.Add(new object[4] { t, Clock.getTimestamp(), 0, false }); } } }
public static void blacklist(byte[] wallet) { lock (peerList) { Peer p = peerList.Find(x => x.walletAddress != null && x.walletAddress.SequenceEqual(wallet)); if (p == null) { return; } p.blacklisted = Clock.getTimestamp(); } }
public static void updateBlacklist() { lock (peerList) { long bl_window = Clock.getTimestamp() - CoreConfig.NodeBlacklistExpiration; var peers = peerList.FindAll(x => x.blacklisted != 0 && x.blacklisted < bl_window); foreach (var peer in peers) { peer.blacklisted = 0; } } }
public static void blacklist(string host_name) { lock (peerList) { Peer p = peerList.Find(x => x.hostname == host_name); if (p == null) { return; } p.blacklisted = Clock.getTimestamp(); } }
public static void updateLastConnected(string hostname) { lock (peerList) { var tmp_peer = peerList.Find(x => x.hostname == hostname); if (tmp_peer == null) { return; } tmp_peer.lastConnected = Clock.getTimestamp(); tmp_peer.rating++; initialConnectionCount++; } }
public void onUpdate() { while (running) { if (updateBlockHeaders()) { verifyUnprocessedTransactions(); long currentTime = Clock.getTimestamp(); if (currentTime - lastPITPruneTime > pitCachePruneInterval) { prunePITCache(); } } Thread.Sleep(ConsensusConfig.blockGenerationInterval); } }
private bool updateBlockHeaders(bool force_update = false) { long currentTime = Clock.getTimestamp(); // Check if the request expired if (force_update || currentTime - lastRequestedBlockTime > ConsensusConfig.blockGenerationInterval) { lastRequestedBlockTime = currentTime; // request next blocks requestBlockHeaders(lastBlockHeader.blockNum, lastBlockHeader.blockNum + 500); return(true); } return(false); }
public static Peer getRandomMasterNodeAddress() { List <Peer> connectableList = null; lock (peerList) { long curTime = Clock.getTimestamp(); connectableList = peerList.FindAll(x => curTime - x.lastConnectAttempt > 30); if (connectableList != null && connectableList.Count > 0) { Random rnd = new Random(); Peer p = connectableList[rnd.Next(connectableList.Count)]; p.lastConnectAttempt = curTime; return(p); } } return(null); }
/// <summary> /// Prepares and sends an Ixian protocol 'Hello' message to the specified remote endpoint. /// </summary> /// <remarks> /// A valid Ixian 'Hello' message includes certain Node data, verified by a public-key signature, which this function prepares using /// the primary wallet's keypair. If this message is a reply to the other endpoint's hello message, then /// </remarks> /// <param name="endpoint">Remote endpoint to send the message to.</param> /// <param name="sendHelloData">True if the message is the first hello sent to the remote node, false if it is a reply to the challenge.</param> /// <param name="challenge_response">Response byte-field to the other node's hello challenge</param> public static void sendHelloMessageV6(RemoteEndpoint endpoint, bool sendHelloData, int challenge) { using (MemoryStream m = new MemoryStream(1856)) { using (BinaryWriter writer = new BinaryWriter(m)) { string publicHostname = IxianHandler.getFullPublicAddress(); // Send the node version writer.WriteIxiVarInt(6); // Send the public node address byte[] address = IxianHandler.getWalletStorage().getPrimaryAddress(); writer.WriteIxiVarInt(address.Length); writer.Write(address); // Send the testnet designator writer.Write(IxianHandler.isTestNet); char node_type = PresenceList.myPresenceType; writer.Write(node_type); // Send the version writer.Write(CoreConfig.productVersion); // Send the node device id writer.WriteIxiVarInt(CoreConfig.device_id.Length); writer.Write(CoreConfig.device_id); // Send the wallet public key writer.WriteIxiVarInt(IxianHandler.getWalletStorage().getPrimaryPublicKey().Length); writer.Write(IxianHandler.getWalletStorage().getPrimaryPublicKey()); // Send listening port writer.WriteIxiVarInt(IxianHandler.publicPort); // Send timestamp long timestamp = Clock.getTimestamp() + endpoint.calculateTimeDifference(); writer.WriteIxiVarInt(timestamp); // generate signature using (MemoryStream mSig = new MemoryStream(1024)) { using (BinaryWriter sigWriter = new BinaryWriter(mSig)) { sigWriter.Write(ConsensusConfig.ixianChecksumLock); sigWriter.Write(CoreConfig.device_id); sigWriter.Write(timestamp); sigWriter.Write(publicHostname); sigWriter.Write(challenge); } byte[] signature = CryptoManager.lib.getSignature(mSig.ToArray(), IxianHandler.getWalletStorage().getPrimaryPrivateKey()); writer.WriteIxiVarInt(signature.Length); writer.Write(signature); } if (sendHelloData) { Block block = IxianHandler.getLastBlock(); if (block == null) { Logging.warn("Clients are connecting, but we have no blocks yet to send them!"); sendBye(endpoint, ProtocolByeCode.notReady, string.Format("The node isn't ready yet, please try again later."), "", true); return; } writer.WriteIxiVarInt(block.blockNum); writer.WriteIxiVarInt(block.blockChecksum.Length); writer.Write(block.blockChecksum); writer.WriteIxiVarInt(block.version); writer.Write(endpoint.getFullAddress(true)); #if TRACE_MEMSTREAM_SIZES Logging.info(String.Format("CoreProtocolMessage::sendHelloMessage: {0}", m.Length)); #endif endpoint.sendData(ProtocolMessageCode.helloData, m.ToArray()); } else { byte[] challenge_bytes = IxiVarInt.GetIxiVarIntBytes(challenge); endpoint.challenge = BitConverter.GetBytes(challenge); writer.Write(challenge_bytes); #if TRACE_MEMSTREAM_SIZES Logging.info(String.Format("CoreProtocolMessage::sendHelloMessage: {0}", m.Length)); #endif endpoint.sendData(ProtocolMessageCode.hello, m.ToArray()); } } } }
// 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.SequenceEqual(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, Clock.getTimestamp(), 0, 0, 0); } //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, Clock.getTimestamp(), 0, 0, 0); } 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, Clock.getTimestamp(), 0, 0, 0); } 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); } } } }
/// <summary> /// Prepares and sends an Ixian protocol 'Hello' message to the specified remote endpoint. /// </summary> /// <remarks> /// A valid Ixian 'Hello' message includes certain Node data, verified by a public-key signature, which this function prepares using /// the primary wallet's keypair. If this message is a reply to the other endpoint's hello message, then /// </remarks> /// <param name="endpoint">Remote endpoint to send the message to.</param> /// <param name="sendHelloData">True if the message is the first hello sent to the remote node, false if it is a reply to the challenge.</param> /// <param name="challenge_response">Response byte-field to the other node's hello challenge</param> public static void sendHelloMessage(RemoteEndpoint endpoint, bool sendHelloData, byte[] challenge_response) { using (MemoryStream m = new MemoryStream(1856)) { using (BinaryWriter writer = new BinaryWriter(m)) { string publicHostname = IxianHandler.getFullPublicAddress(); // Send the node version writer.Write(CoreConfig.protocolVersion); // Send the public node address byte[] address = IxianHandler.getWalletStorage().getPrimaryAddress(); writer.Write(address.Length); writer.Write(address); // Send the testnet designator writer.Write(CoreConfig.isTestNet); char node_type = PresenceList.myPresenceType; writer.Write(node_type); // Send the version writer.Write(CoreConfig.productVersion); // Send the node device id writer.Write(CoreConfig.device_id); // Send the wallet public key writer.Write(IxianHandler.getWalletStorage().getPrimaryPublicKey().Length); writer.Write(IxianHandler.getWalletStorage().getPrimaryPublicKey()); // Send listening port writer.Write(IxianHandler.publicPort); // Send timestamp long timestamp = Clock.getTimestamp() + endpoint.calculateTimeDifference(); writer.Write(timestamp); // send signature byte[] signature = CryptoManager.lib.getSignature(Encoding.UTF8.GetBytes(ConsensusConfig.ixianChecksumLockString + "-" + CoreConfig.device_id + "-" + timestamp + "-" + publicHostname), IxianHandler.getWalletStorage().getPrimaryPrivateKey()); writer.Write(signature.Length); writer.Write(signature); if (sendHelloData) { Block block = IxianHandler.getLastBlock(); if (block == null) { Logging.warn("Clients are connecting, but we have no blocks yet to send them!"); CoreProtocolMessage.sendBye(endpoint, ProtocolByeCode.notReady, string.Format("The node isn't ready yet, please try again later."), "", true); return; } ulong lastBlock = block.blockNum; writer.Write(lastBlock); writer.Write(block.blockChecksum.Length); writer.Write(block.blockChecksum); writer.Write(block.walletStateChecksum.Length); writer.Write(block.walletStateChecksum); writer.Write((int)0); // deprecated, can be replaced with something else of type int32 writer.Write(block.version); // Write the legacy level writer.Write((ulong)0); // deprecated, can be replaced with something else of type UInt64 writer.Write(challenge_response.Length); writer.Write(challenge_response); #if TRACE_MEMSTREAM_SIZES Logging.info(String.Format("CoreProtocolMessage::sendHelloMessage: {0}", m.Length)); #endif endpoint.sendData(ProtocolMessageCode.helloData, m.ToArray()); } else { List <byte> challenge = new List <byte>(); challenge.AddRange(IxianHandler.getWalletStorage().getPrimaryAddress()); Random rnd = new Random(); challenge.AddRange(BitConverter.GetBytes(rnd.Next(20000))); byte[] challenge_bytes = challenge.ToArray(); endpoint.challenge = challenge_bytes; writer.Write(challenge_bytes.Length); writer.Write(challenge_bytes); #if TRACE_MEMSTREAM_SIZES Logging.info(String.Format("CoreProtocolMessage::sendHelloMessage: {0}", m.Length)); #endif endpoint.sendData(ProtocolMessageCode.hello, m.ToArray()); } } } }
/// <summary> /// Saves block header to local storage /// </summary> /// <param name="block_header">Block header to save</param> /// <exception cref="Exception">Exception occured while saving block header.</exception> public static bool saveBlockHeader(BlockHeader block_header) { if (stopped) { return(false); } lock (lockObject) { ulong block_num = block_header.blockNum; ulong file_block_num = ((ulong)(block_num / CoreConfig.maxBlockHeadersPerDatabase)) * CoreConfig.maxBlockHeadersPerDatabase; string db_path = path + Path.DirectorySeparatorChar + "0000" + Path.DirectorySeparatorChar + file_block_num + ".dat"; FileStream fs = getStorageFile(db_path, true); // File.Open(db_path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); if (fs.Length > 0) { fs.Seek(-8, SeekOrigin.End); byte[] file_block_header_bytes = new byte[8]; fs.Read(file_block_header_bytes, 0, 8); if (BitConverter.ToUInt64(file_block_header_bytes, 0) + 1 != block_num) { // TODO should probably delete the file and reset lastBlockHeader return(false); } } try { // insert dummy block for first block if (block_num == 1) { BlockHeader dummy_block_header = new BlockHeader(); dummy_block_header.blockNum = 0; dummy_block_header.blockChecksum = new byte[1]; byte[] dummy_block_header_bytes = dummy_block_header.getBytes(); byte[] dummy_block_header_len_bytes = BitConverter.GetBytes(dummy_block_header_bytes.Length); fs.Write(dummy_block_header_len_bytes, 0, dummy_block_header_len_bytes.Length); fs.Write(dummy_block_header_bytes, 0, dummy_block_header_bytes.Length); byte[] dummy_block_num_bytes = BitConverter.GetBytes(dummy_block_header.blockNum); fs.Write(dummy_block_num_bytes, 0, dummy_block_num_bytes.Length); } byte[] block_header_bytes = block_header.getBytes(); byte[] block_header_len_bytes = BitConverter.GetBytes(block_header_bytes.Length); fs.Write(block_header_len_bytes, 0, block_header_len_bytes.Length); fs.Write(block_header_bytes, 0, block_header_bytes.Length); byte[] block_num_bytes = BitConverter.GetBytes(block_header.blockNum); fs.Write(block_num_bytes, 0, block_num_bytes.Length); lastBlockHeaderTime = Clock.getTimestamp(); } catch (Exception e) { Logging.error("Exception occured while saving block header: {0}", e); return(false); } return(true); } }
public static long getCurrentTimestamp() { return((long)(Clock.getTimestamp() - networkTimeDifference)); }