Browse Source

Merge commit '8893f7a' into stratum

Luke Dashjr 13 years ago
parent
commit
b9190c1722
5 changed files with 128 additions and 65 deletions
  1. 1 2
      configure.ac
  2. 13 4
      miner.c
  3. 7 0
      miner.h
  4. 105 57
      util.c
  5. 2 2
      util.h

+ 1 - 2
configure.ac

@@ -360,8 +360,7 @@ AM_CONDITIONAL([HAVE_LIBUDEV], [test x$libudev != xno])
 
 PKG_PROG_PKG_CONFIG()
 
-PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.15.6], [AC_DEFINE([CURL_HAS_SOCKOPT], [1], [Defined if version of curl supports sockopts.])],
-[PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.10.1], ,[AC_MSG_ERROR([Missing required libcurl dev >= 7.10.1])])])
+PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.18.2], ,[AC_MSG_ERROR([Missing required libcurl dev >= 7.18.2])])
 AC_SUBST(LIBCURL_LIBS)
 
 dnl CCAN wants to know a lot of vars.

+ 13 - 4
miner.c

@@ -449,6 +449,8 @@ struct pool *add_pool(void)
 		quit(1, "Failed to pthread_mutex_init in add_pool");
 	if (unlikely(pthread_cond_init(&pool->cr_cond, NULL)))
 		quit(1, "Failed to pthread_cond_init in add_pool");
+	if (unlikely(pthread_mutex_init(&pool->stratum_lock, NULL)))
+		quit(1, "Failed to pthread_mutex_init in add_pool");
 	INIT_LIST_HEAD(&pool->curlring);
 
 	/* Make sure the pool doesn't think we've been idle since time 0 */
@@ -3386,8 +3388,8 @@ next_submit:
 	if (work->stratum) {
 		struct stratum_share *sshare = calloc(sizeof(struct stratum_share), 1);
 		uint32_t *hash32 = (uint32_t *)work->hash, nonce;
-		char *s = alloca(1024);
 		char *noncehex;
+		char s[1024];
 
 		memcpy(&sshare->work, work, sizeof(struct work));
 
@@ -3400,6 +3402,7 @@ next_submit:
 		nonce = *((uint32_t *)(work->data + 76));
 		noncehex = bin2hex((const unsigned char *)&nonce, 4);
 
+		memset(s, 0, 1024);
 		sprintf(s, "{\"params\": [\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"], \"id\": %d, \"method\": \"mining.submit\"}",
 			pool->rpc_user, work->job_id, work->nonce2, work->ntime, noncehex, sshare->id);
 		free(noncehex);
@@ -4987,7 +4990,7 @@ static void *stratum_thread(void *userdata)
 				sleep(5);
 			}
 		}
-		s = recv_line(pool->sock);
+		s = recv_line(pool);
 		if (unlikely(!s))
 			continue;
 		if (!parse_method(pool, s) && !parse_stratum_response(s))
@@ -5104,7 +5107,8 @@ tryagain:
 	gettimeofday(&tv_getwork_reply, NULL);
 
 	/* Detect if a http getwork pool has an X-Stratum header at startup,
-	 * and if so, switch to that in preference to getwork */
+	 * and if so, switch to that in preference to getwork. Currently no
+	 * proxy support so don't try to switch if a proxy is in use. */
 	if (unlikely(pool->stratum_url)) {
 		applog(LOG_NOTICE, "Switching pool %d %s to %s", pool->pool_no, pool->rpc_url, pool->stratum_url);
 		pool->has_stratum = true;
@@ -5384,7 +5388,7 @@ static void set_work_target(struct work *work, int diff)
 			free(htarget);
 		}
 	}
-	memcpy(work->target, target, 256);
+	memcpy(work->target, target, 32);
 }
 
 static void gen_stratum_work(struct pool *pool, struct work *work)
@@ -5394,6 +5398,10 @@ static void gen_stratum_work(struct pool *pool, struct work *work)
 	int len, cb1_len, n1_len, cb2_len, i;
 	uint32_t *data32, *swap32;
 
+	memset(work->job_id, 0, 64);
+	memset(work->nonce2, 0, 64);
+	memset(work->ntime, 0, 16);
+
 	mutex_lock(&pool->pool_lock);
 
 	/* Generate coinbase */
@@ -5452,6 +5460,7 @@ static void gen_stratum_work(struct pool *pool, struct work *work)
 
 	applog(LOG_DEBUG, "Generated stratum merkle %s", merkle_hash);
 	applog(LOG_DEBUG, "Generated stratum header %s", header);
+	applog(LOG_DEBUG, "Work job_id %s nonce2 %s ntime %s", work->job_id, work->nonce2, work->ntime);
 
 	free(merkle_hash);
 

+ 7 - 0
miner.h

@@ -872,6 +872,9 @@ struct stratum_work {
 	int diff;
 };
 
+#define RECVSIZE 8191
+#define RBUFSIZE (RECVSIZE + 1)
+
 struct pool {
 	int pool_no;
 	int prio;
@@ -943,7 +946,10 @@ struct pool {
 
 	/* Stratum variables */
 	char *stratum_url;
+	char *stratum_port;
+	CURL *stratum_curl;
 	SOCKETTYPE sock;
+	char sockbuf[RBUFSIZE];
 	struct sockaddr_in *server, client;
 	char *sockaddr_url; /* stripped url used for sockaddr */
 	char *nonce1;
@@ -954,6 +960,7 @@ struct pool {
 	bool stratum_auth;
 	struct stratum_work swork;
 	pthread_t stratum_thread;
+	pthread_mutex_t stratum_lock;
 };
 
 #define GETWORK_MODE_TESTPOOL 'T'

+ 105 - 57
util.c

@@ -216,7 +216,6 @@ out:
 	return ptrlen;
 }
 
-#ifdef CURL_HAS_SOCKOPT
 int json_rpc_call_sockopt_cb(void __maybe_unused *userdata, curl_socket_t fd,
 			     curlsocktype __maybe_unused purpose)
 {
@@ -264,7 +263,6 @@ int json_rpc_call_sockopt_cb(void __maybe_unused *userdata, curl_socket_t fd,
 
 	return 0;
 }
-#endif
 
 static void last_nettime(struct timeval *last)
 {
@@ -341,10 +339,8 @@ json_t *json_rpc_call(CURL *curl, const char *url,
 		curl_easy_setopt(curl, CURLOPT_USERPWD, userpass);
 		curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
 	}
-#ifdef CURL_HAS_SOCKOPT
 	if (longpoll)
 		curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, json_rpc_call_sockopt_cb);
-#endif
 	curl_easy_setopt(curl, CURLOPT_POST, 1);
 
 	if (opt_protocol)
@@ -789,7 +785,7 @@ double tdiff(struct timeval *end, struct timeval *start)
 bool extract_sockaddr(struct pool *pool, char *url)
 {
 	char *url_begin, *url_end, *port_start = NULL;
-	char *url_address, *port;
+	char url_address[256], port[6];
 	struct addrinfo hints, *res;
 	int url_len, port_len = 0;
 
@@ -811,16 +807,14 @@ bool extract_sockaddr(struct pool *pool, char *url)
 	if (url_len < 1)
 		return false;
 
-	url_address = alloca(url_len + 1);
 	sprintf(url_address, "%.*s", url_len, url_begin);
 
-	if (port_len) {
-		port = alloca(port_len + 1);
+	if (port_len)
 		sprintf(port, "%.*s", port_len, port_start);
-	} else {
-		port = alloca(4);
+	else
 		strcpy(port, "80");
-	}
+
+	pool->stratum_port = strdup(port);
 
 	memset(&hints, 0, sizeof(struct addrinfo));
 	hints.ai_family = AF_UNSPEC;
@@ -840,8 +834,7 @@ bool extract_sockaddr(struct pool *pool, char *url)
 /* Send a single command across a socket, appending \n to it */
 bool stratum_send(struct pool *pool, char *s, ssize_t len)
 {
-	SOCKETTYPE sock = pool->sock;
-	ssize_t sent = 0;
+	ssize_t ssent = 0;
 	bool ret = false;
 
 	if (opt_protocol)
@@ -850,37 +843,45 @@ bool stratum_send(struct pool *pool, char *s, ssize_t len)
 	strcat(s, "\n");
 	len++;
 
-	mutex_lock(&pool->pool_lock);
+	mutex_lock(&pool->stratum_lock);
 	while (len > 0 ) {
-		sent = send(sock, s + sent, len, 0);
-		if (SOCKETFAIL(sent)) {
+		size_t sent = 0;
+
+		if (curl_easy_send(pool->stratum_curl, s + ssent, len, &sent) != CURLE_OK) {
+			applog(LOG_DEBUG, "Failed to curl_easy_send in stratum_send");
 			ret = false;
 			goto out_unlock;
 		}
-		len -= sent;
+		ssent += sent;
+		len -= ssent;
 	}
 	ret = true;
-	fsync(sock);
 out_unlock:
-	mutex_unlock(&pool->pool_lock);
+	mutex_unlock(&pool->stratum_lock);
 	return ret;;
 }
 
 #define RECVSIZE 8191
+#define RBUFSIZE (RECVSIZE + 1)
 
-static void clear_sock(SOCKETTYPE sock)
+static void clear_sock(struct pool *pool)
 {
-	char *s = alloca(RECVSIZE);
+	SOCKETTYPE sock = pool->sock;
 
-	recv(sock, s, RECVSIZE, MSG_DONTWAIT);
+	recv(sock, pool->sockbuf, RECVSIZE, MSG_DONTWAIT);
+	strcpy(pool->sockbuf, "");
 }
 
 /* Check to see if Santa's been good to you */
-static bool sock_full(SOCKETTYPE sock, bool wait)
+static bool sock_full(struct pool *pool, bool wait)
 {
+	SOCKETTYPE sock = pool->sock;
 	struct timeval timeout;
 	fd_set rd;
 
+	if (strlen(pool->sockbuf))
+		return true;
+
 	FD_ZERO(&rd);
 	FD_SET(sock, &rd);
 	timeout.tv_usec = 0;
@@ -895,31 +896,51 @@ static bool sock_full(SOCKETTYPE sock, bool wait)
 
 /* Peeks at a socket to find the first end of line and then reads just that
  * from the socket and returns that as a malloced char */
-char *recv_line(SOCKETTYPE sock)
+char *recv_line(struct pool *pool)
 {
-	char *sret = NULL, *s, c;
-	ssize_t offset = 0;
+	ssize_t len, buflen;
+	char *tok, *sret = NULL;
+	size_t n;
 
-	s = alloca(RECVSIZE + 1);
-	if (SOCKETFAIL(recv(sock, s, RECVSIZE, MSG_PEEK))) {
-		applog(LOG_DEBUG, "Failed to recv sock in recv_line");
-		goto out;
+	if (!strstr(pool->sockbuf, "\n")) {
+		char s[RBUFSIZE];
+		CURLcode rc;
+
+		if (!sock_full(pool, true)) {
+			applog(LOG_DEBUG, "Timed out waiting for data on sock_full");
+			goto out;
+		}
+		memset(s, 0, RBUFSIZE);
+
+		mutex_lock(&pool->stratum_lock);
+		rc = curl_easy_recv(pool->stratum_curl, s, RECVSIZE, &n);
+		mutex_unlock(&pool->stratum_lock);
+
+		if (rc != CURLE_OK) {
+			applog(LOG_DEBUG, "Failed to recv sock in recv_line");
+			goto out;
+		}
+		strcat(pool->sockbuf, s);
 	}
-	sret = strtok(s, "\n");
-	if (!sret) {
+
+	buflen = strlen(pool->sockbuf);
+	tok = strtok(pool->sockbuf, "\n");
+	if (!tok) {
 		applog(LOG_DEBUG, "Failed to parse a \\n terminated string in recv_line");
 		goto out;
 	}
+	sret = strdup(tok);
+	len = strlen(sret);
 
-	do {
-		read(sock, &c, 1);
-		memcpy(s + offset++, &c, 1);
-	} while (strncmp(&c, "\n", 1));
-	sret = strdup(s);
-	strcpy(sret + offset - 1, "\0");
+	/* Copy what's left in the buffer after the \n, including the
+	 * terminating \0 */
+	if (buflen > len + 1)
+		memmove(pool->sockbuf, pool->sockbuf + len + 1, buflen - len + 1);
+	else
+		strcpy(pool->sockbuf, "");
 out:
 	if (!sret)
-		clear_sock(sock);
+		clear_sock(pool);
 	else if (opt_protocol)
 		applog(LOG_DEBUG, "RECVD: %s", sret);
 	return sret;
@@ -1111,19 +1132,18 @@ out:
 bool auth_stratum(struct pool *pool)
 {
 	json_t *val = NULL, *res_val, *err_val;
-	char *s, *sret = NULL;
+	char s[RBUFSIZE], *sret = NULL;
 	json_error_t err;
 	bool ret = false;
 
-	s = alloca(RECVSIZE + 1);
 	sprintf(s, "{\"id\": %d, \"method\": \"mining.authorize\", \"params\": [\"%s\", \"%s\"]}",
 		swork_id++, pool->rpc_user, pool->rpc_pass);
 
 	/* Parse all data prior sending auth request */
-	while (sock_full(pool->sock, false)) {
-		sret = recv_line(pool->sock);
+	while (sock_full(pool, false)) {
+		sret = recv_line(pool);
 		if (!parse_method(pool, sret)) {
-			clear_sock(pool->sock);
+			clear_sock(pool);
 			applog(LOG_WARNING, "Failed to parse stratum buffer");
 			free(sret);
 			return ret;
@@ -1134,7 +1154,7 @@ bool auth_stratum(struct pool *pool)
 	if (!stratum_send(pool, s, strlen(s)))
 		goto out;
 
-	sret = recv_line(pool->sock);
+	sret = recv_line(pool);
 	if (!sret)
 		goto out;
 	val = JSON_LOADS(sret, &err);
@@ -1168,35 +1188,59 @@ out:
 bool initiate_stratum(struct pool *pool)
 {
 	json_t *val = NULL, *res_val, *err_val;
-	char *s, *sret = NULL;
+	char curl_err_str[CURL_ERROR_SIZE];
+	char s[RBUFSIZE], *sret = NULL;
+	CURL *curl = NULL;
 	json_error_t err;
 	bool ret = false;
 
 	if (pool->stratum_active)
 		return true;
 
-	s = alloca(RECVSIZE + 1);
-	sprintf(s, "{\"id\": %d, \"method\": \"mining.subscribe\", \"params\": []}", swork_id++);
+	if (!pool->stratum_curl) {
+		pool->stratum_curl = curl_easy_init();
+		if (unlikely(!pool->stratum_curl))
+			quit(1, "Failed to curl_easy_init in initiate_stratum");
+	}
+	curl = pool->stratum_curl;
+
+	/* Create a http url for use with curl */
+	memset(s, 0, RBUFSIZE);
+	sprintf(s, "http://%s:%s", pool->sockaddr_url, pool->stratum_port);
 
-	pool->sock = socket(AF_INET, SOCK_STREAM, 0);
-	if (pool->sock == INVSOCK)
-		quit(1, "Failed to create pool socket in initiate_stratum");
-	if (SOCKETFAIL(connect(pool->sock, (struct sockaddr *)pool->server, sizeof(struct sockaddr)))) {
-		applog(LOG_DEBUG, "Failed to connect socket to pool");
+	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 60);
+	curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_err_str);
+	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+	curl_easy_setopt(curl, CURLOPT_URL, s);
+	curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, 1);
+	curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
+	if (pool->rpc_proxy) {
+		curl_easy_setopt(curl, CURLOPT_PROXY, pool->rpc_proxy);
+		curl_easy_setopt(curl, CURLOPT_PROXYTYPE, pool->rpc_proxytype);
+	} else if (opt_socks_proxy) {
+		curl_easy_setopt(curl, CURLOPT_PROXY, opt_socks_proxy);
+		curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
+	}
+	curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1);
+	if (curl_easy_perform(curl)) {
+		applog(LOG_INFO, "Stratum connect failed to pool %d: %s", pool->pool_no, curl_err_str);
 		goto out;
 	}
+	curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, (long *)&pool->sock);
+
+	sprintf(s, "{\"id\": %d, \"method\": \"mining.subscribe\", \"params\": []}", swork_id++);
 
 	if (!stratum_send(pool, s, strlen(s))) {
 		applog(LOG_DEBUG, "Failed to send s in initiate_stratum");
 		goto out;
 	}
 
-	if (!sock_full(pool->sock, true)) {
+	if (!sock_full(pool, true)) {
 		applog(LOG_DEBUG, "Timed out waiting for response in initiate_stratum");
 		goto out;
 	}
 
-	sret = recv_line(pool->sock);
+	sret = recv_line(pool);
 	if (!sret)
 		goto out;
 
@@ -1250,8 +1294,12 @@ out:
 			applog(LOG_DEBUG, "Pool %d confirmed mining.subscribe with extranonce1 %s extran2size %d",
 			       pool->pool_no, pool->nonce1, pool->n2size);
 		}
-	} else
-		CLOSESOCKET(pool->sock);
+	} else {
+		if (curl) {
+			curl_easy_cleanup(curl);
+			pool->stratum_curl = NULL;
+		}
+	}
 
 	return ret;
 }

+ 2 - 2
util.h

@@ -7,7 +7,7 @@
 	#include <netinet/in.h>
 	#include <arpa/inet.h>
 
-	#define SOCKETTYPE int
+	#define SOCKETTYPE long
 	#define SOCKETFAIL(a) ((a) < 0)
 	#define INVSOCK -1
 	#define INVINETADDR -1
@@ -44,7 +44,7 @@
 
 struct pool;
 bool stratum_send(struct pool *pool, char *s, ssize_t len);
-char *recv_line(SOCKETTYPE sock);
+char *recv_line(struct pool *pool);
 bool parse_method(struct pool *pool, char *s);
 bool extract_sockaddr(struct pool *pool, char *url);
 bool auth_stratum(struct pool *pool);