|
|
@@ -1,5 +1,5 @@
|
|
|
/*
|
|
|
- * Copyright 2013-2014 Luke Dashjr
|
|
|
+ * Copyright 2013-2016 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
|
|
|
@@ -24,6 +24,7 @@
|
|
|
#include <event2/bufferevent.h>
|
|
|
#include <event2/event.h>
|
|
|
#include <event2/listener.h>
|
|
|
+#include <event2/thread.h>
|
|
|
|
|
|
#include <jansson.h>
|
|
|
|
|
|
@@ -101,8 +102,6 @@ void stratumsrv_send_set_difficulty(struct stratumsrv_conn * const conn, const f
|
|
|
bufferevent_write(bev, buf, bufsz);
|
|
|
}
|
|
|
|
|
|
-#define _ssm_gen_dummy_work work2d_gen_dummy_work
|
|
|
-
|
|
|
static
|
|
|
float stratumsrv_choose_share_pdiff(const struct stratumsrv_conn * const conn, const struct mining_algorithm * const malgo)
|
|
|
{
|
|
|
@@ -112,27 +111,75 @@ float stratumsrv_choose_share_pdiff(const struct stratumsrv_conn * const conn, c
|
|
|
return conn_pdiff;
|
|
|
}
|
|
|
|
|
|
+static void stratumsrv_boot_all_subscribed(const char *);
|
|
|
+static void _ssj_free(struct stratumsrv_job *);
|
|
|
+static void stratumsrv_job_pruner();
|
|
|
+
|
|
|
static
|
|
|
-bool stratumsrv_update_notify_str(struct pool * const pool, bool clean)
|
|
|
+bool stratumsrv_update_notify_str(struct pool * const pool)
|
|
|
{
|
|
|
+ const bool clean = _ssm_cur_job_work.pool ? stale_work(&_ssm_cur_job_work, true) : true;
|
|
|
+ struct timeval tv_now;
|
|
|
+
|
|
|
cg_rlock(&pool->data_lock);
|
|
|
|
|
|
+ if (!pool_has_usable_swork(pool))
|
|
|
+ {
|
|
|
+fail:
|
|
|
+ cg_runlock(&pool->data_lock);
|
|
|
+ applog(LOG_WARNING, "SSM: No usable 2D work upstream!");
|
|
|
+ if (clean)
|
|
|
+ stratumsrv_boot_all_subscribed("Current upstream pool does not have usable 2D work");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ timer_set_now(&tv_now);
|
|
|
+
|
|
|
+ {
|
|
|
+ struct work work;
|
|
|
+ work2d_gen_dummy_work_for_stale_check(&work, &pool->swork, &tv_now, NULL);
|
|
|
+
|
|
|
+ const bool is_stale = stale_work(&work, false);
|
|
|
+
|
|
|
+ clean_work(&work);
|
|
|
+
|
|
|
+ if (is_stale) {
|
|
|
+ cg_runlock(&pool->data_lock);
|
|
|
+ applog(LOG_DEBUG, "SSM: Ignoring work update notification while pool %d has stale swork", pool->pool_no);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
struct stratumsrv_conn *conn;
|
|
|
const struct stratum_work * const swork = &pool->swork;
|
|
|
const int n2size = pool->swork.n2size;
|
|
|
+ const size_t coinb2_offset = swork->nonce2_offset + n2size;
|
|
|
+ const size_t coinb2_len = bytes_len(&swork->coinbase) - swork->nonce2_offset - n2size;
|
|
|
+
|
|
|
+ if (_ssm_last_ssj &&
|
|
|
+ !(memcmp(&swork->header1[0], &_ssm_last_ssj->swork.header1[0], 0x24)
|
|
|
+ || swork->nonce2_offset != _ssm_last_ssj->swork.nonce2_offset
|
|
|
+ || bytes_len(&swork->coinbase) != bytes_len(&_ssm_last_ssj->swork.coinbase)
|
|
|
+ || memcmp(bytes_buf(&swork->coinbase), bytes_buf(&_ssm_last_ssj->swork.coinbase), swork->nonce2_offset)
|
|
|
+ || memcmp(&bytes_buf(&swork->coinbase)[coinb2_offset], &bytes_buf(&_ssm_last_ssj->swork.coinbase)[coinb2_offset], coinb2_len)
|
|
|
+ || memcmp(swork->diffbits, _ssm_last_ssj->swork.diffbits, 4)
|
|
|
+ )) {
|
|
|
+ cg_runlock(&pool->data_lock);
|
|
|
+ applog(LOG_DEBUG, "SSM: Updating with (near?-)identical work2d; skipping...");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
char my_job_id[33];
|
|
|
int i;
|
|
|
struct stratumsrv_job *ssj;
|
|
|
ssize_t n2pad = work2d_pad_xnonce_size(swork);
|
|
|
if (n2pad < 0)
|
|
|
{
|
|
|
- cg_runlock(&pool->data_lock);
|
|
|
- return false;
|
|
|
+ goto fail;
|
|
|
}
|
|
|
size_t coinb1in_lenx = swork->nonce2_offset * 2;
|
|
|
size_t n2padx = n2pad * 2;
|
|
|
size_t coinb1_lenx = coinb1in_lenx + n2padx;
|
|
|
- size_t coinb2_len = bytes_len(&swork->coinbase) - swork->nonce2_offset - n2size;
|
|
|
size_t coinb2_lenx = coinb2_len * 2;
|
|
|
sprintf(my_job_id, "%"PRIx64"-%"PRIx64, (uint64_t)time(NULL), _ssm_jobid++);
|
|
|
// NOTE: The buffer has up to 2 extra/unused bytes:
|
|
|
@@ -147,7 +194,7 @@ bool stratumsrv_update_notify_str(struct pool * const pool, bool clean)
|
|
|
bin2hex(coinb1, bytes_buf(&swork->coinbase), swork->nonce2_offset);
|
|
|
work2d_pad_xnonce(&coinb1[coinb1in_lenx], swork, true);
|
|
|
coinb1[coinb1_lenx] = '\0';
|
|
|
- bin2hex(coinb2, &bytes_buf(&swork->coinbase)[swork->nonce2_offset + n2size], coinb2_len);
|
|
|
+ bin2hex(coinb2, &bytes_buf(&swork->coinbase)[coinb2_offset], coinb2_len);
|
|
|
p += sprintf(p, "{\"params\":[\"%s\",\"%s\",\"%s\",\"%s\",[", my_job_id, prevhash, coinb1, coinb2);
|
|
|
for (i = 0; i < swork->merkles; ++i)
|
|
|
{
|
|
|
@@ -172,17 +219,30 @@ bool stratumsrv_update_notify_str(struct pool * const pool, bool clean)
|
|
|
*ssj = (struct stratumsrv_job){
|
|
|
.my_job_id = strdup(my_job_id),
|
|
|
};
|
|
|
- timer_set_now(&ssj->tv_prepared);
|
|
|
+ ssj->tv_prepared = tv_now;
|
|
|
stratum_work_cpy(&ssj->swork, swork);
|
|
|
|
|
|
cg_runlock(&pool->data_lock);
|
|
|
|
|
|
- ssj->swork.data_lock_p = NULL;
|
|
|
+ if (clean)
|
|
|
+ {
|
|
|
+ struct stratumsrv_job *ssj, *tmp;
|
|
|
+
|
|
|
+ applog(LOG_DEBUG, "SSM: Current replacing job stale, pruning all jobs");
|
|
|
+ HASH_ITER(hh, _ssm_jobs, ssj, tmp)
|
|
|
+ {
|
|
|
+ HASH_DEL(_ssm_jobs, ssj);
|
|
|
+ _ssj_free(ssj);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ stratumsrv_job_pruner();
|
|
|
+
|
|
|
HASH_ADD_KEYPTR(hh, _ssm_jobs, ssj->my_job_id, strlen(ssj->my_job_id), ssj);
|
|
|
|
|
|
if (likely(_ssm_cur_job_work.pool))
|
|
|
clean_work(&_ssm_cur_job_work);
|
|
|
- _ssm_gen_dummy_work(&_ssm_cur_job_work, &ssj->swork, &ssj->tv_prepared, NULL, 0);
|
|
|
+ work2d_gen_dummy_work_for_stale_check(&_ssm_cur_job_work, &ssj->swork, &ssj->tv_prepared, NULL);
|
|
|
|
|
|
_ssm_notify_sz = p - buf;
|
|
|
assert(_ssm_notify_sz <= bufsz);
|
|
|
@@ -329,7 +389,6 @@ static
|
|
|
void _stratumsrv_update_notify(evutil_socket_t fd, short what, __maybe_unused void *p)
|
|
|
{
|
|
|
struct pool *pool = current_pool();
|
|
|
- bool clean;
|
|
|
|
|
|
if (fd == _ssm_update_notifier[0])
|
|
|
{
|
|
|
@@ -338,37 +397,8 @@ void _stratumsrv_update_notify(evutil_socket_t fd, short what, __maybe_unused vo
|
|
|
applog(LOG_DEBUG, "SSM: Update triggered by notifier");
|
|
|
}
|
|
|
|
|
|
- clean = _ssm_cur_job_work.pool ? stale_work(&_ssm_cur_job_work, true) : true;
|
|
|
- if (clean)
|
|
|
- {
|
|
|
- struct stratumsrv_job *ssj, *tmp;
|
|
|
-
|
|
|
- applog(LOG_DEBUG, "SSM: Current replacing job stale, pruning all jobs");
|
|
|
- HASH_ITER(hh, _ssm_jobs, ssj, tmp)
|
|
|
- {
|
|
|
- HASH_DEL(_ssm_jobs, ssj);
|
|
|
- _ssj_free(ssj);
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- stratumsrv_job_pruner();
|
|
|
-
|
|
|
- if (!pool_has_usable_swork(pool))
|
|
|
- {
|
|
|
- applog(LOG_WARNING, "SSM: No usable 2D work upstream!");
|
|
|
- if (clean)
|
|
|
- stratumsrv_boot_all_subscribed("Current upstream pool does not have usable 2D work");
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- if (!stratumsrv_update_notify_str(pool, clean))
|
|
|
- {
|
|
|
- applog(LOG_WARNING, "SSM: Failed to subdivide upstream stratum notify!");
|
|
|
- if (clean)
|
|
|
- stratumsrv_boot_all_subscribed("Current upstream pool does not have active stratum");
|
|
|
- }
|
|
|
+ stratumsrv_update_notify_str(pool);
|
|
|
|
|
|
-out: ;
|
|
|
struct timeval tv_scantime = {
|
|
|
.tv_sec = opt_scantime,
|
|
|
};
|
|
|
@@ -789,32 +819,42 @@ void stratumlistener(struct evconnlistener *listener, evutil_socket_t sock, stru
|
|
|
bufferevent_enable(bev, EV_READ | EV_WRITE);
|
|
|
}
|
|
|
|
|
|
-void stratumsrv_start();
|
|
|
+static bool stratumsrv_init_server(void);
|
|
|
|
|
|
-void stratumsrv_change_port()
|
|
|
+bool stratumsrv_change_port(const unsigned port)
|
|
|
{
|
|
|
- struct event_base * const evbase = _smm_evbase;
|
|
|
-
|
|
|
- if (_smm_listener)
|
|
|
- evconnlistener_free(_smm_listener);
|
|
|
-
|
|
|
- if (!_smm_running)
|
|
|
- {
|
|
|
- stratumsrv_start();
|
|
|
- return;
|
|
|
+ if (!_smm_running) {
|
|
|
+ if (!stratumsrv_init_server()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+ struct event_base * const evbase = _smm_evbase;
|
|
|
+ struct evconnlistener * const old_smm_listener = _smm_listener;
|
|
|
+
|
|
|
struct sockaddr_in sin = {
|
|
|
.sin_family = AF_INET,
|
|
|
.sin_addr.s_addr = INADDR_ANY,
|
|
|
- .sin_port = htons(stratumsrv_port),
|
|
|
+ .sin_port = htons(port),
|
|
|
};
|
|
|
_smm_listener = evconnlistener_new_bind(evbase, stratumlistener, NULL, (
|
|
|
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_CLOSE_ON_EXEC | LEV_OPT_REUSEABLE
|
|
|
), 0x10, (void*)&sin, sizeof(sin));
|
|
|
|
|
|
+ if (!_smm_listener) {
|
|
|
+ applog(LOG_ERR, "SSM: Failed to listen on port %u", (unsigned)port);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
// NOTE: libevent doesn't seem to implement LEV_OPT_CLOSE_ON_EXEC for Windows, so we must do this ourselves
|
|
|
set_cloexec_socket(evconnlistener_get_fd(_smm_listener), true);
|
|
|
+
|
|
|
+ if (old_smm_listener) {
|
|
|
+ evconnlistener_free(old_smm_listener);
|
|
|
+ }
|
|
|
+ stratumsrv_port = port;
|
|
|
+
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
static
|
|
|
@@ -823,29 +863,59 @@ void *stratumsrv_thread(__maybe_unused void *p)
|
|
|
pthread_detach(pthread_self());
|
|
|
RenameThread("stratumsrv");
|
|
|
|
|
|
+ struct event_base *evbase = _smm_evbase;
|
|
|
+ event_base_dispatch(evbase);
|
|
|
+ _smm_running = false;
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static
|
|
|
+bool stratumsrv_init_server() {
|
|
|
work2d_init();
|
|
|
|
|
|
+ if (-1
|
|
|
+#if EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED
|
|
|
+ && evthread_use_windows_threads()
|
|
|
+#endif
|
|
|
+#if EVTHREAD_USE_PTHREADS_IMPLEMENTED
|
|
|
+ && evthread_use_pthreads()
|
|
|
+#endif
|
|
|
+ ) {
|
|
|
+ applog(LOG_ERR, "SSM: %s failed", "event_use_*threads");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
struct event_base *evbase = event_base_new();
|
|
|
+ if (!evbase) {
|
|
|
+ applog(LOG_ERR, "SSM: %s failed", "event_base_new");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
_smm_evbase = evbase;
|
|
|
+
|
|
|
{
|
|
|
ev_notify = evtimer_new(evbase, _stratumsrv_update_notify, NULL);
|
|
|
+ if (!ev_notify) {
|
|
|
+ applog(LOG_ERR, "SSM: %s failed", "evtimer_new");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
_stratumsrv_update_notify(-1, 0, NULL);
|
|
|
}
|
|
|
{
|
|
|
notifier_init(_ssm_update_notifier);
|
|
|
struct event *ev_update_notifier = event_new(evbase, _ssm_update_notifier[0], EV_READ | EV_PERSIST, _stratumsrv_update_notify, NULL);
|
|
|
+ if (!ev_update_notifier) {
|
|
|
+ applog(LOG_ERR, "SSM: %s failed", "event_new");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
event_add(ev_update_notifier, NULL);
|
|
|
}
|
|
|
- stratumsrv_change_port();
|
|
|
- event_base_dispatch(evbase);
|
|
|
|
|
|
- return NULL;
|
|
|
-}
|
|
|
-
|
|
|
-void stratumsrv_start()
|
|
|
-{
|
|
|
_smm_running = true;
|
|
|
+
|
|
|
pthread_t pth;
|
|
|
if (unlikely(pthread_create(&pth, NULL, stratumsrv_thread, NULL)))
|
|
|
quit(1, "stratumsrv thread create failed");
|
|
|
-}
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|