Browse Source

Merge branch 'multiblockchain' into bfgminer

Luke Dashjr 11 years ago
parent
commit
ffaa34b068
6 changed files with 533 additions and 212 deletions
  1. 18 11
      README
  2. 12 2
      README.RPC
  3. 50 30
      api.c
  4. 383 164
      miner.c
  5. 45 5
      miner.h
  6. 25 0
      util.c

+ 18 - 11
README

@@ -2,9 +2,8 @@ BFGMiner:
 St. Barbara's Faithfully Glorified Mining Initiative Naturally Exceeding Rivals
 or Basically a Freaking Good Miner
 
-This is a multi-threaded multi-pool ASIC, FPGA, GPU and CPU miner with dynamic
-clocking, monitoring, and fanspeed support for bitcoin. Do not use on multiple
-block chains at the same time!
+This is a multi-threaded, multi-blockchain, multi-pool ASIC, FPGA, GPU and CPU
+miner with dynamic clocking, monitoring, and fanspeed support for bitcoin.
 
 This code is provided entirely free of charge by the programmer in his spare
 time so donations would be greatly appreciated. Please consider donating to the
@@ -46,6 +45,10 @@ Multiple pools:
 
 bfgminer -o http://pool1:port -u pool1username -p pool1password -o http://pool2:port -u pool2usernmae -p pool2password
 
+Multiple blockchains:
+
+bfgminer -o http://pool1:port -u pool1username -p pool1password --pool-goal default -o http://pool2:port -u pool2usernmae -p pool2password --pool-goal freicoin
+
 Single pool with a standard http proxy:
 
 bfgminer -o http://pool:port -x http://proxy:port -u username -p password
@@ -289,6 +292,7 @@ Options for both config file and command line:
 --noncelog <arg>    Create log of all nonces found
 --pass|-p <arg>     Password for bitcoin JSON-RPC server
 --per-device-stats  Force verbose mode and output per-device statistics
+--pool-goal <arg>   Named goal for the previous-defined pool
 --pool-priority <arg> Priority for just the previous-defined pool
 --pool-proxy|-x     Proxy URI to use for connecting to just the previous-defined pool
 --protocol-dump|-P  Verbose dump of protocol-level activities
@@ -467,14 +471,16 @@ the range of current share difficulties, whether block notification is working
 work.
 
 The block display shows:
-Block: ...1b89f8d3 #217364  Diff:7.67M (54.93Th/s)  Started: [17:17:22]
+Block #217364: ...1b89f8d3  Diff:7.67M (54.93T)  Started: [17:17:22]  I:12.99mBTC/hr
 
-This shows a short stretch of the current block, the next block's height and
-difficulty (including the network hashrate that difficulty represents), and when
-the search for the new block started.
+This shows a short stretch of the next block's height, the current block,
+difficulty (including the network hashrate that difficulty represents), when the
+search for the new block started, and finally expected Income, calculated by
+actual shares submitted in 100% PPS value (assumes Bitcoin, does not account for
+altcoin conversions!).
 
 The BFGMiner status line shows:
- ST:1  F:0  NB:1  AS:0  BW:[ 75/241 B/s]  E:2.42  I:12.99mBTC/hr  BS:2.71k
+ ST:1  F:0  NB:1  AS:0  BW:[ 75/241 B/s]  E:2.42  BS:2.71k
 
 ST is STaged work items (ready to use).
 F  is network Failure occasions (server down or slow to provide work)
@@ -483,8 +489,6 @@ AS is Active Submissions (shares in the process of submitting)
 BW is BandWidth usage on the network (received/sent)
 E  is Efficiency defined as number of shares accepted (multiplied by their
           difficulty) per 2 KB of bandwidth
-I  is expected Income, calculated by actual shares submitted in 100% PPS value
-          (assumes Bitcoin, does not account for altcoin conversions!)
 BS is the all time Best Share difficulty you've found
 
 The totals line shows the following:
@@ -560,7 +564,10 @@ bitcoind). To use this mode, you need to specify the URL of your bitcoind node
 using the usual pool options (--url, --userpass, etc), and the --coinbase-addr
 option to specify the Bitcoin address you wish to receive the block rewards
 mined. When you run Bitcoin Core on the same computer as your miner, the pool
-itself will be automatically configured for you.
+itself will be automatically configured for you. Please be aware that solo
+mining via GBT is at this time only supported for Bitcoin, and only on for the
+default goal - ie, if you are solo mining, don't use --pool-goal on your Bitcoin
+pool(s).
 
 IMPORTANT: If you are solo mining with more than one instance of BFGMiner (or
 any other software) per payout address, you must also specify data using the

+ 12 - 2
README.RPC

@@ -213,12 +213,12 @@ The list of requests - a (*) means it requires privileged access - and replies:
                               stating the results of enabling pool N
                               The Msg includes the pool URL
 
- addpool|URL,USR,PASS (*)
+ addpool|URL,USR,PASS[,GOAL] (*)
                none           There is no reply section just the STATUS section
                               stating the results of attempting to add pool N
                               The Msg includes the pool URL
                               Use '\\' to get a '\' and '\,' to include a comma
-                              inside URL, USR or PASS
+                              inside URL, USR, PASS, or GOAL
 
  poolpriority|N,... (*)
                none           There is no reply section just the STATUS section
@@ -449,6 +449,16 @@ https://www.npmjs.org/package/miner-rpc
 Feature Changelog for external applications using the API:
 
 
+API V3.3
+
+Modified API command:
+ 'addpool' - accept an additional argument to indicate mining goal by name
+ 'coin' - return multiple elements, when there are multiple mining goals
+          defined; add 'Difficulty Accepted'
+ 'pools' - add 'Mining Goal'
+
+---------
+
 API V3.2 (BFGMiner v4.1.0)
 
 Modified API command:

+ 50 - 30
api.c

@@ -26,6 +26,8 @@
 #include <unistd.h>
 #include <sys/types.h>
 
+#include <uthash.h>
+
 #include "compat.h"
 #include "deviceapi.h"
 #ifdef USE_LIBMICROHTTPD
@@ -170,11 +172,9 @@ static const char ISJSON = '{';
 #define JSON_PGAS	JSON1 _PGAS JSON2
 #define JSON_CPUS	JSON1 _CPUS JSON2
 #define JSON_NOTIFY	JSON1 _NOTIFY JSON2
-#define JSON_DEVDETAILS	JSON1 _DEVDETAILS JSON2
 #define JSON_CLOSE	JSON3
 #define JSON_MINESTATS	JSON1 _MINESTATS JSON2
 #define JSON_CHECK	JSON1 _CHECK JSON2
-#define JSON_MINECOIN	JSON1 _MINECOIN JSON2
 #define JSON_DEBUGSET	JSON1 _DEBUGSET JSON2
 #define JSON_SETCONFIG	JSON1 _SETCONFIG JSON2
 #define JSON_END	JSON4 JSON5
@@ -1934,6 +1934,7 @@ static void poolstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __m
 		root = api_add_string(root, "Status", status, false);
 		root = api_add_int(root, "Priority", &(pool->prio), false);
 		root = api_add_int(root, "Quota", &pool->quota, false);
+		root = api_add_string(root, "Mining Goal", pool->goal->name, false);
 		root = api_add_string(root, "Long Poll", lp, false);
 		root = api_add_uint(root, "Getworks", &(pool->getwork_requested), false);
 		root = api_add_int(root, "Accepted", &(pool->accepted), false);
@@ -2365,7 +2366,7 @@ static void copyadvanceafter(char ch, char **param, char **buf)
 	*(dst_b++) = '\0';
 }
 
-static bool pooldetails(char *param, char **url, char **user, char **pass)
+static bool pooldetails(char *param, char **url, char **user, char **pass, char **goalname)
 {
 	char *ptr, *buf;
 
@@ -2393,6 +2394,12 @@ static bool pooldetails(char *param, char **url, char **user, char **pass)
 
 	// copy pass
 	copyadvanceafter(',', &param, &buf);
+	
+	if (*param)
+		*goalname = buf;
+	
+	// copy goalname
+	copyadvanceafter(',', &param, &buf);
 
 	return true;
 
@@ -2403,7 +2410,7 @@ exitsama:
 
 static void addpool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 {
-	char *url, *user, *pass;
+	char *url, *user, *pass, *goalname = "default";
 	struct pool *pool;
 	char *ptr;
 
@@ -2412,7 +2419,8 @@ static void addpool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *
 		return;
 	}
 
-	if (!pooldetails(param, &url, &user, &pass)) {
+	if (!pooldetails(param, &url, &user, &pass, &goalname))
+	{
 		ptr = escape_string(param, isjson);
 		message(io_data, MSG_INVPDP, 0, ptr, isjson);
 		if (ptr != param)
@@ -2421,7 +2429,8 @@ static void addpool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *
 		return;
 	}
 
-	pool = add_pool();
+	struct mining_goal_info * const goal = get_mining_goal(goalname);
+	pool = add_pool2(goal);
 	detect_stratum(pool, url);
 	add_pool_details(pool, true, url, user, pass);
 
@@ -3062,36 +3071,47 @@ static void minecoin(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __may
 {
 	struct api_data *root = NULL;
 	char buf[TMPBUFSIZ];
-	bool io_open;
 
 	message(io_data, MSG_MINECOIN, 0, NULL, isjson);
-	io_open = io_add(io_data, isjson ? COMSTR JSON_MINECOIN : _MINECOIN COMSTR);
 
+	struct mining_goal_info *goal, *tmpgoal;
+	bool precom = false;
+	HASH_ITER(hh, mining_goals, goal, tmpgoal)
+	{
+		if (goal->is_default)
+			io_add(io_data, isjson ? COMSTR JSON1 _MINECOIN JSON2 : _MINECOIN COMSTR);
+		else
+		{
+			sprintf(buf, isjson ? COMSTR JSON1 _MINECOIN "%u" JSON2 : _MINECOIN "%u" COMSTR, goal->id);
+			io_add(io_data, buf);
+		}
+		
 #ifdef USE_SCRYPT
-	if (opt_scrypt)
-		root = api_add_const(root, "Hash Method", SCRYPTSTR, false);
-	else
+		if (opt_scrypt)
+			root = api_add_const(root, "Hash Method", SCRYPTSTR, false);
+		else
 #endif
-		root = api_add_const(root, "Hash Method", SHA256STR, false);
-
-	cg_rlock(&ch_lock);
-	if (current_fullhash && *current_fullhash) {
-		root = api_add_time(root, "Current Block Time", &block_time, true);
-		root = api_add_string(root, "Current Block Hash", current_fullhash, true);
-	} else {
-		time_t t = 0;
-		root = api_add_time(root, "Current Block Time", &t, true);
-		root = api_add_const(root, "Current Block Hash", BLANK, false);
+			root = api_add_const(root, "Hash Method", SHA256STR, false);
+
+		cg_rlock(&ch_lock);
+		struct blockchain_info * const blkchain = goal->blkchain;
+		struct block_info * const blkinfo = blkchain->currentblk;
+		root = api_add_time(root, "Current Block Time", &blkinfo->first_seen_time, true);
+		char fullhash[(sizeof(blkinfo->prevblkhash) * 2) + 1];
+		blkhashstr(fullhash, blkinfo->prevblkhash);
+		root = api_add_string(root, "Current Block Hash", fullhash, true);
+		cg_runlock(&ch_lock);
+
+		root = api_add_bool(root, "LP", &goal->have_longpoll, false);
+		root = api_add_diff(root, "Network Difficulty", &goal->current_diff, true);
+		
+		root = api_add_diff(root, "Difficulty Accepted", &goal->diff_accepted, false);
+		
+		root = print_data(root, buf, isjson, precom);
+		io_add(io_data, buf);
+		if (isjson)
+			io_add(io_data, JSON_CLOSE);
 	}
-	cg_runlock(&ch_lock);
-
-	root = api_add_bool(root, "LP", &have_longpoll, false);
-	root = api_add_diff(root, "Network Difficulty", &current_diff, true);
-
-	root = print_data(root, buf, isjson, false);
-	io_add(io_data, buf);
-	if (isjson && io_open)
-		io_close(io_data);
 }
 
 static void debugstate(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)

+ 383 - 164
miner.c

@@ -147,7 +147,6 @@ char *request_target_str;
 float request_pdiff = 1.0;
 double request_bdiff;
 static bool want_stratum = true;
-bool have_longpoll;
 int opt_skip_checks;
 bool want_per_device_stats;
 bool use_syslog;
@@ -358,40 +357,24 @@ const char unicode_micro = 'u';
 #define U8_BAD_END   "\xef\x80\x80"
 #define AS_BAD(x) U8_BAD_START x U8_BAD_END
 
+/* logstart is where the log window should start */
+static int devcursor, logstart, logcursor;
+
 bool selecting_device;
 unsigned selected_device;
 #endif
 
 static int max_lpdigits;
 
-static char current_block[40];
-
-/* Protected by ch_lock */
-static char *current_hash;
-static uint32_t current_block_id;
-char *current_fullhash;
+// current_hash was replaced with goal->current_goal_detail
+// current_block_id was replaced with blkchain->currentblk->block_id
 
 static char datestamp[40];
-static char blocktime[32];
-time_t block_time;
 static char best_share[ALLOC_H2B_SHORTV] = "0";
-double current_diff = 0xFFFFFFFFFFFFFFFFULL;
-static char block_diff[ALLOC_H2B_SHORTV];
-static char net_hashrate[ALLOC_H2B_SHORT];
 double best_diff = 0;
 
-static bool known_blkheight_current;
-static uint32_t known_blkheight;
-static uint32_t known_blkheight_blkid;
-static uint64_t block_subsidy;
-
-struct block {
-	char hash[40];
-	UT_hash_handle hh;
-	int block_no;
-};
-
-static struct block *blocks = NULL;
+struct mining_goal_info *mining_goals;
+int active_goals = 1;
 
 
 int swork_id;
@@ -1008,6 +991,75 @@ static void sharelog(const char*disposition, const struct work*work)
 		applog(LOG_ERR, "sharelog fwrite error");
 }
 
+#ifdef HAVE_CURSES
+static void switch_logsize(void);
+#endif
+
+static
+int mining_goals_name_cmp(const struct mining_goal_info * const a, const struct mining_goal_info * const b)
+{
+	// default always goes first
+	if (a->is_default)
+		return -1;
+	if (b->is_default)
+		return 1;
+	return strcmp(a->name, b->name);
+}
+
+static
+void blkchain_init_block(struct blockchain_info * const blkchain)
+{
+	struct block_info * const dummy_block = calloc(sizeof(*dummy_block), 1);
+	memset(dummy_block->prevblkhash, 0, 0x20);
+	HASH_ADD(hh, blkchain->blocks, prevblkhash, sizeof(dummy_block->prevblkhash), dummy_block);
+	blkchain->currentblk = dummy_block;
+}
+
+struct mining_goal_info *get_mining_goal(const char * const name)
+{
+	static unsigned next_goal_id;
+	struct mining_goal_info *goal;
+	HASH_FIND_STR(mining_goals, name, goal);
+	if (!goal)
+	{
+		struct blockchain_info * const blkchain = malloc(sizeof(*blkchain) + sizeof(*goal));
+		goal = (void*)(&blkchain[1]);
+		
+		*blkchain = (struct blockchain_info){
+			.currentblk = NULL,
+		};
+		blkchain_init_block(blkchain);
+		
+		*goal = (struct mining_goal_info){
+			.id = next_goal_id++,
+			.name = strdup(name),
+			.is_default = !strcmp(name, "default"),
+			.blkchain = blkchain,
+			.current_diff = 0xFFFFFFFFFFFFFFFFULL,
+		};
+		HASH_ADD_STR(mining_goals, name, goal);
+		HASH_SORT(mining_goals, mining_goals_name_cmp);
+		
+#ifdef HAVE_CURSES
+		devcursor = 7 + active_goals;
+		switch_logsize();
+#endif
+	}
+	return goal;
+}
+
+void mining_goal_reset(struct mining_goal_info * const goal)
+{
+	struct blockchain_info * const blkchain = goal->blkchain;
+	struct block_info *blkinfo, *tmpblkinfo;
+	HASH_ITER(hh, blkchain->blocks, blkinfo, tmpblkinfo)
+	{
+		HASH_DEL(blkchain->blocks, blkinfo);
+		free(blkinfo);
+	}
+	blkchain_init_block(blkchain);
+}
+
 static char *getwork_req = "{\"method\": \"getwork\", \"params\": [], \"id\":0}\n";
 
 /* Adjust all the pools' quota to the greatest common denominator after a pool
@@ -1052,7 +1104,7 @@ void adjust_quota_gcd(void)
 }
 
 /* Return value is ignored if not called from add_pool_details */
-struct pool *add_pool(void)
+struct pool *add_pool2(struct mining_goal_info * const goal)
 {
 	struct pool *pool;
 
@@ -1069,6 +1121,7 @@ struct pool *add_pool(void)
 	mutex_init(&pool->stratum_lock);
 	timer_unset(&pool->swork.tv_transparency);
 	pool->swork.pool = pool;
+	pool->goal = goal;
 
 	pool->idle = true;
 	/* Make sure the pool doesn't think we've been idle since time 0 */
@@ -1675,6 +1728,20 @@ static char *set_cbcperc(const char *arg)
 	return NULL;
 }
 
+static
+char *set_pool_goal(const char * const arg)
+{
+	struct pool *pool;
+	
+	if (!total_pools)
+		return "Usage of --pool-goal before pools are defined does not make sense";
+	
+	pool = pools[total_pools - 1];
+	pool->goal = get_mining_goal(arg);
+	
+	return NULL;
+}
+
 static char *set_pool_priority(const char *arg)
 {
 	struct pool *pool;
@@ -2365,6 +2432,9 @@ static struct opt_table opt_config_table[] = {
 	OPT_WITH_ARG("--userpass|-O",
 	             set_userpass, NULL, NULL,
 	             "Username:Password pair for bitcoin JSON-RPC server"),
+	OPT_WITH_ARG("--pool-goal",
+			 set_pool_goal, NULL, NULL,
+			 "Named goal for the previous-defined pool"),
 	OPT_WITH_ARG("--pool-priority",
 			 set_pool_priority, NULL, NULL,
 			 "Priority for just the previous-defined pool"),
@@ -2894,44 +2964,63 @@ void free_work(struct work *work)
 const char *bfg_workpadding_bin = "\0\0\0\x80\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x80\x02\0\0";
 #define workpadding_bin  bfg_workpadding_bin
 
-// Must only be called with ch_lock held!
+static const size_t block_info_str_sz = 3 /* ... */ + 16 /* block hash segment */ + 1;
+
 static
-void __update_block_title(const unsigned char *hash_swap)
-{
-	if (hash_swap) {
-		char tmp[17];
-		// Only provided when the block has actually changed
-		free(current_hash);
-		current_hash = malloc(3 /* ... */ + 16 /* block hash segment */ + 1);
-		bin2hex(tmp, &hash_swap[24], 8);
-		memset(current_hash, '.', 3);
-		memcpy(&current_hash[3], tmp, 17);
-		known_blkheight_current = false;
-	} else if (likely(known_blkheight_current)) {
-		return;
-	}
-	if (current_block_id == known_blkheight_blkid) {
-		// FIXME: The block number will overflow this sometime around AD 2025-2027
-		if (known_blkheight < 1000000) {
-			memmove(&current_hash[3], &current_hash[11], 8);
-			snprintf(&current_hash[11], 20-11, " #%6u", known_blkheight);
-		}
-		known_blkheight_current = true;
+void block_info_str(char * const out, const struct block_info * const blkinfo)
+{
+	unsigned char hash_swap[32];
+	swap256(hash_swap, blkinfo->prevblkhash);
+	swap32tole(hash_swap, hash_swap, 32 / 4);
+	
+	memset(out, '.', 3);
+	// FIXME: The block number will overflow this sometime around AD 2025-2027
+	if (blkinfo->height > 0 && blkinfo->height < 1000000)
+	{
+		bin2hex(&out[3], &hash_swap[0x1c], 4);
+		snprintf(&out[11], block_info_str_sz-11, " #%6u", blkinfo->height);
 	}
+	else
+		bin2hex(&out[3], &hash_swap[0x18], 8);
+}
+
+#ifdef HAVE_CURSES
+static void update_block_display(void);
+#endif
+
+// Must only be called with ch_lock held!
+static
+void __update_block_title(struct mining_goal_info * const goal)
+{
+	struct blockchain_info * const blkchain = goal->blkchain;
+	
+	if (!goal->current_goal_detail)
+		goal->current_goal_detail = malloc(block_info_str_sz);
+	block_info_str(goal->current_goal_detail, blkchain->currentblk);
+#ifdef HAVE_CURSES
+	update_block_display();
+#endif
 }
 
+static struct block_info *block_exists(const struct blockchain_info *, const void *);
+
 static
-void have_block_height(uint32_t block_id, uint32_t blkheight)
+void have_block_height(struct mining_goal_info * const goal, const void * const prevblkhash, uint32_t blkheight)
 {
-	if (known_blkheight == blkheight)
+	struct blockchain_info * const blkchain = goal->blkchain;
+	struct block_info * const blkinfo = block_exists(blkchain, prevblkhash);
+	if ((!blkinfo) || blkinfo->height)
 		return;
+	
+	uint32_t block_id = ((uint32_t*)prevblkhash)[0];
 	applog(LOG_DEBUG, "Learned that block id %08" PRIx32 " is height %" PRIu32, (uint32_t)be32toh(block_id), blkheight);
 	cg_wlock(&ch_lock);
-	known_blkheight = blkheight;
-	known_blkheight_blkid = block_id;
-	block_subsidy = 5000000000LL >> (blkheight / 210000);
-	if (block_id == current_block_id)
-		__update_block_title(NULL);
+	blkinfo->height = blkheight;
+	if (blkinfo == blkchain->currentblk)
+	{
+		blkchain->currentblk_subsidy = 5000000000LL >> (blkheight / 210000);
+		__update_block_title(goal);
+	}
 	cg_wunlock(&ch_lock);
 }
 
@@ -3025,6 +3114,9 @@ int work_ntime_range(struct work * const work, const struct timeval * const tvp_
 static
 void refresh_bitcoind_address(const bool fresh)
 {
+	struct mining_goal_info * const goal = get_mining_goal("default");
+	struct blockchain_info * const blkchain = goal->blkchain;
+	
 	if (!have_at_least_one_getcbaddr)
 		return;
 	
@@ -3041,6 +3133,9 @@ void refresh_bitcoind_address(const bool fresh)
 		struct pool * const pool = pools[i];
 		if (!uri_get_param_bool(pool->rpc_url, "getcbaddr", false))
 			continue;
+		if (pool->goal != goal)
+			// TODO: Multi-blockchain support
+			continue;
 		
 		applog(LOG_DEBUG, "Refreshing coinbase address from pool %d", pool->pool_no);
 		if (!curl)
@@ -3087,7 +3182,7 @@ void refresh_bitcoind_address(const bool fresh)
 			break;
 		}
 		bytes_assimilate(&opt_coinbase_script, &newscript);
-		coinbase_script_block_id = current_block_id;
+		coinbase_script_block_id = blkchain->currentblk->block_id;
 		applog(LOG_NOTICE, "Now using coinbase address %s, provided by pool %d", s, pool->pool_no);
 		break;
 	}
@@ -3134,7 +3229,7 @@ static bool work_decode(struct pool *pool, struct work *work, json_t *val)
 		const uint32_t tmpl_block_id = ((uint32_t*)tmpl->prevblk)[0];
 		if ((!tmpl->cbtxn) && coinbase_script_block_id != tmpl_block_id)
 			refresh_bitcoind_address(false);
-		if (bytes_len(&opt_coinbase_script))
+		if (bytes_len(&opt_coinbase_script) && get_mining_goal("default") == pool->goal)
 		{
 			bool newcb;
 #if BLKMAKER_VERSION > 2
@@ -3270,9 +3365,10 @@ static bool work_decode(struct pool *pool, struct work *work, json_t *val)
 	}
 
 	if ( (tmp_val = json_object_get(res_val, "height")) ) {
+		struct mining_goal_info * const goal = pool->goal;
 		uint32_t blkheight = json_number_value(tmp_val);
-		uint32_t block_id = ((uint32_t*)work->data)[1];
-		have_block_height(block_id, blkheight);
+		const void * const prevblkhash = &work->data[4];
+		have_block_height(goal, prevblkhash, blkheight);
 	}
 
 	memset(work->hash, 0, sizeof(work->hash));
@@ -3373,8 +3469,6 @@ static int total_staged(void)
 WINDOW *mainwin, *statuswin, *logwin;
 #endif
 double total_secs = 1.0;
-/* logstart is where the log window should start */
-static int devcursor, logstart, logcursor;
 #ifdef HAVE_CURSES
 static char statusline[256];
 /* statusy is where the status window goes up to in cases where it won't fit at startup */
@@ -4177,6 +4271,116 @@ static int menu_attr = A_REVERSE;
 	bfg_waddstr(win, tmp42); \
 } while (0)
 
+static
+void update_block_display_line(const int blky, struct mining_goal_info *goal)
+{
+	struct blockchain_info * const blkchain = goal->blkchain;
+	struct block_info * const blkinfo = blkchain->currentblk;
+	double income;
+	char incomestr[ALLOC_H2B_SHORT+6+1];
+	
+	if (blkinfo->height)
+	{
+		income = goal->diff_accepted * 3600 * blkchain->currentblk_subsidy / total_secs / goal->current_diff;
+		format_unit3(incomestr, sizeof(incomestr), FUP_BTC, "BTC/hr", H2B_SHORT, income/1e8, -1);
+	}
+	else
+		strcpy(incomestr, "?");
+	
+	int linelen = bfg_win_linelen(statuswin);
+	wmove(statuswin, blky, 0);
+	
+	bfg_waddstr(statuswin, " Block");
+	if (!goal->is_default)
+		linelen -= strlen(goal->name) + 1;
+	linelen -= 6;  // " Block"
+	
+	if (blkinfo->height && blkinfo->height < 1000000)
+	{
+		cg_wprintw(statuswin, " #%6u", blkinfo->height);
+		linelen -= 8;
+	}
+	bfg_waddstr(statuswin, ":");
+	
+	if (linelen > 55)
+		bfg_waddstr(statuswin, " ");
+	if (linelen >= 65)
+		bfg_waddstr(statuswin, "...");
+	
+	{
+		char hexpbh[0x11];
+		if (!(blkinfo->height && blkinfo->height < 1000000))
+		{
+			bin2hex(hexpbh, &blkinfo->prevblkhash[4], 4);
+			bfg_waddstr(statuswin, hexpbh);
+		}
+		bin2hex(hexpbh, &blkinfo->prevblkhash[0], 4);
+		bfg_waddstr(statuswin, hexpbh);
+	}
+	
+	if (linelen >= 55)
+		bfg_waddstr(statuswin, " ");
+	
+	cg_wprintw(statuswin, " Diff:%s", goal->current_diff_str);
+	
+	if (linelen >= 69)
+		bfg_waddstr(statuswin, " ");
+	
+	cg_wprintw(statuswin, "(%s) ", goal->net_hashrate);
+	
+	if (linelen >= 62)
+	{
+		if (linelen >= 69)
+			bfg_waddstr(statuswin, " ");
+		bfg_waddstr(statuswin, "Started:");
+	}
+	else
+		bfg_waddstr(statuswin, "S:");
+	if (linelen >= 69)
+		bfg_waddstr(statuswin, " ");
+	
+	bfg_waddstr(statuswin, blkchain->currentblk_first_seen_time_str);
+	
+	if (linelen >= 69)
+		bfg_waddstr(statuswin, " ");
+	
+	cg_wprintw(statuswin, " I:%s", incomestr);
+	
+	if (!goal->is_default)
+		cg_wprintw(statuswin, " %s", goal->name);
+	
+	wclrtoeol(statuswin);
+}
+
+static bool pool_actively_in_use(const struct pool *, const struct pool *);
+
+static
+void update_block_display(void)
+{
+	struct mining_goal_info *goal, *tmpgoal;
+	int blky = 3, i, total_found_goals = 0;
+	HASH_ITER(hh, mining_goals, goal, tmpgoal)
+	{
+		for (i = 0; i < total_pools; ++i)
+		{
+			struct pool * const pool = pools[i];
+			if (pool->goal == goal && pool_actively_in_use(pool, NULL))
+				break;
+		}
+		if (i >= total_pools)
+			// no pools using this goal, so it's probably stale anyway
+			continue;
+		update_block_display_line(blky++, goal);
+		++total_found_goals;
+	}
+	if (total_found_goals != active_goals)
+	{
+		active_goals = total_found_goals;
+		devcursor = 7 + active_goals;
+		switch_logsize();
+	}
+}
+
 static bool pool_unworkable(const struct pool *);
 
 /* Must be called with curses mutex lock held and curses_active */
@@ -4185,7 +4389,6 @@ static void curses_print_status(const int ts)
 	struct pool *pool = currentpool;
 	struct timeval now, tv;
 	float efficiency;
-	double income;
 	int logdiv;
 
 	efficiency = total_bytes_xfer ? total_diff_accepted * 2048. / total_bytes_xfer : 0.0;
@@ -4283,7 +4486,7 @@ static void curses_print_status(const int ts)
 		             lowdiff_pool->diff,
 		             (lowdiff == highdiff) ? "" : "-",
 		             (lowdiff == highdiff) ? "" : highdiff_pool->diff,
-		             have_longpoll ? '+' : '-',
+		             pool->goal->have_longpoll ? '+' : '-',
 		             oldest_work_restart_pool->work_restart_timestamp);
 	}
 	else
@@ -4309,18 +4512,15 @@ one_workable_pool: ;
 		}
 		cg_mvwprintw(statuswin, 2, 0, " Pool%2u: %s  Diff:%s  %c%s  LU:%s  User:%s",
 		             pool->pool_no, pooladdr, pool->diff,
-		             have_longpoll ? '+' : '-', pool_proto_str(pool),
+		             pool->goal->have_longpoll ? '+' : '-', pool_proto_str(pool),
 		             pool->work_restart_timestamp,
 		             pool->rpc_user);
 	}
 	wclrtoeol(statuswin);
-	cg_mvwprintw(statuswin, 3, 0, " Block: %s  Diff:%s (%s)  Started: %s",
-		  current_hash, block_diff, net_hashrate, blocktime);
 	
-	income = total_diff_accepted * 3600 * block_subsidy / total_secs / current_diff;
-	char bwstr[(ALLOC_H2B_SHORT*2)+3+1], incomestr[ALLOC_H2B_SHORT+6+1];
-	format_unit3(incomestr, sizeof(incomestr), FUP_BTC, "BTC/hr", H2B_SHORT, income/1e8, -1);
-	cg_mvwprintw(statuswin, 4, 0, " ST:%d  F:%d  NB:%d  AS:%d  BW:[%s]  E:%.2f  I:%s  BS:%s",
+	char bwstr[(ALLOC_H2B_SHORT*2)+3+1];
+	
+	cg_mvwprintw(statuswin, devcursor - 4, 0, " ST:%d  F:%d  NB:%d  AS:%d  BW:[%s]  E:%.2f  BS:%s",
 		ts,
 		total_go + total_ro,
 		new_blocks,
@@ -4330,16 +4530,16 @@ one_workable_pool: ;
 		                  (float)(total_bytes_rcvd / total_secs),
 		                  (float)(total_bytes_sent / total_secs)),
 		efficiency,
-		incomestr,
 		best_share);
 	wclrtoeol(statuswin);
 	
-	mvwaddstr(statuswin, 5, 0, " ");
+	mvwaddstr(statuswin, devcursor - 3, 0, " ");
 	bfg_waddstr(statuswin, statusline);
 	wclrtoeol(statuswin);
 	
+	int devdiv = devcursor - 2;
 	logdiv = statusy - 1;
-	bfg_hline(statuswin, 6);
+	bfg_hline(statuswin, devdiv);
 	bfg_hline(statuswin, logdiv);
 #ifdef USE_UNICODE
 	if (use_unicode)
@@ -4349,10 +4549,10 @@ one_workable_pool: ;
 			offset += max_lpdigits;  // proc letter(s)
 		if (have_unicode_degrees)
 			++offset;  // degrees symbol
-		mvwadd_wch(statuswin, 6, offset, WACS_PLUS);
+		mvwadd_wch(statuswin, devdiv, offset, WACS_PLUS);
 		mvwadd_wch(statuswin, logdiv, offset, WACS_BTEE);
 		offset += 24;  // hashrates etc
-		mvwadd_wch(statuswin, 6, offset, WACS_PLUS);
+		mvwadd_wch(statuswin, devdiv, offset, WACS_PLUS);
 		mvwadd_wch(statuswin, logdiv, offset, WACS_BTEE);
 	}
 #endif
@@ -4517,6 +4717,7 @@ static void switch_logsize(void)
 		unlock_curses();
 	}
 	check_winsizes();
+	update_block_display();
 }
 
 /* For mandatory printing when mutex is already locked */
@@ -4744,6 +4945,8 @@ share_result(json_t *val, json_t *res, json_t *err, const struct work *work,
 	cgpu = get_thr_cgpu(work->thr_id);
 
 	if ((json_is_null(err) || !err) && (json_is_null(res) || json_is_true(res))) {
+		struct mining_goal_info * const goal = pool->goal;
+		
 		mutex_lock(&stats_lock);
 		cgpu->accepted++;
 		total_accepted++;
@@ -4751,6 +4954,7 @@ share_result(json_t *val, json_t *res, json_t *err, const struct work *work,
 		cgpu->diff_accepted += work->work_difficulty;
 		total_diff_accepted += work->work_difficulty;
 		pool->diff_accepted += work->work_difficulty;
+		goal->diff_accepted += work->work_difficulty;
 		mutex_unlock(&stats_lock);
 
 		pool->seq_rejects = 0;
@@ -5408,7 +5612,7 @@ erradd:
 	json_decref(n);
 }
 
-static char *prepare_rpc_req2(struct work *work, enum pool_protocol proto, const char *lpid, bool probe)
+static char *prepare_rpc_req2(struct work *work, enum pool_protocol proto, const char *lpid, bool probe, struct pool * const pool)
 {
 	char *rpc_req;
 
@@ -5428,7 +5632,7 @@ static char *prepare_rpc_req2(struct work *work, enum pool_protocol proto, const
 				goto gbtfail;
 			caps |= GBT_LONGPOLL;
 #if BLKMAKER_VERSION > 1
-			if (bytes_len(&opt_coinbase_script) || have_at_least_one_getcbaddr)
+			if ((bytes_len(&opt_coinbase_script) || have_at_least_one_getcbaddr) && get_mining_goal("default") == pool->goal)
 				caps |= GBT_CBVALUE;
 #endif
 			json_t *req = blktmpl_request_jansson(caps, lpid);
@@ -5455,8 +5659,8 @@ gbtfail2:
 	return NULL;
 }
 
-#define prepare_rpc_req(work, proto, lpid)  prepare_rpc_req2(work, proto, lpid, false)
-#define prepare_rpc_req_probe(work, proto, lpid)  prepare_rpc_req2(work, proto, lpid, true)
+#define prepare_rpc_req(work, proto, lpid, pool)  prepare_rpc_req2(work, proto, lpid, false, pool)
+#define prepare_rpc_req_probe(work, proto, lpid, pool)  prepare_rpc_req2(work, proto, lpid, true, pool)
 
 static const char *pool_protocol_name(enum pool_protocol proto)
 {
@@ -5497,7 +5701,7 @@ static bool get_upstream_work(struct work *work, CURL *curl)
 		pool->proto = PLP_GETBLOCKTEMPLATE;
 
 tryagain:
-	rpc_req = prepare_rpc_req(work, pool->proto, NULL);
+	rpc_req = prepare_rpc_req(work, pool->proto, NULL, pool);
 	work->pool = pool;
 	if (!rpc_req)
 		return false;
@@ -5986,6 +6190,8 @@ bool stale_work(struct work *work, bool share)
 
 	block_id = ((uint32_t*)work->data)[1];
 	pool = work->pool;
+	struct mining_goal_info * const goal = pool->goal;
+	struct blockchain_info * const blkchain = goal->blkchain;
 
 	/* Technically the rolltime should be correct but some pools
 	 * advertise a broken expire= that is lower than a meaningful
@@ -5995,7 +6201,7 @@ bool stale_work(struct work *work, bool share)
 	else
 		work_expiry = opt_expiry;
 
-	unsigned max_expiry = (have_longpoll ? opt_expiry_lp : opt_expiry);
+	unsigned max_expiry = (goal->have_longpoll ? opt_expiry_lp : opt_expiry);
 	if (work_expiry > max_expiry)
 		work_expiry = max_expiry;
 
@@ -6020,13 +6226,13 @@ bool stale_work(struct work *work, bool share)
 		if (enabled_pools <= 1 || opt_fail_only) {
 			if (pool->block_id && block_id != pool->block_id)
 			{
-				applog(LOG_DEBUG, "Work stale due to block mismatch (%08lx != 1 ? %08lx : %08lx)", (long)block_id, (long)pool->block_id, (long)current_block_id);
+				applog(LOG_DEBUG, "Work stale due to block mismatch (%08lx != 1 ? %08lx : %08lx)", (long)block_id, (long)pool->block_id, (long)blkchain->currentblk->block_id);
 				return true;
 			}
 		} else {
-			if (block_id != current_block_id)
+			if (block_id != blkchain->currentblk->block_id)
 			{
-				applog(LOG_DEBUG, "Work stale due to block mismatch (%08lx != 0 ? %08lx : %08lx)", (long)block_id, (long)pool->block_id, (long)current_block_id);
+				applog(LOG_DEBUG, "Work stale due to block mismatch (%08lx != 0 ? %08lx : %08lx)", (long)block_id, (long)pool->block_id, (long)blkchain->currentblk->block_id);
 				return true;
 			}
 		}
@@ -6112,8 +6318,11 @@ double share_diff(const struct work *work)
 static
 void work_check_for_block(struct work * const work)
 {
+	struct pool * const pool = work->pool;
+	struct mining_goal_info * const goal = pool->goal;
+	
 	work->share_diff = share_diff(work);
-	if (unlikely(work->share_diff >= current_diff)) {
+	if (unlikely(work->share_diff >= goal->current_diff)) {
 		work->block = true;
 		work->pool->solved++;
 		found_blocks++;
@@ -6729,6 +6938,9 @@ void switch_pools(struct pool *selected)
 	pthread_cond_broadcast(&lp_cond);
 	mutex_unlock(&lp_lock);
 
+#ifdef HAVE_CURSES
+	update_block_display();
+#endif
 }
 
 static void discard_work(struct work *work)
@@ -6830,7 +7042,6 @@ static void restart_threads(void)
 	rd_unlock(&mining_thr_lock);
 }
 
-static
 void blkhashstr(char *rv, const unsigned char *hash)
 {
 	unsigned char hash_swap[32];
@@ -6840,60 +7051,42 @@ void blkhashstr(char *rv, const unsigned char *hash)
 	bin2hex(rv, hash_swap, 32);
 }
 
-static void set_curblock(char *hexstr, unsigned char *hash)
+static
+void set_curblock(struct mining_goal_info * const goal, struct block_info * const blkinfo)
 {
-	unsigned char hash_swap[32];
+	struct blockchain_info * const blkchain = goal->blkchain;
 
-	current_block_id = ((uint32_t*)hash)[0];
-	strcpy(current_block, hexstr);
-	swap256(hash_swap, hash);
-	swap32tole(hash_swap, hash_swap, 32 / 4);
+	blkchain->currentblk = blkinfo;
+	blkchain->currentblk_subsidy = 5000000000LL >> (blkinfo->height / 210000);
 
 	cg_wlock(&ch_lock);
-	block_time = time(NULL);
-	__update_block_title(hash_swap);
-	free(current_fullhash);
-	current_fullhash = malloc(65);
-	bin2hex(current_fullhash, hash_swap, 32);
-	get_timestamp(blocktime, sizeof(blocktime), block_time);
+	__update_block_title(goal);
+	get_timestamp(blkchain->currentblk_first_seen_time_str, sizeof(blkchain->currentblk_first_seen_time_str), blkinfo->first_seen_time);
 	cg_wunlock(&ch_lock);
 
-	applog(LOG_INFO, "New block: %s diff %s (%s)", current_hash, block_diff, net_hashrate);
+	applog(LOG_INFO, "New block: %s diff %s (%s)", goal->current_goal_detail, goal->current_diff_str, goal->net_hashrate);
 }
 
-/* Search to see if this string is from a block that has been seen before */
-static bool block_exists(char *hexstr)
+/* Search to see if this prevblkhash has been seen before */
+static
+struct block_info *block_exists(const struct blockchain_info * const blkchain, const void * const prevblkhash)
 {
-	struct block *s;
+	struct block_info *s;
 
 	rd_lock(&blk_lock);
-	HASH_FIND_STR(blocks, hexstr, s);
+	HASH_FIND(hh, blkchain->blocks, prevblkhash, 0x20, s);
 	rd_unlock(&blk_lock);
 
-	if (s)
-		return true;
-	return false;
-}
-
-#if 0
-/* Tests if this work is from a block that has been seen before */
-static inline bool from_existing_block(struct work *work)
-{
-	char hexstr[37];
-	bool ret;
-
-	bin2hex(hexstr, work->data + 8, 18);
-	ret = block_exists(hexstr);
-	return ret;
+	return s;
 }
-#endif
 
-static int block_sort(struct block *blocka, struct block *blockb)
+static int block_sort(struct block_info * const blocka, struct block_info * const blockb)
 {
-	return blocka->block_no - blockb->block_no;
+	return blocka->block_seen_order - blockb->block_seen_order;
 }
 
-static void set_blockdiff(const struct work *work)
+static
+void set_blockdiff(struct mining_goal_info * const goal, const struct work * const work)
 {
 	unsigned char target[32];
 	double diff;
@@ -6903,60 +7096,72 @@ static void set_blockdiff(const struct work *work)
 	diff = target_diff(target);
 	diff64 = diff;
 
-	suffix_string(diff64, block_diff, sizeof(block_diff), 0);
-	format_unit2(net_hashrate, sizeof(net_hashrate),
+	suffix_string(diff64, goal->current_diff_str, sizeof(goal->current_diff_str), 0);
+	format_unit2(goal->net_hashrate, sizeof(goal->net_hashrate),
 	             true, "h/s", H2B_SHORT, diff * 7158278, -1);
-	if (unlikely(current_diff != diff))
-		applog(LOG_NOTICE, "Network difficulty changed to %s (%s)", block_diff, net_hashrate);
-	current_diff = diff;
+	if (unlikely(goal->current_diff != diff))
+		applog(LOG_NOTICE, "Network difficulty changed to %s (%s)", goal->current_diff_str, goal->net_hashrate);
+	goal->current_diff = diff;
 }
 
 static bool test_work_current(struct work *work)
 {
 	bool ret = true;
-	char hexstr[65];
 	
 	if (work->mandatory)
 		return ret;
 	
 	uint32_t block_id = ((uint32_t*)(work->data))[1];
+	const uint8_t * const prevblkhash = &work->data[4];
 	
-	/* Hack to work around dud work sneaking into test */
-	bin2hex(hexstr, work->data + 8, 18);
-	if (!strncmp(hexstr, "000000000000000000000000000000000000", 36))
-		goto out_free;
+	{
+		/* Hack to work around dud work sneaking into test */
+		bool dudwork = true;
+		for (int i = 8; i < 26; ++i)
+			if (work->data[i])
+			{
+				dudwork = false;
+				break;
+			}
+		if (dudwork)
+			goto out_free;
+	}
 	
 	struct pool * const pool = work->pool;
+	struct mining_goal_info * const goal = pool->goal;
+	struct blockchain_info * const blkchain = goal->blkchain;
 	
 	/* Search to see if this block exists yet and if not, consider it a
 	 * new block and set the current block details to this one */
-	if (!block_exists(hexstr))
+	if (!block_exists(blkchain, prevblkhash))
 	{
-		struct block *s = calloc(sizeof(struct block), 1);
+		struct block_info * const s = calloc(sizeof(struct block_info), 1);
 		int deleted_block = 0;
 		ret = false;
 		
 		if (unlikely(!s))
 			quit (1, "test_work_current OOM");
-		strcpy(s->hash, hexstr);
-		s->block_no = new_blocks++;
+		memcpy(s->prevblkhash, prevblkhash, sizeof(s->prevblkhash));
+		s->block_id = block_id;
+		s->block_seen_order = new_blocks++;
+		s->first_seen_time = time(NULL);
 		
 		wr_lock(&blk_lock);
 		/* Only keep the last hour's worth of blocks in memory since
 		 * work from blocks before this is virtually impossible and we
 		 * want to prevent memory usage from continually rising */
-		if (HASH_COUNT(blocks) > 6)
+		if (HASH_COUNT(blkchain->blocks) > 6)
 		{
-			struct block *oldblock;
+			struct block_info *oldblock;
 			
-			HASH_SORT(blocks, block_sort);
-			oldblock = blocks;
-			deleted_block = oldblock->block_no;
-			HASH_DEL(blocks, oldblock);
+			HASH_SORT(blkchain->blocks, block_sort);
+			oldblock = blkchain->blocks;
+			deleted_block = oldblock->block_seen_order;
+			HASH_DEL(blkchain->blocks, oldblock);
 			free(oldblock);
 		}
-		HASH_ADD_STR(blocks, hash, s);
-		set_blockdiff(work);
+		HASH_ADD(hh, blkchain->blocks, prevblkhash, sizeof(s->prevblkhash), s);
+		set_blockdiff(goal, work);
 		wr_unlock(&blk_lock);
 		pool->block_id = block_id;
 		pool_update_work_restart_time(pool);
@@ -6966,7 +7171,7 @@ static bool test_work_current(struct work *work)
 #if BLKMAKER_VERSION > 1
 		template_nonce = 0;
 #endif
-		set_curblock(hexstr, &work->data[4]);
+		set_curblock(goal, s);
 		if (unlikely(new_blocks == 1))
 			goto out_free;
 		
@@ -6978,7 +7183,7 @@ static bool test_work_current(struct work *work)
 				       pool->pool_no);
 			}
 			else
-			if (have_longpoll)
+			if (goal->have_longpoll)
 				applog(LOG_NOTICE, "New block detected on network before longpoll");
 			else
 				applog(LOG_NOTICE, "New block detected on network");
@@ -7000,7 +7205,7 @@ static bool test_work_current(struct work *work)
 				// Pool actively changed block
 				if (pool == current_pool())
 					restart = true;
-				if (block_id == current_block_id)
+				if (block_id == blkchain->currentblk->block_id)
 				{
 					// Caught up, only announce if this pool is the one in use
 					if (restart)
@@ -7014,7 +7219,8 @@ static bool test_work_current(struct work *work)
 					// This might detect pools trying to double-spend or 51%,
 					// but let's not make any accusations until it's had time
 					// in the real world.
-					blkhashstr(hexstr, &work->data[4]);
+					char hexstr[65];
+					blkhashstr(hexstr, prevblkhash);
 					applog(LOG_WARNING, "%s %d is issuing work for an old block: %s",
 					       work->longpoll ? "Longpoll from pool" : "Pool",
 					       pool->pool_no,
@@ -7112,7 +7318,7 @@ static void display_pool_summary(struct pool *pool)
 	int pool_secs;
 
 	if (curses_active_locked()) {
-		wlog("Pool: %s\n", pool->rpc_url);
+		wlog("Pool: %s  Goal: %s\n", pool->rpc_url, pool->goal->name);
 		if (pool->solved)
 			wlog("SOLVED %d BLOCK%s!\n", pool->solved, pool->solved > 1 ? "S" : "");
 		if (!pool->has_stratum)
@@ -7266,6 +7472,8 @@ void write_config(FILE *fcfg)
 			fprintf(fcfg, "\n\t\t\"pool-proxy\" : \"%s\",", json_escape(pool->rpc_proxy));
 		fprintf(fcfg, "\n\t\t\"user\" : \"%s\",", json_escape(pool->rpc_user));
 		fprintf(fcfg, "\n\t\t\"pass\" : \"%s\",", json_escape(pool->rpc_pass));
+		if (strcmp(pool->goal->name, "default"))
+			fprintf(fcfg, "\n\t\t\"pool-goal\" : \"%s\",", pool->goal->name);
 		fprintf(fcfg, "\n\t\t\"pool-priority\" : \"%d\"", pool->prio);
 		if (pool->force_rollntime)
 			fprintf(fcfg, ",\n\t\t\"force-rollntime\" : %d", pool->force_rollntime);
@@ -7412,6 +7620,12 @@ void zero_stats(void)
 	awidth = rwidth = swidth = hwwidth = 1;
 #endif
 
+	struct mining_goal_info *goal, *tmpgoal;
+	HASH_ITER(hh, mining_goals, goal, tmpgoal)
+	{
+		goal->diff_accepted = 0;
+	}
+	
 	for (i = 0; i < total_pools; i++) {
 		struct pool *pool = pools[i];
 
@@ -8666,6 +8880,8 @@ fishy:
 		cg_runlock(&pool->data_lock);
 
 		if (json_is_true(res_val)) {
+			struct mining_goal_info * const goal = pool->goal;
+			
 			applog(LOG_NOTICE, "Accepted untracked stratum share from pool %d", pool->pool_no);
 
 			/* We don't know what device this came from so we can't
@@ -8675,6 +8891,7 @@ fishy:
 			pool->accepted++;
 			total_diff_accepted += pool_diff;
 			pool->diff_accepted += pool_diff;
+			goal->diff_accepted += pool_diff;
 			mutex_unlock(&stats_lock);
 		} else {
 			applog(LOG_NOTICE, "Rejected untracked stratum share from pool %d", pool->pool_no);
@@ -9029,11 +9246,12 @@ static void *stratum_thread(void *userdata)
 			cb_height_sz = bin_height[-1];
 			if (cb_height_sz == 3) {
 				// FIXME: The block number will overflow this by AD 2173
-				uint32_t block_id = ((uint32_t*)work->data)[1];
+				struct mining_goal_info * const goal = pool->goal;
+				const void * const prevblkhash = &work->data[4];
 				uint32_t height = 0;
 				memcpy(&height, bin_height, 3);
 				height = le32toh(height);
-				have_block_height(block_id, height);
+				have_block_height(goal, prevblkhash, height);
 			}
 
 			pool->swork.work_restart_id =
@@ -9067,7 +9285,8 @@ out:
 
 static void init_stratum_thread(struct pool *pool)
 {
-	have_longpoll = true;
+	struct mining_goal_info * const goal = pool->goal;
+	goal->have_longpoll = true;
 
 	if (unlikely(pthread_create(&pool->stratum_thread, NULL, stratum_thread, (void *)pool)))
 		quit(1, "Failed to create stratum thread");
@@ -9160,7 +9379,7 @@ static bool pool_active(struct pool *pool, bool pinging)
 	proto = want_gbt ? PLP_GETBLOCKTEMPLATE : PLP_GETWORK;
 
 tryagain:
-	rpc_req = prepare_rpc_req_probe(work, proto, NULL);
+	rpc_req = prepare_rpc_req_probe(work, proto, NULL, pool);
 	work->pool = pool;
 	if (!rpc_req)
 		goto out;
@@ -10414,6 +10633,8 @@ struct pool *_select_longpoll_pool(struct pool *cp, bool(*func)(struct pool *))
 		return cp;
 	for (i = 0; i < total_pools; i++) {
 		struct pool *pool = pools[i];
+		if (cp->goal != pool->goal)
+			continue;
 
 		if (func(pool))
 			return pool;
@@ -10485,7 +10706,7 @@ retry_pool:
 	}
 
 	/* Any longpoll from any pool is enough for this to be true */
-	have_longpoll = true;
+	pool->goal->have_longpoll = true;
 
 	wait_lpcurrent(cp);
 
@@ -10502,7 +10723,7 @@ retry_pool:
 
 		struct work *work = make_work();
 		char *lpreq;
-		lpreq = prepare_rpc_req(work, pool->lp_proto, pool->lp_id);
+		lpreq = prepare_rpc_req(work, pool->lp_proto, pool->lp_id, pool);
 		work->pool = pool;
 		if (!lpreq)
 		{
@@ -10591,7 +10812,12 @@ static void stop_longpoll(void)
 		pool->lp_started = false;
 		pthread_cancel(pool->longpoll_thread);
 	}
-	have_longpoll = false;
+	
+	struct mining_goal_info *goal, *tmpgoal;
+	HASH_ITER(hh, mining_goals, goal, tmpgoal)
+	{
+		goal->have_longpoll = false;
+	}
 }
 
 static void start_longpoll(void)
@@ -12487,7 +12713,6 @@ int main(int argc, char *argv[])
 {
 	struct sigaction handler;
 	struct thr_info *thr;
-	struct block *block;
 	unsigned int k;
 	int i;
 	int rearrange_pools = 0;
@@ -12626,17 +12851,11 @@ int main(int argc, char *argv[])
 	}
 #endif
 
+#ifdef HAVE_CURSES
 	devcursor = 8;
 	logstart = devcursor;
 	logcursor = logstart;
-
-	block = calloc(sizeof(struct block), 1);
-	if (unlikely(!block))
-		quit (1, "main OOM");
-	for (i = 0; i < 36; i++)
-		strcat(block->hash, "0");
-	HASH_ADD_STR(blocks, hash, block);
-	strcpy(current_block, block->hash);
+#endif
 
 	mutex_init(&submitting_lock);
 

+ 45 - 5
miner.h

@@ -947,7 +947,6 @@ extern bool opt_protocol;
 extern bool opt_dev_protocol;
 extern char *opt_coinbase_sig;
 extern char *request_target_str;
-extern bool have_longpoll;
 extern int opt_skip_checks;
 extern char *opt_kernel_path;
 extern char *opt_socks_proxy;
@@ -1061,6 +1060,7 @@ extern void clear_stratum_shares(struct pool *pool);
 extern void hashmeter2(struct thr_info *);
 extern bool stale_work(struct work *, bool share);
 extern bool stale_work_future(struct work *, bool share, unsigned long ustime);
+extern void blkhashstr(char *out, const unsigned char *hash);
 extern void set_target_to_pdiff(void *dest_target, double pdiff);
 #define bdiff_to_pdiff(n) (n * 1.0000152587)
 extern void set_target_to_bdiff(void *dest_target, double bdiff);
@@ -1093,8 +1093,11 @@ extern int enabled_pools;
 extern bool get_intrange(const char *arg, int *val1, int *val2);
 extern bool detect_stratum(struct pool *pool, char *url);
 extern void print_summary(void);
+extern struct mining_goal_info *get_mining_goal(const char *name);
+extern void mining_goal_reset(struct mining_goal_info * const goal);
 extern void adjust_quota_gcd(void);
-extern struct pool *add_pool(void);
+extern struct pool *add_pool2(struct mining_goal_info *);
+#define add_pool()  add_pool2(get_mining_goal("default"))
 extern bool add_pool_details(struct pool *pool, bool live, char *url, char *user, char *pass);
 
 #define MAX_GPUDEVICES 16
@@ -1122,6 +1125,43 @@ extern bool add_pool_details(struct pool *pool, bool live, char *url, char *user
 #define MAX_GPU_INTENSITY MAX_SHA_INTENSITY
 #endif
 
+struct block_info {
+	uint32_t block_id;
+	uint8_t prevblkhash[0x20];
+	unsigned block_seen_order;  // new_blocks when this block was first seen; was 'block_no'
+	uint32_t height;
+	time_t first_seen_time;
+	
+	UT_hash_handle hh;
+};
+
+struct blockchain_info {
+	struct block_info *blocks;
+	struct block_info *currentblk;
+	uint64_t currentblk_subsidy;  // only valid when height is known! (and assumes Bitcoin)
+	char currentblk_first_seen_time_str[0x20];  // was global blocktime
+};
+
+struct mining_goal_info {
+	unsigned id;
+	char *name;
+	bool is_default;
+	
+	struct blockchain_info *blkchain;
+	
+	double current_diff;
+	char current_diff_str[ALLOC_H2B_SHORTV];  // was global block_diff
+	char net_hashrate[ALLOC_H2B_SHORT];
+	
+	char *current_goal_detail;
+	
+	double diff_accepted;
+	
+	bool have_longpoll;
+	
+	UT_hash_handle hh;
+};
+
 extern struct string_elist *scan_devices;
 extern bool opt_force_dev_init;
 extern int nDevs;
@@ -1170,10 +1210,8 @@ extern int opt_fail_pause;
 extern int opt_log_interval;
 extern unsigned long long global_hashrate;
 extern unsigned unittest_failures;
-extern char *current_fullhash;
-extern double current_diff;
 extern double best_diff;
-extern time_t block_time;
+extern struct mining_goal_info *mining_goals;
 
 struct curl_ent {
 	CURL *curl;
@@ -1297,6 +1335,7 @@ struct pool {
 	time_t work_restart_time;
 	char work_restart_timestamp[11];
 	uint32_t	block_id;
+	struct mining_goal_info *goal;
 
 	enum pool_protocol proto;
 
@@ -1368,6 +1407,7 @@ struct pool {
 	bool stratum_init;
 	bool stratum_notify;
 	struct stratum_work swork;
+	bool next_goalreset;
 	uint8_t next_target[0x20];
 	char *next_nonce1;
 	int next_n2size;

+ 25 - 0
util.c

@@ -2553,6 +2553,12 @@ static bool parse_notify(struct pool *pool, json_t *val)
 	pool->submit_old = !clean;
 	pool->swork.clean = true;
 	
+	if (pool->next_goalreset)
+	{
+		pool->next_goalreset = false;
+		mining_goal_reset(pool->goal);
+	}
+	
 	if (pool->next_nonce1)
 	{
 		free(pool->swork.nonce1);
@@ -2739,6 +2745,16 @@ err:
 	return true;
 }
 
+static
+bool stratum_set_goal(struct pool * const pool, json_t * const val, json_t * const params)
+{
+	if (!uri_get_param_bool(pool->rpc_url, "goalreset", false))
+		return false;
+	
+	pool->next_goalreset = true;
+	return true;
+}
+
 static bool parse_reconnect(struct pool *pool, json_t *val)
 {
 	if (opt_disable_client_reconnect)
@@ -2905,6 +2921,10 @@ bool parse_method(struct pool *pool, char *s)
 		goto out;
 	}
 	
+	// Usage: mining.set_goal("goal name", [reserved])
+	if (!strncasecmp(buf, "mining.set_goal", 15) && stratum_set_goal(pool, val, params))
+		return_via(out, ret = true);
+	
 out:
 	if (val)
 		json_decref(val);
@@ -3282,6 +3302,11 @@ out:
 			sprintf(s, "{\"id\": \"xnsub\", \"method\": \"mining.extranonce.subscribe\", \"params\": []}");
 			_stratum_send(pool, s, strlen(s), true);
 		}
+		if (uri_get_param_bool(pool->rpc_url, "goalreset", false))
+		{
+			sprintf(s, "{\"id\": \"goalsub\", \"method\": \"mining.goal.subscribe\", \"params\": []}");
+			_stratum_send(pool, s, strlen(s), true);
+		}
 	} else {
 		if (recvd)
 		{