private int GetCurrentBlockHeight()
		{
			P2PWebClient client = new P2PWebClient();
			client.RequestTimeout = 10000;
			string data = client.DownloadString("http://blockchain.info/q/getblockcount");
			return int.Parse(data);
		}
		private List<Block> GetBlocksAtHeight(int height)
		{
			List<Block> blocks = new List<Block>();
			P2PWebClient client = new P2PWebClient();
			client.RequestTimeout = 10000;
			var blocksAtHeight = JObject.Parse(client.DownloadString(string.Format("http://blockchain.info/block-height/{0}?format=json", height)));

			foreach (var blockData in (JArray)blocksAtHeight["blocks"])
			{
				Block block = new Block();
				block.Id = (string)blockData["hash"];
				block.BlockHeight = (int)blockData["height"];
				block.Difficulty = (decimal)BitcoinMathHelper.Difficulty((long)blockData["bits"]);
				block.PrevBlock = (string)blockData["prev_block"];
				block.Timestamp = (int)blockData["time"];
				block.IsOrphaned = !(bool)blockData["main_chain"];
				var genTx = blockData["tx"][0];
				block.GenerationTxHash = (string)genTx["hash"];
				var outputs = (JArray)genTx["out"];
				foreach (var output in outputs)
				{
					if ((string)output["addr"] == DonationAddress)
					{
						var lastOutput = outputs[outputs.Count - 1];
						if (outputs.Count > 25 && (decimal)lastOutput["value"] == 0 && (decimal)lastOutput["type"] == -1)
						{
							block.IsP2Pool = true;
						}
						break;
					}
				}
				blocks.Add(block);
			}
			return blocks;
		}
        public void UpdateStats()
        {
            try
            {
                int timestamp = DateTime.UtcNow.ToUnixTime();

                int remainder = timestamp % 300;
                timestamp -= remainder;

                Log(string.Format("Updating stats for timestamp {0}", timestamp));

                IEnumerable<string> servers = null;

                bool updatedData = false;
                P2PWebClient client = new P2PWebClient();
                client.RequestTimeout = 3000;

                Retry.ExecuteAction(() =>
                {
                    using (P2PoolDb db = new P2PoolDb())
                    {
                        var existingUsersCount = (from u in db.Users where u.Timestamp == timestamp select u.Address).Count();

                        if (existingUsersCount == 0)
                        {
                            JObject users = null;

                            if (servers == null)
                            {
                                servers = GetServers();
                            }
                            foreach (var server in servers)
                            {
                                if (string.IsNullOrWhiteSpace(server))
                                {
                                    continue;
                                }
                                try
                                {
                                    var baseUrl = new Uri(server.Trim());
                                    users = JObject.Parse(client.DownloadString(new Uri(baseUrl, "/users")));
                                    Log(string.Format(" Stats: Retrived users from {0}", server));
                                    break;
                                }
                                catch (Exception ex)
                                {
                                    Log(" Stats: " + ex.Message);
                                }
                            }

                            if (users == null)
                            {
                                return;
                            }

                            Dictionary<string, decimal> addresses = new Dictionary<string, decimal>();
                            foreach (var userEntry in users.Properties())
                            {
                                string address = P2PHelper.ExtractAddress(userEntry.Name) ?? "Unknown";
                                if (address != null)
                                {
                                    decimal portion = 0;
                                    if (addresses.ContainsKey(address))
                                    {
                                        portion += addresses[address];
                                    }
                                    portion += (decimal)userEntry.Value;
                                    addresses[address] = portion;
                                }
                            }
                            foreach (var item in addresses)
                            {
                                User user = new User()
                                {
                                    Timestamp = timestamp,
                                    Address = item.Key,
                                    Portion = item.Value
                                };
                                db.Users.Add(user);

                            }
                            db.SaveChanges();
                            updatedData = true;
                            Log(string.Format(" Stats: Added {0} users", addresses.Count));
                        }


                    }
                });

                Retry.ExecuteAction(() =>
                {
                    using (P2PoolDb db = new P2PoolDb())
                    {


                        Stat entry = db.Stats.Find(timestamp);
                        if (entry == null)
                        {
                            decimal rate = -1;
                            if (servers == null)
                            {
                                servers = GetServers();
                            }
                            foreach (var server in servers)
                            {
                                if (string.IsNullOrWhiteSpace(server))
                                {
                                    continue;
                                }
                                try
                                {
                                    var baseUrl = new Uri(server.Trim());
                                    rate = decimal.Parse(client.DownloadString(new Uri(baseUrl, "/rate"))) / 1000000000m;
                                    Log(string.Format(" Stats: Retrived rate from {0}", server));
                                    break;
                                }
                                catch (Exception ex)
                                {
                                    Log(" Stats: " + ex.Message);
                                }
                            }

                            if (rate == -1)
                            {
                                return;
                            }

                            int userCount;

                            userCount = db.Database.SqlQuery<int>("select count (distinct address) from p2pool_Users where timestamp >= @start and timestamp <= @end", new SqlParameter("start", timestamp - 86400), new SqlParameter("end", timestamp)).First();

                            entry = new Stat
                            {
                                Timestamp = timestamp,
                                Rate = rate,
                                Users = userCount
                            };
                            db.Stats.Add(entry);
                            db.SaveChanges();
                            updatedData = true;
                            Log(string.Format(" Stats: Saved new rate: {0}", rate));

                        }
                    }
                });

                if (updatedData)
                {

                    Retry.ExecuteAction(() =>
                    {
                        using (P2PoolDb db = new P2PoolDb())
                        {
                            CurrentPayouts entry = db.CurrentPayouts.Find(1);
                            if (entry != null)
                            {
                                JObject payouts = null;
                                if (servers == null)
                                {
                                    servers = GetServers();
                                }
                                foreach (var server in servers)
                                {
                                    if (string.IsNullOrWhiteSpace(server))
                                    {
                                        continue;
                                    }
                                    try
                                    {
                                        var baseUrl = new Uri(server.Trim());
                                        payouts = JObject.Parse(client.DownloadString(new Uri(baseUrl, "/current_payouts")));
                                        Log(string.Format(" Stats: Retrived payouts from {0}", server));
                                        break;
                                    }
                                    catch (Exception ex)
                                    {
                                        Log(" Stats: " + ex.Message);
                                    }
                                }
                                if (payouts == null)
                                {
                                    return;
                                }
                                entry.Payouts = payouts.ToString();
                                entry.Updated = timestamp;
                                db.SaveChanges();
                                Log(" Stats: Saved updated payouts");
                            }
                        }
                    });

                    //cleanup old user stats
                    Retry.ExecuteAction(() =>
                    {
                        using (P2PoolDb db = new P2PoolDb())
                        {
                            // delete all the specific user stats older than 3 days
                            var result = db.Database.ExecuteSqlCommand("delete from p2pool_Users where [Timestamp] < @cutoff", new SqlParameter("cutoff", timestamp - 259200));
                            Log(string.Format(" Stats: Deleted old user rows", result));
                        }
                    });

                }

            }
            catch (Exception ex)
            {
                Log(" Stats: UpdateStats Exception: " + ex.Message);
            }
        }
        public List<Block> FindOrphans(HashSet<string> knownTxHashes)
        {
            List<Block> newBlocks = new List<Block>();
            P2PWebClient client = new P2PWebClient();
            client.RequestTimeout = 30000;
            var addressData = JObject.Parse(client.DownloadString("http://blockchain.info/address/1Kz5QaUPDtKrj5SqW5tFkn7WZh8LmQaQi4?format=json&filter=0"));

            foreach (var tx in (JArray)addressData["txs"])
            {
                try
                {
                    if (tx["inputs"] != null && tx["inputs"].Type == JTokenType.Array && ((JArray)tx["inputs"]).Count == 1 && ((JArray)tx["inputs"])[0]["prev_out"] == null)
                    {
                        string generationTxHash = (string)tx["hash"];
                        if (!knownTxHashes.Contains(generationTxHash))
                        {
                            int blockHeight;
                            if (tx["block_height"] != null)
                            {
                                blockHeight = (int)tx["block_height"];
                                var blocksAtHeight = JObject.Parse(client.DownloadString(string.Format("http://blockchain.info/block-height/{0}?format=json", blockHeight)));

                                foreach (var blockData in (JArray)blocksAtHeight["blocks"])
                                {
                                    try
                                    {
                                        if (blockData["tx"] != null && blockData["tx"][0] != null && blockData["tx"][0]["hash"] != null && (string)blockData["tx"][0]["hash"] == generationTxHash
                                            && blockData["tx"][0]["out"] is JArray && ((JArray)blockData["tx"][0]["out"]).Count > 25)
                                        {
                                            Block block = new Block();
                                            block.Id = (string)blockData["hash"];
                                            block.BlockHeight = (int)blockData["height"];
                                            block.Difficulty = (decimal)BitcoinMathHelper.Difficulty((long)blockData["bits"]);
                                            block.GenerationTxHash = generationTxHash;
                                            block.IsP2Pool = true;
                                            block.PrevBlock = (string)blockData["prev_block"];
                                            block.Timestamp = (int)blockData["time"];
                                            block.IsOrphaned = !(bool)blockData["main_chain"];
                                            newBlocks.Add(block);
                                            break;
                                        }
                                    }
                                    catch
                                    {
                                        //absorb and check next block
                                    }
                                }
                            }
                            else
                            {
                                string TxDataHtml = client.DownloadString(string.Format("http://blockchain.info/tx/{0}", generationTxHash));
                                bool doneParsing = false;

                                HtmlDocument document = new HtmlDocument();
                                document.LoadHtml(TxDataHtml);
                                var tables = document.DocumentNode.SelectNodes("//table");
                                foreach (var table in tables)
                                {
                                    var header = table.SelectSingleNode("tr/th");
                                    if (header == null || header.InnerText.ToLower() != "summary")
                                    {
                                        continue;
                                    }
                                    var links = table.SelectNodes(".//a");
                                    foreach (var link in links)
                                    {
                                        var blockUrl = link.GetAttributeValue("href", "");
                                        if (blockUrl.StartsWith("/block-index/"))
                                        {
                                            var blockData = JObject.Parse(client.DownloadString(string.Format("http://blockchain.info{0}?format=json", blockUrl)));

                                            try
                                            {
                                                if (((JArray)blockData["tx"][0]["out"]).Count > 10)
                                                {

                                                    Block block = new Block();
                                                    block.Id = (string)blockData["hash"];
                                                    block.BlockHeight = (int)blockData["height"];
                                                    block.Difficulty = (decimal)BitcoinMathHelper.Difficulty((long)blockData["bits"]);
                                                    block.GenerationTxHash = generationTxHash;
                                                    block.IsP2Pool = true;
                                                    block.PrevBlock = (string)blockData["prev_block"];
                                                    block.Timestamp = (int)blockData["time"];
                                                    block.IsOrphaned = !(bool)blockData["main_chain"];
                                                    newBlocks.Add(block);
                                                }

                                            }
                                            catch
                                            {
                                                //absorb
                                            }
                                            doneParsing = true;
                                            break;
                                        }
                                    }
                                    if (doneParsing)
                                    {
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
                catch
                {
                    //absorb and check next tx
                }
            }

            return newBlocks;
        }