Complete sources for a monero webminer.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

405 lines
12 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. // The MIT License (MIT)
  2. // Copyright (c) 2018 - the webminerpool developer
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of
  4. // this software and associated documentation files (the "Software"), to deal in
  5. // the Software without restriction, including without limitation the rights to
  6. // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  7. // the Software, and to permit persons to whom the Software is furnished to do so,
  8. // subject to the following conditions:
  9. // The above copyright notice and this permission notice shall be included in all
  10. // copies or substantial portions of the Software.
  11. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  12. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  13. // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  14. // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  15. // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  16. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  17. using System;
  18. using System.Collections.Concurrent;
  19. using System.Collections.Generic;
  20. using System.Net.Sockets;
  21. using System.Text;
  22. using System.Text.RegularExpressions;
  23. using System.Threading.Tasks;
  24. using TinyJson;
  25. using JsonData = System.Collections.Generic.Dictionary<string, object>;
  26. using Fleck;
  27. namespace Server {
  28. public class PoolConnection {
  29. public TcpClient TcpClient;
  30. public byte[] ReceiveBuffer;
  31. public string Login;
  32. public string Password;
  33. public int Port;
  34. public string Url;
  35. public bool Closed;
  36. public string PoolId;
  37. public string Credentials;
  38. public long Hashes = 0;
  39. public Client LastSender;
  40. public JsonData LastJob;
  41. public DateTime LastInteraction = DateTime.Now;
  42. public CcHashset<string> LastSolved;
  43. public CcHashset<Client> WebClients = new CcHashset<Client> ();
  44. public void Send (Client client, string msg) {
  45. try {
  46. Byte[] bytesSent = Encoding.ASCII.GetBytes (msg);
  47. TcpClient.GetStream ().BeginWrite (bytesSent, 0, bytesSent.Length, SendCallback, null);
  48. this.LastSender = client;
  49. } catch { }
  50. }
  51. private void SendCallback (IAsyncResult result) {
  52. if (!TcpClient.Connected) return;
  53. try {
  54. NetworkStream networkStream = TcpClient.GetStream ();
  55. networkStream.EndWrite (result);
  56. } catch { }
  57. }
  58. }
  59. public class PoolConnectionFactory {
  60. public delegate void ReceiveJobDelegate (Client client, JsonData json, CcHashset<string> hashset);
  61. public delegate void ReceiveErrorDelegate (Client client, JsonData json);
  62. public delegate void DisconnectedDelegate (Client client, string reason);
  63. private static ReceiveErrorDelegate ReceiveError;
  64. private static ReceiveJobDelegate ReceiveJob;
  65. private static DisconnectedDelegate Disconnect;
  66. public static CcDictionary<string, PoolConnection> Connections = new CcDictionary<string, PoolConnection> ();
  67. private static bool VerifyJob (JsonData data) {
  68. if (data == null) return false;
  69. if (!data.ContainsKey ("job_id")) return false;
  70. if (!data.ContainsKey ("blob")) return false;
  71. if (!data.ContainsKey ("target")) return false;
  72. string blob = data["blob"].GetString ();
  73. string target = data["target"].GetString ();
  74. if (blob.Length != 152) return false;
  75. if (target.Length != 8) return false;
  76. if (!Regex.IsMatch (blob, MainClass.RegexIsHex)) return false;
  77. if (!Regex.IsMatch (target, MainClass.RegexIsHex)) return false;
  78. return true;
  79. }
  80. private static void ReceiveCallback (IAsyncResult result) {
  81. PoolConnection mypc = result.AsyncState as PoolConnection;
  82. TcpClient client = mypc.TcpClient;
  83. if (!client.Connected) return;
  84. NetworkStream networkStream;
  85. try { networkStream = client.GetStream (); } catch { return; }
  86. int bytesread = 0;
  87. try { bytesread = networkStream.EndRead (result); } catch { return; }
  88. string json = string.Empty;
  89. try {
  90. if (bytesread == 0) // disconnected
  91. {
  92. // slow that down a bit to avoid negative feedback loop
  93. Task.Run (async delegate {
  94. await Task.Delay (TimeSpan.FromSeconds (4));
  95. List<Client> cllist = new List<Client> (mypc.WebClients.Values);
  96. foreach (Client ev in cllist) Disconnect (ev, "lost pool connection.");
  97. });
  98. return;
  99. }
  100. json = ASCIIEncoding.ASCII.GetString (mypc.ReceiveBuffer, 0, bytesread);
  101. networkStream.BeginRead (mypc.ReceiveBuffer, 0, mypc.ReceiveBuffer.Length, new AsyncCallback (ReceiveCallback), mypc);
  102. } catch { return; }
  103. if (bytesread == 0 || string.IsNullOrEmpty (json)) return; //?!
  104. var msg = json.FromJson<JsonData> ();
  105. if (msg == null) return;
  106. if (string.IsNullOrEmpty (mypc.PoolId)) {
  107. // this "protocol" is strange
  108. if (!msg.ContainsKey ("result")) {
  109. string additionalInfo = "none";
  110. // try to get the error
  111. if (msg.ContainsKey ("error")) {
  112. msg = msg["error"] as JsonData;
  113. if (msg != null && msg.ContainsKey ("message"))
  114. additionalInfo = msg["message"].GetString ();
  115. }
  116. List<Client> cllist = new List<Client> (mypc.WebClients.Values);
  117. foreach (Client ev in cllist)
  118. Disconnect (ev, "can not connect. additional information: " + additionalInfo);
  119. return;
  120. }
  121. msg = msg["result"] as JsonData;
  122. if (msg == null)
  123. return;
  124. if (!msg.ContainsKey ("id"))
  125. return;
  126. if (!msg.ContainsKey ("job"))
  127. return;
  128. mypc.PoolId = msg["id"].GetString ();
  129. var lastjob = msg["job"] as JsonData;
  130. if (!VerifyJob (lastjob)) {
  131. CConsole.ColorWarning(() =>
  132. Console.WriteLine ("Failed to verify job: {0}", json));
  133. return;
  134. }
  135. // extended stratum
  136. // extended stratum - we do not support "algo" yet
  137. if(!lastjob.ContainsKey("variant")) lastjob.Add("variant","-1");
  138. mypc.LastJob = lastjob;
  139. mypc.LastInteraction = DateTime.Now;
  140. mypc.LastSolved = new CcHashset<string> ();
  141. List<Client> cllist2 = new List<Client> (mypc.WebClients.Values);
  142. foreach (Client ev in cllist2) {
  143. ReceiveJob (ev, mypc.LastJob, mypc.LastSolved);
  144. }
  145. } else if (msg.ContainsKey ("method") && msg["method"].GetString () == "job") {
  146. if (!msg.ContainsKey ("params"))
  147. return;
  148. var lastjob = msg["params"] as JsonData;
  149. if (!VerifyJob (lastjob)) {
  150. CConsole.ColorWarning(() =>
  151. Console.WriteLine ("Failed to verify job: {0}", json));
  152. return;
  153. }
  154. // extended stratum - we do not support "algo" yet
  155. // if(!lastjob.ContainsKey("algo")) lastjob.Add("algo","");
  156. if(!lastjob.ContainsKey("variant")) lastjob.Add("variant","-1");
  157. mypc.LastJob = lastjob;
  158. mypc.LastInteraction = DateTime.Now;
  159. mypc.LastSolved = new CcHashset<string> ();
  160. List<Client> cllist2 = new List<Client> (mypc.WebClients.Values);
  161. Console.WriteLine ("Sending job to {0} client(s)!", cllist2.Count);
  162. foreach (Client ev in cllist2) {
  163. ReceiveJob (ev, mypc.LastJob, mypc.LastSolved);
  164. }
  165. } else {
  166. if (msg.ContainsKey ("error")) {
  167. // who knows?
  168. ReceiveError (mypc.LastSender, msg);
  169. } else {
  170. CConsole.ColorWarning(() =>
  171. Console.WriteLine ("Pool is sending nonsense."));
  172. }
  173. }
  174. }
  175. private static void ConnectCallback (IAsyncResult result) {
  176. PoolConnection mypc = result.AsyncState as PoolConnection;
  177. TcpClient client = mypc.TcpClient;
  178. if (!mypc.Closed && client.Connected) {
  179. try {
  180. NetworkStream networkStream = client.GetStream ();
  181. mypc.ReceiveBuffer = new byte[client.ReceiveBufferSize];
  182. networkStream.BeginRead (mypc.ReceiveBuffer, 0, mypc.ReceiveBuffer.Length, new AsyncCallback (ReceiveCallback), mypc);
  183. // keep things stupid and simple
  184. string msg0 = "{\"method\":\"login\",\"params\":{\"login\":\"";
  185. string msg1 = "\",\"pass\":\"";
  186. string msg2 = "\",\"agent\":\"webminerpool.com\"},\"id\":1}";
  187. string msg = msg0 + mypc.Login + msg1 + mypc.Password + msg2 + "\n";
  188. mypc.Send (mypc.LastSender, msg);
  189. } catch { return; }
  190. } else {
  191. // slow that down a bit
  192. Task.Run (async delegate {
  193. await Task.Delay (TimeSpan.FromSeconds (4));
  194. List<Client> cllist = new List<Client> (mypc.WebClients.Values);
  195. foreach (Client ev in cllist)
  196. Disconnect (ev, "can not connect to pool.");
  197. });
  198. }
  199. }
  200. public static void Close (PoolConnection connection, Client client) {
  201. connection.WebClients.TryRemove (client);
  202. if (connection.WebClients.Count == 0) {
  203. connection.Closed = true;
  204. try {
  205. var networkStream = connection.TcpClient.GetStream ();
  206. networkStream.EndRead (null);
  207. } catch { }
  208. try { connection.TcpClient.Close ();} catch { }
  209. try { connection.TcpClient.Client.Close ();} catch { }
  210. try { connection.ReceiveBuffer = null;} catch { }
  211. Connections.TryRemove(connection.Credentials);
  212. Console.WriteLine ("{0}: closed a pool connection.", client.WebSocket.ConnectionInfo.Id);
  213. }
  214. }
  215. public static void RegisterCallbacks (ReceiveJobDelegate receiveJob, ReceiveErrorDelegate receiveError, DisconnectedDelegate disconnect) {
  216. PoolConnectionFactory.ReceiveJob = receiveJob;
  217. PoolConnectionFactory.ReceiveError = receiveError;
  218. PoolConnectionFactory.Disconnect = disconnect;
  219. }
  220. public static void CheckPoolConnection (PoolConnection connection) {
  221. if ((DateTime.Now - connection.LastInteraction).TotalMinutes < 10)
  222. return;
  223. CConsole.ColorWarning(() => Console.WriteLine ("Initiating reconnect! {0}:{1}", connection.Url, connection.Login));
  224. try {
  225. var networkStream = connection.TcpClient.GetStream ();
  226. networkStream.EndRead (null);
  227. } catch { }
  228. try { connection.TcpClient.Close (); } catch { }
  229. try { connection.TcpClient.Client.Close (); } catch { }
  230. connection.ReceiveBuffer = null;
  231. connection.LastInteraction = DateTime.Now;
  232. connection.PoolId = "";
  233. connection.LastJob = null;
  234. connection.TcpClient = new TcpClient ();
  235. Fleck.SocketExtensions.SetKeepAlive (connection.TcpClient.Client, 60000, 1000);
  236. connection.TcpClient.Client.ReceiveBufferSize = 4096 * 2;
  237. try { connection.TcpClient.BeginConnect (connection.Url, connection.Port, new AsyncCallback (ConnectCallback), connection); } catch { }
  238. }
  239. public static PoolConnection CreatePoolConnection (Client client, string url, int port, string login, string password) {
  240. string credential = url + port.ToString () + login + password;
  241. PoolConnection lpc, mypc = null;
  242. int batchCounter = 0;
  243. while (Connections.TryGetValue (credential+batchCounter.ToString(), out lpc)) {
  244. if (lpc.WebClients.Count > MainClass.BatchSize) batchCounter++;
  245. else { mypc = lpc; break; }
  246. }
  247. credential += batchCounter.ToString ();
  248. if (mypc == null) {
  249. CConsole.ColorInfo( () => {
  250. Console.WriteLine ("{0}: initiated new pool connection",client.WebSocket.ConnectionInfo.Id);
  251. Console.WriteLine ("{0} {1} {2}", login, password, url);
  252. });
  253. mypc = new PoolConnection ();
  254. mypc.Credentials = credential;
  255. mypc.LastSender = client;
  256. mypc.TcpClient = new TcpClient ();
  257. Fleck.SocketExtensions.SetKeepAlive (mypc.TcpClient.Client, 60000, 1000);
  258. mypc.TcpClient.Client.ReceiveBufferSize = 4096 * 2;
  259. mypc.Login = login;
  260. mypc.Password = password;
  261. mypc.Port = port;
  262. mypc.Url = url;
  263. mypc.WebClients.TryAdd (client);
  264. Connections.TryAdd (credential, mypc);
  265. try { mypc.TcpClient.Client.BeginConnect (url, port, new AsyncCallback (ConnectCallback), mypc); } catch { }
  266. } else {
  267. Console.WriteLine ("{0}: reusing pool connection", client.WebSocket.ConnectionInfo.Id);
  268. mypc.WebClients.TryAdd (client);
  269. if (mypc.LastJob != null) ReceiveJob (client, mypc.LastJob, mypc.LastSolved);
  270. else Console.WriteLine ("{0} no job yet.", client.WebSocket.ConnectionInfo.Id);
  271. }
  272. client.PoolConnection = mypc;
  273. return mypc;
  274. }
  275. }
  276. }