Browse Source

Merge branch 'work2d' into stratumsrv_scrypt

Luke Dashjr 12 years ago
parent
commit
d124b5a5dd
10 changed files with 375 additions and 148 deletions
  1. 4 0
      Makefile.am
  2. 5 0
      configure.ac
  3. 24 68
      driver-stratum.c
  4. 1 1
      libblkmaker
  5. 165 67
      miner.c
  6. 19 5
      miner.h
  7. 12 7
      util.c
  8. 9 0
      util.h
  9. 117 0
      work2d.c
  10. 19 0
      work2d.h

+ 4 - 0
Makefile.am

@@ -197,6 +197,10 @@ if NEED_DYNCLOCK
 bfgminer_SOURCES += dynclock.c dynclock.h
 bfgminer_SOURCES += dynclock.c dynclock.h
 endif
 endif
 
 
+if NEED_BFG_WORK2D
+bfgminer_SOURCES  += work2d.c work2d.h
+endif
+
 if HAS_FPGA
 if HAS_FPGA
 dist_doc_DATA += README.FPGA
 dist_doc_DATA += README.FPGA
 endif
 endif

+ 5 - 0
configure.ac

@@ -127,6 +127,7 @@ need_lowlevel=no
 need_lowl_hid=no
 need_lowl_hid=no
 need_lowl_pci=no
 need_lowl_pci=no
 need_lowl_usb=no
 need_lowl_usb=no
+need_work2d=no
 have_cygwin=false
 have_cygwin=false
 have_win32=false
 have_win32=false
 have_macho=false
 have_macho=false
@@ -550,6 +551,9 @@ if test "x$libevent" != "xno"; then
 			AC_MSG_WARN([libevent 2.0.3+ not found; stratum proxy will be unavailable])
 			AC_MSG_WARN([libevent 2.0.3+ not found; stratum proxy will be unavailable])
 		fi
 		fi
 	])
 	])
+	if test "x$libevent" = "xyes"; then
+		need_work2d=yes
+	fi
 fi
 fi
 AM_CONDITIONAL([USE_LIBEVENT], [test x$libevent = xyes])
 AM_CONDITIONAL([USE_LIBEVENT], [test x$libevent = xyes])
 
 
@@ -1261,6 +1265,7 @@ AM_CONDITIONAL([NEED_BFG_LOWL_VCOM], [test x$need_lowl_vcom = xyes])
 AM_CONDITIONAL([NEED_BFG_LOWL_HID], [test x$need_lowl_hid = xyes])
 AM_CONDITIONAL([NEED_BFG_LOWL_HID], [test x$need_lowl_hid = xyes])
 AM_CONDITIONAL([NEED_BFG_LOWL_PCI], [test x$need_lowl_pci = xyes])
 AM_CONDITIONAL([NEED_BFG_LOWL_PCI], [test x$need_lowl_pci = xyes])
 AM_CONDITIONAL([NEED_BFG_LOWLEVEL], [test x$need_lowlevel = xyes])
 AM_CONDITIONAL([NEED_BFG_LOWLEVEL], [test x$need_lowlevel = xyes])
+AM_CONDITIONAL([NEED_BFG_WORK2D], [test x$need_work2d = xyes])
 AM_CONDITIONAL([HAS_SCRYPT], [test x$scrypt = xyes])
 AM_CONDITIONAL([HAS_SCRYPT], [test x$scrypt = xyes])
 AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes])
 AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes])
 AM_CONDITIONAL([HAVE_SENSORS], [test x$with_sensors = xyes])
 AM_CONDITIONAL([HAVE_SENSORS], [test x$with_sensors = xyes])

+ 24 - 68
driver-stratum.c

@@ -30,12 +30,10 @@
 #include "driver-proxy.h"
 #include "driver-proxy.h"
 #include "miner.h"
 #include "miner.h"
 #include "util.h"
 #include "util.h"
+#include "work2d.h"
 
 
-#define MAX_CLIENTS 255
-
-static bool _ssm_xnonce1s[MAX_CLIENTS + 1] = { true };
-static uint8_t _ssm_client_octets;
-static uint8_t _ssm_client_xnonce2sz;
+#define _ssm_client_octets     work2d_xnonce1sz
+#define _ssm_client_xnonce2sz  work2d_xnonce2sz
 static char *_ssm_notify;
 static char *_ssm_notify;
 static int _ssm_notify_sz;
 static int _ssm_notify_sz;
 static struct event *ev_notify;
 static struct event *ev_notify;
@@ -44,12 +42,8 @@ static notifier_t _ssm_update_notifier;
 struct stratumsrv_job {
 struct stratumsrv_job {
 	char *my_job_id;
 	char *my_job_id;
 	
 	
-	struct pool *pool;
-	uint8_t work_restart_id;
-	uint8_t n2size;
 	struct timeval tv_prepared;
 	struct timeval tv_prepared;
 	struct stratum_work swork;
 	struct stratum_work swork;
-	char *nonce1;
 	
 	
 	UT_hash_handle hh;
 	UT_hash_handle hh;
 };
 };
@@ -73,31 +67,7 @@ struct stratumsrv_conn {
 
 
 static struct stratumsrv_conn *_ssm_connections;
 static struct stratumsrv_conn *_ssm_connections;
 
 
-static
-void _ssm_gen_dummy_work(struct work *work, struct stratumsrv_job *ssj, const char * const extranonce2, uint32_t xnonce1)
-{
-	uint8_t *p, *s;
-	
-	*work = (struct work){
-		.pool = ssj->pool,
-		.work_restart_id = ssj->work_restart_id,
-		.tv_staged = ssj->tv_prepared,
-	};
-	bytes_resize(&work->nonce2, ssj->n2size);
-	s = bytes_buf(&work->nonce2);
-	p = &s[ssj->n2size - _ssm_client_xnonce2sz];
-	if (extranonce2)
-		hex2bin(p, extranonce2, _ssm_client_xnonce2sz);
-#ifndef __OPTIMIZE__
-	else
-		memset(p, '\0', _ssm_client_xnonce2sz);
-#endif
-	p -= _ssm_client_octets;
-	memcpy(p, &xnonce1, _ssm_client_octets);
-	if (p != s)
-		memset(s, '\xbb', p - s);
-	gen_stratum_work2(work, &ssj->swork, ssj->nonce1);
-}
+#define _ssm_gen_dummy_work work2d_gen_dummy_work
 
 
 static
 static
 bool stratumsrv_update_notify_str(struct pool * const pool, bool clean)
 bool stratumsrv_update_notify_str(struct pool * const pool, bool clean)
@@ -106,11 +76,11 @@ bool stratumsrv_update_notify_str(struct pool * const pool, bool clean)
 	
 	
 	struct stratumsrv_conn *conn;
 	struct stratumsrv_conn *conn;
 	const struct stratum_work * const swork = &pool->swork;
 	const struct stratum_work * const swork = &pool->swork;
-	const int n2size = pool->n2size;
+	const int n2size = pool->swork.n2size;
 	char my_job_id[33];
 	char my_job_id[33];
 	int i;
 	int i;
 	struct stratumsrv_job *ssj;
 	struct stratumsrv_job *ssj;
-	ssize_t n2pad = n2size - _ssm_client_octets - _ssm_client_xnonce2sz;
+	ssize_t n2pad = work2d_pad_xnonce_size(swork);
 	if (n2pad < 0)
 	if (n2pad < 0)
 		return false;
 		return false;
 	size_t coinb1in_lenx = swork->nonce2_offset * 2;
 	size_t coinb1in_lenx = swork->nonce2_offset * 2;
@@ -126,7 +96,7 @@ bool stratumsrv_update_notify_str(struct pool * const pool, bool clean)
 	uint32_t ntime_n;
 	uint32_t ntime_n;
 	bin2hex(prevhash, &swork->header1[4], 32);
 	bin2hex(prevhash, &swork->header1[4], 32);
 	bin2hex(coinb1, bytes_buf(&swork->coinbase), swork->nonce2_offset);
 	bin2hex(coinb1, bytes_buf(&swork->coinbase), swork->nonce2_offset);
-	memset(&coinb1[coinb1in_lenx], 'B', n2padx);
+	work2d_pad_xnonce(&coinb1[coinb1in_lenx], swork, true);
 	coinb1[coinb1_lenx] = '\0';
 	coinb1[coinb1_lenx] = '\0';
 	bin2hex(coinb2, &bytes_buf(&swork->coinbase)[swork->nonce2_offset + n2size], coinb2_len);
 	bin2hex(coinb2, &bytes_buf(&swork->coinbase)[swork->nonce2_offset + n2size], coinb2_len);
 	p += sprintf(p, "{\"params\":[\"%s\",\"%s\",\"%s\",\"%s\",[", my_job_id, prevhash, coinb1, coinb2);
 	p += sprintf(p, "{\"params\":[\"%s\",\"%s\",\"%s\",\"%s\",[", my_job_id, prevhash, coinb1, coinb2);
@@ -148,11 +118,6 @@ bool stratumsrv_update_notify_str(struct pool * const pool, bool clean)
 	ssj = malloc(sizeof(*ssj));
 	ssj = malloc(sizeof(*ssj));
 	*ssj = (struct stratumsrv_job){
 	*ssj = (struct stratumsrv_job){
 		.my_job_id = strdup(my_job_id),
 		.my_job_id = strdup(my_job_id),
-		
-		.pool = pool,
-		.work_restart_id = pool->work_restart_id,
-		.n2size = n2size,
-		.nonce1 = strdup(pool->nonce1),
 	};
 	};
 	timer_set_now(&ssj->tv_prepared);
 	timer_set_now(&ssj->tv_prepared);
 	stratum_work_cpy(&ssj->swork, swork);
 	stratum_work_cpy(&ssj->swork, swork);
@@ -164,7 +129,7 @@ bool stratumsrv_update_notify_str(struct pool * const pool, bool clean)
 	
 	
 	if (likely(_ssm_cur_job_work.pool))
 	if (likely(_ssm_cur_job_work.pool))
 		clean_work(&_ssm_cur_job_work);
 		clean_work(&_ssm_cur_job_work);
-	_ssm_gen_dummy_work(&_ssm_cur_job_work, ssj, NULL, 0);
+	_ssm_gen_dummy_work(&_ssm_cur_job_work, &ssj->swork, &ssj->tv_prepared, NULL, 0);
 	
 	
 	_ssm_notify_sz = p - buf;
 	_ssm_notify_sz = p - buf;
 	assert(_ssm_notify_sz <= bufsz);
 	assert(_ssm_notify_sz <= bufsz);
@@ -186,7 +151,6 @@ void _ssj_free(struct stratumsrv_job * const ssj)
 {
 {
 	free(ssj->my_job_id);
 	free(ssj->my_job_id);
 	stratum_work_clean(&ssj->swork);
 	stratum_work_clean(&ssj->swork);
-	free(ssj->nonce1);
 	free(ssj);
 	free(ssj);
 }
 }
 
 
@@ -277,11 +241,11 @@ void _stratumsrv_update_notify(evutil_socket_t fd, short what, __maybe_unused vo
 	else
 	else
 		stratumsrv_job_pruner();
 		stratumsrv_job_pruner();
 	
 	
-	if (!pool->stratum_notify)
+	if (!pool_has_usable_swork(pool))
 	{
 	{
-		applog(LOG_WARNING, "SSM: Not using a stratum server upstream!");
+		applog(LOG_WARNING, "SSM: No usable 2D work upstream!");
 		if (clean)
 		if (clean)
-			stratumsrv_boot_all_subscribed("Current upstream pool does not have active stratum");
+			stratumsrv_boot_all_subscribed("Current upstream pool does not have usable 2D work");
 		goto out;
 		goto out;
 	}
 	}
 	
 	
@@ -368,12 +332,8 @@ void stratumsrv_mining_subscribe(struct bufferevent *bev, json_t *params, const
 	
 	
 	if (!*xnonce1_p)
 	if (!*xnonce1_p)
 	{
 	{
-		uint32_t xnonce1;
-		for (xnonce1 = MAX_CLIENTS; _ssm_xnonce1s[xnonce1]; --xnonce1)
-			if (!xnonce1)
-				return_stratumsrv_failure(20, "Maximum clients already connected");
-		_ssm_xnonce1s[xnonce1] = true;
-		*xnonce1_p = htole32(xnonce1);
+		if (!reserve_work2d_(xnonce1_p))
+			return_stratumsrv_failure(20, "Maximum clients already connected");
 	}
 	}
 	
 	
 	bin2hex(xnonce1x, xnonce1_p, _ssm_client_octets);
 	bin2hex(xnonce1x, xnonce1_p, _ssm_client_octets);
@@ -398,7 +358,6 @@ static
 void stratumsrv_mining_submit(struct bufferevent *bev, json_t *params, const char *idstr, struct stratumsrv_conn * const conn)
 void stratumsrv_mining_submit(struct bufferevent *bev, json_t *params, const char *idstr, struct stratumsrv_conn * const conn)
 {
 {
 	uint32_t * const xnonce1_p = &conn->xnonce1_le;
 	uint32_t * const xnonce1_p = &conn->xnonce1_le;
-	struct work _work, *work;
 	struct stratumsrv_job *ssj;
 	struct stratumsrv_job *ssj;
 	struct proxy_client *client = stratumsrv_find_or_create_client(__json_array_string(params, 0));
 	struct proxy_client *client = stratumsrv_find_or_create_client(__json_array_string(params, 0));
 	struct cgpu_info *cgpu;
 	struct cgpu_info *cgpu;
@@ -407,7 +366,10 @@ void stratumsrv_mining_submit(struct bufferevent *bev, json_t *params, const cha
 	const char * const extranonce2 = __json_array_string(params, 2);
 	const char * const extranonce2 = __json_array_string(params, 2);
 	const char * const ntime = __json_array_string(params, 3);
 	const char * const ntime = __json_array_string(params, 3);
 	const char * const nonce = __json_array_string(params, 4);
 	const char * const nonce = __json_array_string(params, 4);
-	uint32_t nonce_n;
+	uint8_t xnonce2[work2d_xnonce2sz];
+	uint32_t ntime_n, nonce_n;
+	const float nonce_diff = 1;
+	bool is_stale;
 	
 	
 	if (unlikely(!client))
 	if (unlikely(!client))
 		return_stratumsrv_failure(20, "Failed creating new cgpu");
 		return_stratumsrv_failure(20, "Failed creating new cgpu");
@@ -428,24 +390,21 @@ void stratumsrv_mining_submit(struct bufferevent *bev, json_t *params, const cha
 	if (!ssj)
 	if (!ssj)
 		return_stratumsrv_failure(21, "Job not found");
 		return_stratumsrv_failure(21, "Job not found");
 	
 	
-	// Generate dummy work
-	work = &_work;
-	_ssm_gen_dummy_work(work, ssj, extranonce2, *xnonce1_p);
+	hex2bin(xnonce2, extranonce2, work2d_xnonce2sz);
 	
 	
 	// Submit nonce
 	// Submit nonce
-	hex2bin(&work->data[68], ntime, 4);
+	hex2bin((void*)&ntime_n, ntime, 4);
+	ntime_n = be32toh(ntime_n);
 	hex2bin((void*)&nonce_n, nonce, 4);
 	hex2bin((void*)&nonce_n, nonce, 4);
 	nonce_n = le32toh(nonce_n);
 	nonce_n = le32toh(nonce_n);
-	if (!submit_nonce(thr, work, nonce_n))
+	if (!work2d_submit_nonce(thr, &ssj->swork, &ssj->tv_prepared, xnonce2, *xnonce1_p, nonce_n, ntime_n, &is_stale, nonce_diff))
 		_stratumsrv_failure(bev, idstr, 23, "H-not-zero");
 		_stratumsrv_failure(bev, idstr, 23, "H-not-zero");
 	else
 	else
-	if (stale_work(work, true))
+	if (is_stale)
 		_stratumsrv_failure(bev, idstr, 21, "stale");
 		_stratumsrv_failure(bev, idstr, 21, "stale");
 	else
 	else
 		_stratumsrv_success(bev, idstr);
 		_stratumsrv_success(bev, idstr);
 	
 	
-	clean_work(work);
-	
 	if (!conn->hashes_done_ext)
 	if (!conn->hashes_done_ext)
 	{
 	{
 		struct timeval tv_now, tv_delta;
 		struct timeval tv_now, tv_delta;
@@ -540,12 +499,11 @@ static
 void stratumsrv_client_close(struct stratumsrv_conn * const conn)
 void stratumsrv_client_close(struct stratumsrv_conn * const conn)
 {
 {
 	struct bufferevent * const bev = conn->bev;
 	struct bufferevent * const bev = conn->bev;
-	uint32_t xnonce1 = le32toh(conn->xnonce1_le);
 	
 	
 	bufferevent_free(bev);
 	bufferevent_free(bev);
 	LL_DELETE(_ssm_connections, conn);
 	LL_DELETE(_ssm_connections, conn);
+	release_work2d_(conn->xnonce1_le);
 	free(conn);
 	free(conn);
-	_ssm_xnonce1s[xnonce1] = false;
 }
 }
 
 
 static
 static
@@ -626,9 +584,7 @@ void *stratumsrv_thread(__maybe_unused void *p)
 	pthread_detach(pthread_self());
 	pthread_detach(pthread_self());
 	RenameThread("stratumsrv");
 	RenameThread("stratumsrv");
 	
 	
-	for (uint64_t n = MAX_CLIENTS; n; n >>= 8)
-		++_ssm_client_octets;
-	_ssm_client_xnonce2sz = 2;
+	work2d_init();
 	
 	
 	struct event_base *evbase = event_base_new();
 	struct event_base *evbase = event_base_new();
 	_smm_evbase = evbase;
 	_smm_evbase = evbase;

+ 1 - 1
libblkmaker

@@ -1 +1 @@
-Subproject commit bca8f6f5e56c547e9bbc808fb644152e44f3344d
+Subproject commit 9f5e949f7e1e0bb49bfe8eab1d99107b3114b4c7

+ 165 - 67
miner.c

@@ -1046,6 +1046,7 @@ struct pool *add_pool(void)
 	cglock_init(&pool->data_lock);
 	cglock_init(&pool->data_lock);
 	mutex_init(&pool->stratum_lock);
 	mutex_init(&pool->stratum_lock);
 	timer_unset(&pool->swork.tv_transparency);
 	timer_unset(&pool->swork.tv_transparency);
+	pool->swork.pool = pool;
 
 
 	/* Make sure the pool doesn't think we've been idle since time 0 */
 	/* Make sure the pool doesn't think we've been idle since time 0 */
 	pool->tv_idle.tv_sec = ~0UL;
 	pool->tv_idle.tv_sec = ~0UL;
@@ -2656,6 +2657,39 @@ static void calc_midstate(struct work *work)
 	swap32tole(work->midstate, work->midstate, 8);
 	swap32tole(work->midstate, work->midstate, 8);
 }
 }
 
 
+static
+struct bfg_tmpl_ref *tmpl_makeref(blktemplate_t * const tmpl)
+{
+	struct bfg_tmpl_ref * const tr = malloc(sizeof(*tr));
+	*tr = (struct bfg_tmpl_ref){
+		.tmpl = tmpl,
+		.refcount = 1,
+	};
+	mutex_init(&tr->mutex);
+	return tr;
+}
+
+static
+void tmpl_incref(struct bfg_tmpl_ref * const tr)
+{
+	mutex_lock(&tr->mutex);
+	++tr->refcount;
+	mutex_unlock(&tr->mutex);
+}
+
+void tmpl_decref(struct bfg_tmpl_ref * const tr)
+{
+	mutex_lock(&tr->mutex);
+	bool free_tmpl = !--tr->refcount;
+	mutex_unlock(&tr->mutex);
+	if (free_tmpl)
+	{
+		blktmpl_free(tr->tmpl);
+		mutex_destroy(&tr->mutex);
+		free(tr);
+	}
+}
+
 static struct work *make_work(void)
 static struct work *make_work(void)
 {
 {
 	struct work *work = calloc(1, sizeof(struct work));
 	struct work *work = calloc(1, sizeof(struct work));
@@ -2680,16 +2714,8 @@ void clean_work(struct work *work)
 	if (work->device_data_free_func)
 	if (work->device_data_free_func)
 		work->device_data_free_func(work);
 		work->device_data_free_func(work);
 
 
-	if (work->tmpl) {
-		struct pool *pool = work->pool;
-		mutex_lock(&pool->pool_lock);
-		bool free_tmpl = !--*work->tmpl_refcount;
-		mutex_unlock(&pool->pool_lock);
-		if (free_tmpl) {
-			blktmpl_free(work->tmpl);
-			free(work->tmpl_refcount);
-		}
-	}
+	if (work->tr)
+		tmpl_decref(work->tr);
 
 
 	memset(work, 0, sizeof(struct work));
 	memset(work, 0, sizeof(struct work));
 }
 }
@@ -2760,11 +2786,21 @@ void pool_set_opaque(struct pool *pool, bool opaque)
 		       pool->pool_no);
 		       pool->pool_no);
 }
 }
 
 
+static double target_diff(const unsigned char *);
+
+#define GBT_XNONCESZ (sizeof(uint32_t))
+
+#if 1 // FIXME BLKMAKER_VERSION > 4
+#define blkmk_append_coinbase_safe(tmpl, append, appendsz)  \
+       blkmk_append_coinbase_safe2(tmpl, append, appendsz, GBT_XNONCESZ, false)
+#endif
+
 static bool work_decode(struct pool *pool, struct work *work, json_t *val)
 static bool work_decode(struct pool *pool, struct work *work, json_t *val)
 {
 {
 	json_t *res_val = json_object_get(val, "result");
 	json_t *res_val = json_object_get(val, "result");
 	json_t *tmp_val;
 	json_t *tmp_val;
 	bool ret = false;
 	bool ret = false;
+	struct timeval tv_now;
 
 
 	if (unlikely(detect_algo == 1)) {
 	if (unlikely(detect_algo == 1)) {
 		json_t *tmp = json_object_get(res_val, "algorithm");
 		json_t *tmp = json_object_get(res_val, "algorithm");
@@ -2773,28 +2809,30 @@ static bool work_decode(struct pool *pool, struct work *work, json_t *val)
 			detect_algo = 2;
 			detect_algo = 2;
 	}
 	}
 	
 	
-	if (work->tmpl) {
-		struct timeval tv_now;
-		cgtime(&tv_now);
-		const char *err = blktmpl_add_jansson(work->tmpl, res_val, tv_now.tv_sec);
+	timer_set_now(&tv_now);
+	
+	if (work->tr)
+	{
+		blktemplate_t * const tmpl = work->tr->tmpl;
+		const char *err = blktmpl_add_jansson(tmpl, res_val, tv_now.tv_sec);
 		if (err) {
 		if (err) {
 			applog(LOG_ERR, "blktmpl error: %s", err);
 			applog(LOG_ERR, "blktmpl error: %s", err);
 			return false;
 			return false;
 		}
 		}
-		work->rolltime = blkmk_time_left(work->tmpl, tv_now.tv_sec);
+		work->rolltime = blkmk_time_left(tmpl, tv_now.tv_sec);
 #if BLKMAKER_VERSION > 1
 #if BLKMAKER_VERSION > 1
 		if (opt_coinbase_script.sz)
 		if (opt_coinbase_script.sz)
 		{
 		{
 			bool newcb;
 			bool newcb;
 #if BLKMAKER_VERSION > 2
 #if BLKMAKER_VERSION > 2
-			blkmk_init_generation2(work->tmpl, opt_coinbase_script.data, opt_coinbase_script.sz, &newcb);
+			blkmk_init_generation2(tmpl, opt_coinbase_script.data, opt_coinbase_script.sz, &newcb);
 #else
 #else
-			newcb = !work->tmpl->cbtxn;
-			blkmk_init_generation(work->tmpl, opt_coinbase_script.data, opt_coinbase_script.sz);
+			newcb = !tmpl->cbtxn;
+			blkmk_init_generation(tmpl, opt_coinbase_script.data, opt_coinbase_script.sz);
 #endif
 #endif
 			if (newcb)
 			if (newcb)
 			{
 			{
-				ssize_t ae = blkmk_append_coinbase_safe(work->tmpl, &template_nonce, sizeof(template_nonce));
+				ssize_t ae = blkmk_append_coinbase_safe(tmpl, &template_nonce, sizeof(template_nonce));
 				if (ae < (ssize_t)sizeof(template_nonce))
 				if (ae < (ssize_t)sizeof(template_nonce))
 					applog(LOG_WARNING, "Cannot append template-nonce to coinbase on pool %u (%"PRId64") - you might be wasting hashing!", work->pool->pool_no, (int64_t)ae);
 					applog(LOG_WARNING, "Cannot append template-nonce to coinbase on pool %u (%"PRId64") - you might be wasting hashing!", work->pool->pool_no, (int64_t)ae);
 				++template_nonce;
 				++template_nonce;
@@ -2803,7 +2841,7 @@ static bool work_decode(struct pool *pool, struct work *work, json_t *val)
 #endif
 #endif
 #if BLKMAKER_VERSION > 0
 #if BLKMAKER_VERSION > 0
 		{
 		{
-			ssize_t ae = blkmk_append_coinbase_safe(work->tmpl, opt_coinbase_sig, 101);
+			ssize_t ae = blkmk_append_coinbase_safe(tmpl, opt_coinbase_sig, 101);
 			static bool appenderr = false;
 			static bool appenderr = false;
 			if (ae <= 0) {
 			if (ae <= 0) {
 				if (opt_coinbase_sig) {
 				if (opt_coinbase_sig) {
@@ -2839,7 +2877,7 @@ static bool work_decode(struct pool *pool, struct work *work, json_t *val)
 					free(tmp);
 					free(tmp);
 					truncatewarning = true;
 					truncatewarning = true;
 				}
 				}
-				ae = blkmk_append_coinbase_safe(work->tmpl, cbappend, ae);
+				ae = blkmk_append_coinbase_safe(tmpl, cbappend, ae);
 				if (ae <= 0) {
 				if (ae <= 0) {
 					applog((appenderr ? LOG_DEBUG : LOG_WARNING), "Error appending coinbase signature (%"PRId64")", (int64_t)ae);
 					applog((appenderr ? LOG_DEBUG : LOG_WARNING), "Error appending coinbase signature (%"PRId64")", (int64_t)ae);
 					appenderr = true;
 					appenderr = true;
@@ -2848,13 +2886,13 @@ static bool work_decode(struct pool *pool, struct work *work, json_t *val)
 			}
 			}
 		}
 		}
 #endif
 #endif
-		if (blkmk_get_data(work->tmpl, work->data, 80, tv_now.tv_sec, NULL, &work->dataid) < 76)
+		if (blkmk_get_data(tmpl, work->data, 80, tv_now.tv_sec, NULL, &work->dataid) < 76)
 			return false;
 			return false;
 		swap32yes(work->data, work->data, 80 / 4);
 		swap32yes(work->data, work->data, 80 / 4);
 		memcpy(&work->data[80], workpadding_bin, 48);
 		memcpy(&work->data[80], workpadding_bin, 48);
 
 
 		const struct blktmpl_longpoll_req *lp;
 		const struct blktmpl_longpoll_req *lp;
-		if ((lp = blktmpl_get_longpoll(work->tmpl)) && ((!pool->lp_id) || strcmp(lp->id, pool->lp_id))) {
+		if ((lp = blktmpl_get_longpoll(tmpl)) && ((!pool->lp_id) || strcmp(lp->id, pool->lp_id))) {
 			free(pool->lp_id);
 			free(pool->lp_id);
 			pool->lp_id = strdup(lp->id);
 			pool->lp_id = strdup(lp->id);
 
 
@@ -2884,7 +2922,8 @@ static bool work_decode(struct pool *pool, struct work *work, json_t *val)
 		applog(LOG_ERR, "JSON inval target");
 		applog(LOG_ERR, "JSON inval target");
 		return false;
 		return false;
 	}
 	}
-	if (work->tmpl) {
+	if (work->tr)
+	{
 		for (size_t i = 0; i < sizeof(work->target) / 2; ++i)
 		for (size_t i = 0; i < sizeof(work->target) / 2; ++i)
 		{
 		{
 			int p = (sizeof(work->target) - 1) - i;
 			int p = (sizeof(work->target) - 1) - i;
@@ -2902,9 +2941,50 @@ static bool work_decode(struct pool *pool, struct work *work, json_t *val)
 
 
 	memset(work->hash, 0, sizeof(work->hash));
 	memset(work->hash, 0, sizeof(work->hash));
 
 
-	cgtime(&work->tv_staged);
+	work->tv_staged = tv_now;
 	
 	
-	pool_set_opaque(pool, !work->tmpl);
+#if 1 // FIXME BLKMAKER_VERSION > 4
+	if (work->tr)
+	{
+		blktemplate_t * const tmpl = work->tr->tmpl;
+		uint8_t buf[80];
+		int16_t expire;
+		uint8_t *cbtxn;
+		size_t cbtxnsz;
+		size_t cbextranonceoffset;
+		int branchcount;
+		libblkmaker_hash_t *branches;
+		
+		if (blkmk_get_mdata(tmpl, buf, sizeof(buf), tv_now.tv_sec, &expire, &cbtxn, &cbtxnsz, &cbextranonceoffset, &branchcount, &branches, GBT_XNONCESZ))
+		{
+			struct stratum_work * const swork = &pool->swork;
+			const size_t branchdatasz = branchcount * 0x20;
+			
+			cg_wlock(&pool->data_lock);
+			swork->tr = work->tr;
+			bytes_assimilate_raw(&swork->coinbase, cbtxn, cbtxnsz, cbtxnsz);
+			swork->nonce2_offset = cbextranonceoffset;
+			bytes_assimilate_raw(&swork->merkle_bin, branches, branchdatasz, branchdatasz);
+			swork->merkles = branchcount;
+			memcpy(swork->header1, &buf[0], 36);
+			swork->ntime = le32toh(*(uint32_t *)(&buf[68]));
+			swork->tv_received = tv_now;
+			memcpy(swork->diffbits, &buf[72], 4);
+			swork->diff = target_diff(work->target);
+			free(swork->job_id);
+			swork->job_id = NULL;
+			swork->clean = true;
+			swork->work_restart_id = pool->work_restart_id;
+			// FIXME: Do something with expire
+			pool->nonce2sz = swork->n2size = GBT_XNONCESZ;
+			pool->nonce2 = 0;
+			cg_wunlock(&pool->data_lock);
+		}
+		else
+			applog(LOG_DEBUG, "blkmk_get_mdata failed for pool %u", pool->pool_no);
+	}
+#endif  // BLKMAKER_VERSION > 4
+	pool_set_opaque(pool, !work->tr);
 
 
 	ret = true;
 	ret = true;
 
 
@@ -4112,7 +4192,7 @@ static
 void maybe_local_submit(const struct work *work)
 void maybe_local_submit(const struct work *work)
 {
 {
 #if BLKMAKER_VERSION > 3
 #if BLKMAKER_VERSION > 3
-	if (unlikely(work->block && work->tmpl))
+	if (unlikely(work->block && work->tr))
 	{
 	{
 		// This is a block with a full template (GBT)
 		// This is a block with a full template (GBT)
 		// Regardless of the result, submit to local bitcoind(s) as well
 		// Regardless of the result, submit to local bitcoind(s) as well
@@ -4278,17 +4358,19 @@ static char *submit_upstream_work_request(struct work *work)
 	char *s, *sd;
 	char *s, *sd;
 	struct pool *pool = work->pool;
 	struct pool *pool = work->pool;
 
 
-	if (work->tmpl) {
+	if (work->tr)
+	{
+		blktemplate_t * const tmpl = work->tr->tmpl;
 		json_t *req;
 		json_t *req;
 		unsigned char data[80];
 		unsigned char data[80];
 		
 		
 		swap32yes(data, work->data, 80 / 4);
 		swap32yes(data, work->data, 80 / 4);
 #if BLKMAKER_VERSION > 3
 #if BLKMAKER_VERSION > 3
 		if (work->do_foreign_submit)
 		if (work->do_foreign_submit)
-			req = blkmk_submit_foreign_jansson(work->tmpl, data, work->dataid, le32toh(*((uint32_t*)&work->data[76])));
+			req = blkmk_submit_foreign_jansson(tmpl, data, work->dataid, le32toh(*((uint32_t*)&work->data[76])));
 		else
 		else
 #endif
 #endif
-			req = blkmk_submit_jansson(work->tmpl, data, work->dataid, le32toh(*((uint32_t*)&work->data[76])));
+			req = blkmk_submit_jansson(tmpl, data, work->dataid, le32toh(*((uint32_t*)&work->data[76])));
 		s = json_dumps(req, 0);
 		s = json_dumps(req, 0);
 		json_decref(req);
 		json_decref(req);
 		sd = malloc(161);
 		sd = malloc(161);
@@ -4310,7 +4392,7 @@ static char *submit_upstream_work_request(struct work *work)
 	}
 	}
 
 
 	applog(LOG_DEBUG, "DBG: sending %s submit RPC call: %s", pool->rpc_url, sd);
 	applog(LOG_DEBUG, "DBG: sending %s submit RPC call: %s", pool->rpc_url, sd);
-	if (work->tmpl)
+	if (work->tr)
 		free(sd);
 		free(sd);
 	else
 	else
 		s = realloc_strcat(s, "\n");
 		s = realloc_strcat(s, "\n");
@@ -4633,7 +4715,7 @@ static void wake_gws(void);
 
 
 static void update_last_work(struct work *work)
 static void update_last_work(struct work *work)
 {
 {
-	if (!work->tmpl)
+	if (!work->tr)
 		// Only save GBT jobs, since rollntime isn't coordinated well yet
 		// Only save GBT jobs, since rollntime isn't coordinated well yet
 		return;
 		return;
 
 
@@ -4700,14 +4782,11 @@ static char *prepare_rpc_req2(struct work *work, enum pool_protocol proto, const
 			return strdup(getwork_req);
 			return strdup(getwork_req);
 		case PLP_GETBLOCKTEMPLATE:
 		case PLP_GETBLOCKTEMPLATE:
 			work->getwork_mode = GETWORK_MODE_GBT;
 			work->getwork_mode = GETWORK_MODE_GBT;
-			work->tmpl_refcount = malloc(sizeof(*work->tmpl_refcount));
-			if (!work->tmpl_refcount)
-				return NULL;
-			work->tmpl = blktmpl_create();
-			if (!work->tmpl)
+			blktemplate_t * const tmpl = blktmpl_create();
+			if (!tmpl)
 				goto gbtfail2;
 				goto gbtfail2;
-			*work->tmpl_refcount = 1;
-			gbt_capabilities_t caps = blktmpl_addcaps(work->tmpl);
+			work->tr = tmpl_makeref(tmpl);
+			gbt_capabilities_t caps = blktmpl_addcaps(tmpl);
 			if (!caps)
 			if (!caps)
 				goto gbtfail;
 				goto gbtfail;
 			caps |= GBT_LONGPOLL;
 			caps |= GBT_LONGPOLL;
@@ -4733,11 +4812,9 @@ static char *prepare_rpc_req2(struct work *work, enum pool_protocol proto, const
 	return NULL;
 	return NULL;
 
 
 gbtfail:
 gbtfail:
-	blktmpl_free(work->tmpl);
-	work->tmpl = NULL;
+	tmpl_decref(work->tr);
+	work->tr = NULL;
 gbtfail2:
 gbtfail2:
-	free(work->tmpl_refcount);
-	work->tmpl_refcount = NULL;
 	return NULL;
 	return NULL;
 }
 }
 
 
@@ -5104,10 +5181,11 @@ static inline bool can_roll(struct work *work)
 		return false;
 		return false;
 	if (!(work->pool && !work->clone))
 	if (!(work->pool && !work->clone))
 		return false;
 		return false;
-	if (work->tmpl) {
+	if (work->tr)
+	{
 		if (stale_work(work, false))
 		if (stale_work(work, false))
 			return false;
 			return false;
-		return blkmk_work_left(work->tmpl);
+		return blkmk_work_left(work->tr->tmpl);
 	}
 	}
 	return (work->rolltime &&
 	return (work->rolltime &&
 		work->rolls < 7000 && !stale_work(work, false));
 		work->rolls < 7000 && !stale_work(work, false));
@@ -5115,10 +5193,11 @@ static inline bool can_roll(struct work *work)
 
 
 static void roll_work(struct work *work)
 static void roll_work(struct work *work)
 {
 {
-	if (work->tmpl) {
+	if (work->tr)
+	{
 		struct timeval tv_now;
 		struct timeval tv_now;
 		cgtime(&tv_now);
 		cgtime(&tv_now);
-		if (blkmk_get_data(work->tmpl, work->data, 80, tv_now.tv_sec, NULL, &work->dataid) < 76)
+		if (blkmk_get_data(work->tr->tmpl, work->data, 80, tv_now.tv_sec, NULL, &work->dataid) < 76)
 			applog(LOG_ERR, "Failed to get next data from template; spinning wheels!");
 			applog(LOG_ERR, "Failed to get next data from template; spinning wheels!");
 		swap32yes(work->data, work->data, 80 / 4);
 		swap32yes(work->data, work->data, 80 / 4);
 		calc_midstate(work);
 		calc_midstate(work);
@@ -5162,12 +5241,8 @@ static void _copy_work(struct work *work, const struct work *base_work, int noff
 		work->nonce1 = strdup(base_work->nonce1);
 		work->nonce1 = strdup(base_work->nonce1);
 	bytes_cpy(&work->nonce2, &base_work->nonce2);
 	bytes_cpy(&work->nonce2, &base_work->nonce2);
 
 
-	if (base_work->tmpl) {
-		struct pool *pool = work->pool;
-		mutex_lock(&pool->pool_lock);
-		++*work->tmpl_refcount;
-		mutex_unlock(&pool->pool_lock);
-	}
+	if (base_work->tr)
+		tmpl_incref(base_work->tr);
 	
 	
 	if (noffset)
 	if (noffset)
 	{
 	{
@@ -5273,7 +5348,7 @@ bool stale_work(struct work *work, bool share)
 	/* Technically the rolltime should be correct but some pools
 	/* Technically the rolltime should be correct but some pools
 	 * advertise a broken expire= that is lower than a meaningful
 	 * advertise a broken expire= that is lower than a meaningful
 	 * scantime */
 	 * scantime */
-	if (work->rolltime >= opt_scantime || work->tmpl)
+	if (work->rolltime >= opt_scantime || work->tr)
 		work_expiry = work->rolltime;
 		work_expiry = work->rolltime;
 	else
 	else
 		work_expiry = opt_expiry;
 		work_expiry = opt_expiry;
@@ -5682,7 +5757,7 @@ next_write_sws:
 			cg_rlock(&pool->data_lock);
 			cg_rlock(&pool->data_lock);
 			// NOTE: cgminer only does this check on retries, but BFGMiner does it for even the first/normal submit; therefore, it needs to be such that it always is true on the same connection regardless of session management
 			// NOTE: cgminer only does this check on retries, but BFGMiner does it for even the first/normal submit; therefore, it needs to be such that it always is true on the same connection regardless of session management
 			// NOTE: Worst case scenario for a false positive: the pool rejects it as H-not-zero
 			// NOTE: Worst case scenario for a false positive: the pool rejects it as H-not-zero
-			sessionid_match = (!pool->nonce1) || !strcmp(work->nonce1, pool->nonce1);
+			sessionid_match = (!pool->swork.nonce1) || !strcmp(work->nonce1, pool->swork.nonce1);
 			cg_runlock(&pool->data_lock);
 			cg_runlock(&pool->data_lock);
 			if (!sessionid_match)
 			if (!sessionid_match)
 			{
 			{
@@ -6276,7 +6351,10 @@ static bool test_work_current(struct work *work)
 			}
 			}
 		}
 		}
 	  if (work->longpoll) {
 	  if (work->longpoll) {
+		struct pool * const pool = work->pool;
 		++work->pool->work_restart_id;
 		++work->pool->work_restart_id;
+		if (work->tr && work->tr == pool->swork.tr)
+			pool->swork.work_restart_id = pool->work_restart_id;
 		update_last_work(work);
 		update_last_work(work);
 		if ((!restart) && work->pool == current_pool()) {
 		if ((!restart) && work->pool == current_pool()) {
 			applog(
 			applog(
@@ -8270,6 +8348,7 @@ static void *stratum_thread(void *userdata)
 				have_block_height(block_id, height);
 				have_block_height(block_id, height);
 			}
 			}
 
 
+			pool->swork.work_restart_id =
 			++pool->work_restart_id;
 			++pool->work_restart_id;
 			if (test_work_current(work)) {
 			if (test_work_current(work)) {
 				/* Only accept a work update if this stratum
 				/* Only accept a work update if this stratum
@@ -8457,7 +8536,7 @@ badwork:
 		/* Decipher the longpoll URL, if any, and store it in ->lp_url */
 		/* Decipher the longpoll URL, if any, and store it in ->lp_url */
 
 
 		const struct blktmpl_longpoll_req *lp;
 		const struct blktmpl_longpoll_req *lp;
-		if (work->tmpl && (lp = blktmpl_get_longpoll(work->tmpl))) {
+		if (work->tr && (lp = blktmpl_get_longpoll(work->tr->tmpl))) {
 			// NOTE: work_decode takes care of lp id
 			// NOTE: work_decode takes care of lp id
 			pool->lp_url = lp->uri ? absolute_uri(lp->uri, pool->rpc_url) : pool->rpc_url;
 			pool->lp_url = lp->uri ? absolute_uri(lp->uri, pool->rpc_url) : pool->rpc_url;
 			if (!pool->lp_url)
 			if (!pool->lp_url)
@@ -8673,18 +8752,36 @@ void set_target(unsigned char *dest_target, double diff)
 void stratum_work_cpy(struct stratum_work * const dst, const struct stratum_work * const src)
 void stratum_work_cpy(struct stratum_work * const dst, const struct stratum_work * const src)
 {
 {
 	*dst = *src;
 	*dst = *src;
-	dst->job_id = strdup(src->job_id);
+	if (dst->tr)
+		tmpl_incref(dst->tr);
+	dst->nonce1 = maybe_strdup(src->nonce1);
+	dst->job_id = maybe_strdup(src->job_id);
 	bytes_cpy(&dst->coinbase, &src->coinbase);
 	bytes_cpy(&dst->coinbase, &src->coinbase);
 	bytes_cpy(&dst->merkle_bin, &src->merkle_bin);
 	bytes_cpy(&dst->merkle_bin, &src->merkle_bin);
 }
 }
 
 
 void stratum_work_clean(struct stratum_work * const swork)
 void stratum_work_clean(struct stratum_work * const swork)
 {
 {
+	if (swork->tr)
+		tmpl_decref(swork->tr);
+	free(swork->nonce1);
 	free(swork->job_id);
 	free(swork->job_id);
 	bytes_free(&swork->coinbase);
 	bytes_free(&swork->coinbase);
 	bytes_free(&swork->merkle_bin);
 	bytes_free(&swork->merkle_bin);
 }
 }
 
 
+bool pool_has_usable_swork(const struct pool * const pool)
+{
+	if (pool->swork.tr)
+	{
+		// GBT
+		struct timeval tv_now;
+		timer_set_now(&tv_now);
+		return blkmk_time_left(pool->swork.tr->tmpl, tv_now.tv_sec);
+	}
+	return pool->stratum_notify;
+}
+
 /* Generates stratum based work based on the most recent notify information
 /* Generates stratum based work based on the most recent notify information
  * from the pool. This will keep generating work while a pool is down so we use
  * from the pool. This will keep generating work while a pool is down so we use
  * other means to detect when the pool has died in stratum_thread */
  * other means to detect when the pool has died in stratum_thread */
@@ -8695,9 +8792,10 @@ static void gen_stratum_work(struct pool *pool, struct work *work)
 	cg_wlock(&pool->data_lock);
 	cg_wlock(&pool->data_lock);
 	pool->swork.data_lock_p = &pool->data_lock;
 	pool->swork.data_lock_p = &pool->data_lock;
 	
 	
-	bytes_resize(&work->nonce2, pool->n2size);
-	if (pool->nonce2sz < pool->n2size)
-		memset(&bytes_buf(&work->nonce2)[pool->nonce2sz], 0, pool->n2size - pool->nonce2sz);
+	const int n2size = pool->swork.n2size;
+	bytes_resize(&work->nonce2, n2size);
+	if (pool->nonce2sz < n2size)
+		memset(&bytes_buf(&work->nonce2)[pool->nonce2sz], 0, n2size - pool->nonce2sz);
 	memcpy(bytes_buf(&work->nonce2),
 	memcpy(bytes_buf(&work->nonce2),
 #ifdef WORDS_BIGENDIAN
 #ifdef WORDS_BIGENDIAN
 	// NOTE: On big endian, the most significant bits are stored at the end, so skip the LSBs
 	// NOTE: On big endian, the most significant bits are stored at the end, so skip the LSBs
@@ -8709,13 +8807,13 @@ static void gen_stratum_work(struct pool *pool, struct work *work)
 	pool->nonce2++;
 	pool->nonce2++;
 	
 	
 	work->pool = pool;
 	work->pool = pool;
-	work->work_restart_id = work->pool->work_restart_id;
-	gen_stratum_work2(work, &pool->swork, pool->nonce1);
+	work->work_restart_id = pool->swork.work_restart_id;
+	gen_stratum_work2(work, &pool->swork);
 	
 	
 	cgtime(&work->tv_staged);
 	cgtime(&work->tv_staged);
 }
 }
 
 
-void gen_stratum_work2(struct work *work, struct stratum_work *swork, const char *nonce1)
+void gen_stratum_work2(struct work *work, struct stratum_work *swork)
 {
 {
 	unsigned char *coinbase, merkle_root[32], merkle_sha[64];
 	unsigned char *coinbase, merkle_root[32], merkle_sha[64];
 	uint8_t *merkle_bin;
 	uint8_t *merkle_bin;
@@ -8755,8 +8853,8 @@ void gen_stratum_work2(struct work *work, struct stratum_work *swork, const char
 	work->sdiff = swork->diff;
 	work->sdiff = swork->diff;
 
 
 	/* Copy parameters required for share submission */
 	/* Copy parameters required for share submission */
-	work->job_id = strdup(swork->job_id);
-	work->nonce1 = strdup(nonce1);
+	work->job_id = maybe_strdup(swork->job_id);
+	work->nonce1 = maybe_strdup(swork->nonce1);
 	if (swork->data_lock_p)
 	if (swork->data_lock_p)
 		cg_runlock(swork->data_lock_p);
 		cg_runlock(swork->data_lock_p);
 
 
@@ -11927,10 +12025,10 @@ retry:
 				work = make_clone(pool->last_work_copy);
 				work = make_clone(pool->last_work_copy);
 				mutex_unlock(&pool->last_work_lock);
 				mutex_unlock(&pool->last_work_lock);
 				roll_work(work);
 				roll_work(work);
-				applog(LOG_DEBUG, "Generated work from latest GBT job in get_work_thread with %d seconds left", (int)blkmk_time_left(work->tmpl, tv_now.tv_sec));
+				applog(LOG_DEBUG, "Generated work from latest GBT job in get_work_thread with %d seconds left", (int)blkmk_time_left(work->tr->tmpl, tv_now.tv_sec));
 				stage_work(work);
 				stage_work(work);
 				continue;
 				continue;
-			} else if (last_work->tmpl && pool->proto == PLP_GETBLOCKTEMPLATE && blkmk_work_left(last_work->tmpl) > (unsigned long)mining_threads) {
+			} else if (last_work->tr && pool->proto == PLP_GETBLOCKTEMPLATE && blkmk_work_left(last_work->tr->tmpl) > (unsigned long)mining_threads) {
 				// Don't free last_work_copy, since it is used to detect upstream provides plenty of work per template
 				// Don't free last_work_copy, since it is used to detect upstream provides plenty of work per template
 			} else {
 			} else {
 				free_work(last_work);
 				free_work(last_work);

+ 19 - 5
miner.h

@@ -1126,12 +1126,23 @@ enum pool_protocol {
 	PLP_GETBLOCKTEMPLATE,
 	PLP_GETBLOCKTEMPLATE,
 };
 };
 
 
+struct bfg_tmpl_ref {
+	blktemplate_t *tmpl;
+	int refcount;
+	pthread_mutex_t mutex;
+};
+
 struct stratum_work {
 struct stratum_work {
+	// Used only as a session id for resuming
+	char *nonce1;
+	
+	struct bfg_tmpl_ref *tr;
 	char *job_id;
 	char *job_id;
 	bool clean;
 	bool clean;
 	
 	
 	bytes_t coinbase;
 	bytes_t coinbase;
 	size_t nonce2_offset;
 	size_t nonce2_offset;
+	int n2size;
 	
 	
 	int merkles;
 	int merkles;
 	bytes_t merkle_bin;
 	bytes_t merkle_bin;
@@ -1140,6 +1151,7 @@ struct stratum_work {
 	uint8_t diffbits[4];
 	uint8_t diffbits[4];
 	uint32_t ntime;
 	uint32_t ntime;
 	struct timeval tv_received;
 	struct timeval tv_received;
+	struct timeval tv_expire;
 
 
 	double diff;
 	double diff;
 
 
@@ -1148,6 +1160,9 @@ struct stratum_work {
 	bool opaque;
 	bool opaque;
 	
 	
 	cglock_t *data_lock_p;
 	cglock_t *data_lock_p;
+	
+	struct pool *pool;
+	unsigned char work_restart_id;
 };
 };
 
 
 #define RBUFSIZE 8192
 #define RBUFSIZE 8192
@@ -1238,14 +1253,12 @@ struct pool {
 	char *sockbuf;
 	char *sockbuf;
 	size_t sockbuf_size;
 	size_t sockbuf_size;
 	char *sockaddr_url; /* stripped url used for sockaddr */
 	char *sockaddr_url; /* stripped url used for sockaddr */
-	char *nonce1;
 	size_t n1_len;
 	size_t n1_len;
 	uint32_t nonce2;
 	uint32_t nonce2;
 	int nonce2sz;
 	int nonce2sz;
 #ifdef WORDS_BIGENDIAN
 #ifdef WORDS_BIGENDIAN
 	int nonce2off;
 	int nonce2off;
 #endif
 #endif
-	int n2size;
 	char *sessionid;
 	char *sessionid;
 	bool has_stratum;
 	bool has_stratum;
 	bool stratum_active;
 	bool stratum_active;
@@ -1322,8 +1335,7 @@ struct work {
 	// Allow devices to timestamp work for their own purposes
 	// Allow devices to timestamp work for their own purposes
 	struct timeval	tv_stamp;
 	struct timeval	tv_stamp;
 
 
-	blktemplate_t	*tmpl;
-	int		*tmpl_refcount;
+	struct bfg_tmpl_ref *tr;
 	unsigned int	dataid;
 	unsigned int	dataid;
 	bool		do_foreign_submit;
 	bool		do_foreign_submit;
 
 
@@ -1345,7 +1357,8 @@ extern void get_datestamp(char *, size_t, time_t);
 extern void get_benchmark_work(struct work *);
 extern void get_benchmark_work(struct work *);
 extern void stratum_work_cpy(struct stratum_work *dst, const struct stratum_work *src);
 extern void stratum_work_cpy(struct stratum_work *dst, const struct stratum_work *src);
 extern void stratum_work_clean(struct stratum_work *);
 extern void stratum_work_clean(struct stratum_work *);
-extern void gen_stratum_work2(struct work *, struct stratum_work *, const char *nonce1);
+extern bool pool_has_usable_swork(const struct pool *);
+extern void gen_stratum_work2(struct work *, struct stratum_work *);
 extern void inc_hw_errors3(struct thr_info *thr, const struct work *work, const uint32_t *bad_nonce_p, float nonce_diff);
 extern void inc_hw_errors3(struct thr_info *thr, const struct work *work, const uint32_t *bad_nonce_p, float nonce_diff);
 static inline
 static inline
 void inc_hw_errors2(struct thr_info * const thr, const struct work * const work, const uint32_t *bad_nonce_p)
 void inc_hw_errors2(struct thr_info * const thr, const struct work * const work, const uint32_t *bad_nonce_p)
@@ -1413,6 +1426,7 @@ extern void tq_freeze(struct thread_q *tq);
 extern void tq_thaw(struct thread_q *tq);
 extern void tq_thaw(struct thread_q *tq);
 extern bool successful_connect;
 extern bool successful_connect;
 extern void adl(void);
 extern void adl(void);
+extern void tmpl_decref(struct bfg_tmpl_ref *);
 extern void clean_work(struct work *work);
 extern void clean_work(struct work *work);
 extern void free_work(struct work *work);
 extern void free_work(struct work *work);
 extern void __copy_work(struct work *work, const struct work *base_work);
 extern void __copy_work(struct work *work, const struct work *base_work);

+ 12 - 7
util.c

@@ -1901,6 +1901,11 @@ static bool parse_notify(struct pool *pool, json_t *val)
 	cgtime(&pool->swork.tv_received);
 	cgtime(&pool->swork.tv_received);
 	free(pool->swork.job_id);
 	free(pool->swork.job_id);
 	pool->swork.job_id = job_id;
 	pool->swork.job_id = job_id;
+	if (pool->swork.tr)
+	{
+		tmpl_decref(pool->swork.tr);
+		pool->swork.tr = NULL;
+	}
 	pool->submit_old = !clean;
 	pool->submit_old = !clean;
 	pool->swork.clean = true;
 	pool->swork.clean = true;
 	
 	
@@ -1914,12 +1919,12 @@ static bool parse_notify(struct pool *pool, json_t *val)
 	pool->swork.nonce2_offset = cb1_len + pool->n1_len;
 	pool->swork.nonce2_offset = cb1_len + pool->n1_len;
 	cb2_len = strlen(coinbase2) / 2;
 	cb2_len = strlen(coinbase2) / 2;
 
 
-	bytes_resize(&pool->swork.coinbase, pool->swork.nonce2_offset + pool->n2size + cb2_len);
+	bytes_resize(&pool->swork.coinbase, pool->swork.nonce2_offset + pool->swork.n2size + cb2_len);
 	uint8_t *coinbase = bytes_buf(&pool->swork.coinbase);
 	uint8_t *coinbase = bytes_buf(&pool->swork.coinbase);
 	hex2bin(coinbase, coinbase1, cb1_len);
 	hex2bin(coinbase, coinbase1, cb1_len);
-	hex2bin(&coinbase[cb1_len], pool->nonce1, pool->n1_len);
+	hex2bin(&coinbase[cb1_len], pool->swork.nonce1, pool->n1_len);
 	// NOTE: gap for nonce2, filled at work generation time
 	// NOTE: gap for nonce2, filled at work generation time
-	hex2bin(&coinbase[pool->swork.nonce2_offset + pool->n2size], coinbase2, cb2_len);
+	hex2bin(&coinbase[pool->swork.nonce2_offset + pool->swork.n2size], coinbase2, cb2_len);
 	
 	
 	bytes_resize(&pool->swork.merkle_bin, 32 * merkles);
 	bytes_resize(&pool->swork.merkle_bin, 32 * merkles);
 	for (i = 0; i < merkles; i++)
 	for (i = 0; i < merkles; i++)
@@ -2431,10 +2436,10 @@ resend:
 	cg_wlock(&pool->data_lock);
 	cg_wlock(&pool->data_lock);
 	free(pool->sessionid);
 	free(pool->sessionid);
 	pool->sessionid = sessionid;
 	pool->sessionid = sessionid;
-	free(pool->nonce1);
-	pool->nonce1 = nonce1;
+	free(pool->swork.nonce1);
+	pool->swork.nonce1 = nonce1;
 	pool->n1_len = strlen(nonce1) / 2;
 	pool->n1_len = strlen(nonce1) / 2;
-	pool->n2size = n2size;
+	pool->swork.n2size = n2size;
 	pool->nonce2sz  = (n2size > sizeof(pool->nonce2)) ? sizeof(pool->nonce2) : n2size;
 	pool->nonce2sz  = (n2size > sizeof(pool->nonce2)) ? sizeof(pool->nonce2) : n2size;
 #ifdef WORDS_BIGENDIAN
 #ifdef WORDS_BIGENDIAN
 	pool->nonce2off = (n2size < sizeof(pool->nonce2)) ? (sizeof(pool->nonce2) - n2size) : 0;
 	pool->nonce2off = (n2size < sizeof(pool->nonce2)) ? (sizeof(pool->nonce2) - n2size) : 0;
@@ -2459,7 +2464,7 @@ out:
 		pool->swork.diff = 1;
 		pool->swork.diff = 1;
 		if (opt_protocol) {
 		if (opt_protocol) {
 			applog(LOG_DEBUG, "Pool %d confirmed mining.subscribe with extranonce1 %s extran2size %d",
 			applog(LOG_DEBUG, "Pool %d confirmed mining.subscribe with extranonce1 %s extran2size %d",
-			       pool->pool_no, pool->nonce1, pool->n2size);
+			       pool->pool_no, pool->swork.nonce1, pool->swork.n2size);
 		}
 		}
 	} else {
 	} else {
 		if (recvd)
 		if (recvd)

+ 9 - 0
util.h

@@ -313,6 +313,15 @@ void bytes_cpy(bytes_t *dst, const bytes_t *src)
 	memcpy(dst->buf, src->buf, dst->sz);
 	memcpy(dst->buf, src->buf, dst->sz);
 }
 }
 
 
+static inline
+void bytes_assimilate_raw(bytes_t * const b, void * const buf, const size_t bufsz, const size_t buflen)
+{
+	free(b->buf);
+	b->buf = buf;
+	b->allocsz = bufsz;
+	b->sz = buflen;
+}
+
 static inline
 static inline
 void bytes_shift(bytes_t *b, size_t shift)
 void bytes_shift(bytes_t *b, size_t shift)
 {
 {

+ 117 - 0
work2d.c

@@ -0,0 +1,117 @@
+/*
+ * Copyright 2013-2014 Luke Dashjr
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+#include "config.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "miner.h"
+#include "util.h"
+
+#define MAX_DIVISIONS 255
+
+static bool work2d_reserved[MAX_DIVISIONS + 1] = { true };
+int work2d_xnonce1sz;
+int work2d_xnonce2sz;
+
+void work2d_init()
+{
+	RUNONCE();
+	
+	for (uint64_t n = MAX_DIVISIONS; n; n >>= 8)
+		++work2d_xnonce1sz;
+	work2d_xnonce2sz = 2;
+}
+
+bool reserve_work2d_(uint32_t * const xnonce1_p)
+{
+	uint32_t xnonce1;
+	for (xnonce1 = MAX_DIVISIONS; work2d_reserved[xnonce1]; --xnonce1)
+		if (!xnonce1)
+			return false;
+	work2d_reserved[xnonce1] = true;
+	*xnonce1_p = htole32(xnonce1);
+	return true;
+}
+
+void release_work2d_(uint32_t xnonce1)
+{
+	xnonce1 = le32toh(xnonce1);
+	work2d_reserved[xnonce1] = false;
+}
+
+int work2d_pad_xnonce_size(const struct stratum_work * const swork)
+{
+	return swork->n2size - work2d_xnonce1sz - work2d_xnonce2sz;
+}
+
+void *work2d_pad_xnonce(void * const buf_, const struct stratum_work * const swork, const bool hex)
+{
+	uint8_t * const buf = buf_;
+	int pad = work2d_pad_xnonce_size(swork);
+	if (pad < 0)
+		return NULL;
+	if (hex)
+	{
+		pad *= 2;
+		memset(buf, 'b', pad);
+	}
+	else
+		memset(buf, '\xbb', pad);
+	return &buf[pad];
+}
+
+void work2d_gen_dummy_work(struct work * const work, struct stratum_work * const swork, const struct timeval * const tvp_prepared, const void * const xnonce2, const uint32_t xnonce1)
+{
+	uint8_t *p, *s;
+	
+	*work = (struct work){
+		.pool = swork->pool,
+		.work_restart_id = swork->work_restart_id,
+		.tv_staged = *tvp_prepared,
+	};
+	bytes_resize(&work->nonce2, swork->n2size);
+	s = bytes_buf(&work->nonce2);
+	p = &s[swork->n2size - work2d_xnonce2sz];
+	if (xnonce2)
+		memcpy(p, xnonce2, work2d_xnonce2sz);
+#ifndef __OPTIMIZE__
+	else
+		memset(p, '\0', work2d_xnonce2sz);
+#endif
+	p -= work2d_xnonce1sz;
+	memcpy(p, &xnonce1, work2d_xnonce1sz);
+	work2d_pad_xnonce(s, swork, false);
+	gen_stratum_work2(work, swork);
+}
+
+bool work2d_submit_nonce(struct thr_info * const thr, struct stratum_work * const swork, const struct timeval * const tvp_prepared, const void * const xnonce2, const uint32_t xnonce1, const uint32_t nonce, const uint32_t ntime, bool * const out_is_stale, const float nonce_diff)
+{
+	struct work _work, *work;
+	bool rv;
+	
+	// Generate dummy work
+	work = &_work;
+	work2d_gen_dummy_work(work, swork, tvp_prepared, xnonce2, xnonce1);
+	*(uint32_t *)&work->data[68] = htobe32(ntime);
+	work->nonce_diff = nonce_diff;
+	
+	// Check if it's stale, if desired
+	if (out_is_stale)
+		*out_is_stale = stale_work(work, true);
+	
+	// Submit nonce
+	rv = submit_nonce(thr, work, nonce);
+	
+	clean_work(work);
+	
+	return rv;
+}

+ 19 - 0
work2d.h

@@ -0,0 +1,19 @@
+#ifndef BFG_WORK2D_H
+#define BFG_WORK2D_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+extern int work2d_xnonce1sz;
+extern int work2d_xnonce2sz;
+
+extern void work2d_init();
+extern bool reserve_work2d_(uint32_t *xnonce1_p);
+extern void release_work2d_(uint32_t xnonce1);
+
+extern int work2d_pad_xnonce_size(const struct stratum_work *);
+extern void *work2d_pad_xnonce(void *buf, const struct stratum_work *, bool hex);
+extern void work2d_gen_dummy_work(struct work *, struct stratum_work *, const struct timeval *tvp_prepared, const void *xnonce2, uint32_t xnonce1);
+extern bool work2d_submit_nonce(struct thr_info *, struct stratum_work *, const struct timeval *tvp_prepared, const void *xnonce2, uint32_t xnonce1, uint32_t nonce, uint32_t ntime, bool *out_is_stale, float nonce_diff);
+
+#endif