Beispiel #1
0
 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);
 }
Beispiel #2
0
 /// <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);
         }
     }
 }
Beispiel #3
0
        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();
            }
        }
Beispiel #4
0
        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
             });
         }
     }
 }
Beispiel #7
0
 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();
     }
 }
Beispiel #8
0
 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;
         }
     }
 }
Beispiel #9
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();
     }
 }
Beispiel #10
0
 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++;
     }
 }
Beispiel #11
0
 public void onUpdate()
 {
     while (running)
     {
         if (updateBlockHeaders())
         {
             verifyUnprocessedTransactions();
             long currentTime = Clock.getTimestamp();
             if (currentTime - lastPITPruneTime > pitCachePruneInterval)
             {
                 prunePITCache();
             }
         }
         Thread.Sleep(ConsensusConfig.blockGenerationInterval);
     }
 }
Beispiel #12
0
        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);
        }
Beispiel #13
0
        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);
        }
Beispiel #14
0
        /// <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());
                    }
                }
            }
        }
Beispiel #15
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.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);
                    }
                }
            }
        }
Beispiel #16
0
        /// <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);
            }
        }
Beispiel #18
0
 public static long getCurrentTimestamp()
 {
     return((long)(Clock.getTimestamp() - networkTimeDifference));
 }