Advertisement
BurningBunny

C# Bitcoin Minimal Miner

Feb 26th, 2014
1,310
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 10.58 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Net;
  6. using System.IO;
  7. using System.Text.RegularExpressions;
  8. using System.Security.Cryptography;
  9.  
  10. namespace MiniMiner
  11. {
  12.     class Utils
  13.     {
  14.         public static byte[] ToBytes(string input)
  15.         {
  16.             byte[] bytes = new byte[input.Length / 2];
  17.             for (int i = 0, j = 0; i < input.Length; j++, i += 2)
  18.                 bytes[j] = byte.Parse(input.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
  19.  
  20.             return bytes;
  21.         }
  22.  
  23.         public static string ToString(byte[] input)
  24.         {
  25.             string result = "";
  26.             foreach (byte b in input)
  27.                 result += b.ToString("x2");
  28.  
  29.             return result;
  30.         }
  31.  
  32.         public static string ToString(uint value)
  33.         {
  34.             string result = "";
  35.             foreach (byte b in BitConverter.GetBytes(value))
  36.                 result += b.ToString("x2");
  37.  
  38.             return result;
  39.         }
  40.  
  41.         public static string EndianFlip32BitChunks(string input)
  42.         {
  43.             //32 bits = 4*4 bytes = 4*4*2 chars
  44.             string result = "";
  45.             for (int i = 0; i < input.Length; i += 8)
  46.                 for (int j = 0; j < 8; j += 2)
  47.                 {
  48.                     //append byte (2 chars)
  49.                     result += input[i - j + 6];
  50.                     result += input[i - j + 7];
  51.                 }
  52.             return result;        
  53.         }
  54.  
  55.         public static string RemovePadding(string input)
  56.         {
  57.             //payload length: final 64 bits in big-endian - 0x0000000000000280 = 640 bits = 80 bytes = 160 chars
  58.             return input.Substring(0, 160);
  59.         }
  60.  
  61.         public static string AddPadding(string input)
  62.         {
  63.             //add the padding to the payload. It never changes.
  64.             return input + "000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000";
  65.         }
  66.     }
  67.  
  68.     class Work
  69.     {
  70.         public Work(byte[] data)
  71.         {
  72.             Data = data;
  73.             Current = (byte[])data.Clone();
  74.             _nonceOffset = Data.Length - 4;
  75.             _ticks = DateTime.Now.Ticks;
  76.             _hasher = new SHA256Managed();
  77.  
  78.         }
  79.         private SHA256Managed _hasher;
  80.         private long _ticks;
  81.         private long _nonceOffset;
  82.         public byte[] Data;
  83.         public byte[] Current;
  84.  
  85.         internal bool FindShare(ref uint nonce, uint batchSize)
  86.         {
  87.             for(;batchSize > 0; batchSize--)
  88.             {
  89.                 BitConverter.GetBytes(nonce).CopyTo(Current, _nonceOffset);
  90.                 byte[] doubleHash = Sha256(Sha256(Current));
  91.  
  92.                 //count trailing bytes that are zero
  93.                 int zeroBytes = 0;
  94.                 for (int i = 31; i >= 28; i--, zeroBytes++)
  95.                     if(doubleHash[i] > 0)
  96.                         break;
  97.  
  98.                 //standard share difficulty matched! (target:ffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000)
  99.                 if(zeroBytes == 4)
  100.                     return true;
  101.  
  102.                 //increase
  103.                 if(++nonce == uint.MaxValue)
  104.                     nonce = 0;
  105.             }
  106.             return false;
  107.         }
  108.  
  109.         private byte[] Sha256(byte[] input)
  110.         {
  111.             byte[] crypto = _hasher.ComputeHash(input, 0, input.Length);
  112.             return crypto;
  113.         }
  114.  
  115.         public byte[] Hash
  116.         {
  117.             get { return Sha256(Sha256(Current)); }
  118.         }
  119.  
  120.         public long Age
  121.         {
  122.             get { return DateTime.Now.Ticks - _ticks; }
  123.         }
  124.     }
  125.  
  126.     class Pool
  127.     {
  128.         public Uri Url;
  129.         public string User;
  130.         public string Password;
  131.  
  132.         public Pool(string login)
  133.         {
  134.             int urlStart = login.IndexOf('@');
  135.             int passwordStart = login.IndexOf(':');
  136.             string user = login.Substring(0, passwordStart);
  137.             string password = login.Substring(passwordStart + 1, urlStart - passwordStart - 1);
  138.             string url = "http://"+login.Substring(urlStart + 1);
  139.             Url = new Uri(url);
  140.             User = user;
  141.             Password = password;
  142.         }
  143.  
  144.         private string InvokeMethod(string method, string paramString = null)
  145.         {
  146.             HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(Url);
  147.             webRequest.Credentials = new NetworkCredential(User, Password);
  148.             webRequest.ContentType = "application/json-rpc";
  149.             webRequest.Method = "POST";
  150.  
  151.             string jsonParam = (paramString != null) ? "\"" + paramString + "\"" : "";
  152.             string request = "{\"id\": 0, \"method\": \"" + method + "\", \"params\": [" + jsonParam + "]}";
  153.  
  154.             // serialize json for the request
  155.             byte[] byteArray = Encoding.UTF8.GetBytes(request);
  156.             webRequest.ContentLength = byteArray.Length;
  157.             using (Stream dataStream = webRequest.GetRequestStream())
  158.                 dataStream.Write(byteArray, 0, byteArray.Length);
  159.  
  160.             string reply = "";
  161.             using (WebResponse webResponse = webRequest.GetResponse())
  162.             using (Stream str = webResponse.GetResponseStream())
  163.             using (StreamReader reader = new StreamReader(str))
  164.                 reply = reader.ReadToEnd();
  165.  
  166.             return reply;
  167.         }
  168.        
  169.         public Work GetWork(bool silent = false)
  170.         {
  171.             return new Work(ParseData(InvokeMethod("getwork")));
  172.         }
  173.  
  174.         private byte[] ParseData(string json)
  175.         {
  176.             Match match = Regex.Match(json, "\"data\": \"([A-Fa-f0-9]+)");
  177.             if (match.Success)
  178.             {
  179.                 string data = Utils.RemovePadding(match.Groups[1].Value);
  180.                 data = Utils.EndianFlip32BitChunks(data);
  181.                 return Utils.ToBytes(data);
  182.             }
  183.             throw new Exception("Didn't find valid 'data' in Server Response");
  184.         }
  185.  
  186.         public bool SendShare(byte[] share)
  187.         {
  188.             string data = Utils.EndianFlip32BitChunks(Utils.ToString(share));
  189.             string paddedData = Utils.AddPadding(data);
  190.             string jsonReply = InvokeMethod("getwork", paddedData);
  191.             Match match = Regex.Match(jsonReply, "\"result\": true");
  192.             return match.Success;
  193.         }
  194.     }
  195.  
  196.     class Program
  197.     {
  198.         static Pool _pool = null;
  199.         static Work _work = null;
  200.         static uint _nonce = 0;
  201.         static long _maxAgeTicks = 20000 * TimeSpan.TicksPerMillisecond;
  202.         static uint _batchSize = 100000;
  203.  
  204.         static void Main(string[] args)
  205.         {
  206.             while (true)
  207.             {
  208.                 try
  209.                 {
  210.                     _pool = SelectPool();
  211.                     _work = GetWork();
  212.                     while (true)
  213.                     {
  214.                         if (_work == null || _work.Age > _maxAgeTicks)
  215.                             _work = GetWork();
  216.  
  217.                         if (_work.FindShare(ref _nonce, _batchSize))
  218.                         {
  219.                             SendShare(_work.Current);
  220.                             _work = null;
  221.                         }
  222.                         else
  223.                             PrintCurrentState();
  224.                     }
  225.                 }
  226.                 catch (Exception e)
  227.                 {
  228.                     Console.WriteLine();
  229.                     Console.Write("ERROR: ");
  230.                     Console.WriteLine(e.Message);
  231.                 }
  232.                 Console.WriteLine();
  233.                 Console.Write("Hit 'Enter' to try again...");
  234.                 Console.ReadLine();
  235.             }
  236.         }
  237.  
  238.  
  239.         private static void ClearConsole()
  240.         {
  241.             Console.Clear();
  242.             Console.WriteLine("*****************************");
  243.             Console.WriteLine("*** Minimal Bitcoin Miner ***");
  244.             Console.WriteLine("*****************************");
  245.             Console.WriteLine();
  246.         }
  247.  
  248.         private static Pool SelectPool()
  249.         {
  250.             ClearConsole();
  251.             Print("Chose a Mining Pool 'user:password@url:port' or leave empty to skip.");
  252.             Console.Write("Select Pool: ");
  253.             string login = ReadLineDefault("lithander_2:[email protected]:8332");
  254.             return new Pool(login);
  255.         }
  256.  
  257.         private static Work GetWork()
  258.         {
  259.             ClearConsole();
  260.             Print("Requesting Work from Pool...");
  261.             Print("Server URL: " + _pool.Url.ToString());
  262.             Print("User: " + _pool.User);
  263.             Print("Password: " + _pool.Password);
  264.             return _pool.GetWork();
  265.         }
  266.  
  267.         private static void SendShare(byte[] share)
  268.         {
  269.             ClearConsole();
  270.             Print("*** Found Valid Share ***");
  271.             Print("Share: " + Utils.ToString(_work.Current));
  272.             Print("Nonce: " + Utils.ToString(_nonce));
  273.             Print("Hash: " + Utils.ToString(_work.Hash));
  274.             Print("Sending Share to Pool...");
  275.             if (_pool.SendShare(share))
  276.                 Print("Server accepted the Share!");
  277.             else
  278.                 Print("Server declined the Share!");
  279.  
  280.             Console.Write("Hit 'Enter' to continue...");
  281.             Console.ReadLine();
  282.         }
  283.  
  284.         private static DateTime _lastPrint = DateTime.Now;
  285.         private static void PrintCurrentState()
  286.         {
  287.             ClearConsole();
  288.             Print("Data: " + Utils.ToString(_work.Data));
  289.             string current = Utils.ToString(_nonce);
  290.             string max = Utils.ToString(uint.MaxValue);
  291.             double progress = ((double)_nonce / uint.MaxValue) * 100;
  292.             Print("Nonce: " + current + "/" + max + " " + progress.ToString("F2") + "%");
  293.             Print("Hash: " + Utils.ToString(_work.Hash));
  294.             TimeSpan span = DateTime.Now - _lastPrint;
  295.             Print("Speed: " + (int)(((_batchSize) / 1000) / span.TotalSeconds) + "Kh/s");
  296.             _lastPrint = DateTime.Now;
  297.         }
  298.  
  299.         private static void Print(string msg)
  300.         {
  301.             Console.WriteLine(msg);
  302.             Console.WriteLine();
  303.         }
  304.  
  305.         private static string ReadLineDefault(string defaultValue)
  306.         {
  307.             //Allow Console.ReadLine with a default value
  308.             string userInput = Console.ReadLine();
  309.             Console.WriteLine();
  310.             if (userInput == "")
  311.                 return defaultValue;
  312.             else
  313.                 return userInput;
  314.         }
  315.     }
  316. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement