diff --git a/Makefile b/Makefile index 2367922..8d34b3e 100644 --- a/Makefile +++ b/Makefile @@ -103,7 +103,7 @@ endif LDPARAM += $(LDFLAGS) -LIBS := lmdb pthread microhttpd unbound +LIBS := lmdb pthread unbound ifeq ($(OS), Darwin) LIBS += c++ \ boost_system-mt boost_date_time-mt boost_chrono-mt \ @@ -123,7 +123,9 @@ ifeq ($(HID_FOUND), 1) endif PKG_LIBS := $(shell pkg-config \ - "libevent >= 2.1" \ + "libevent_core >= 2.1" \ + "libevent_pthreads >= 2.1" \ + "libevent_extra >= 2.1" \ json-c \ openssl \ libsodium \ @@ -135,7 +137,9 @@ DLIBS = INCPATH := $(DIRS) ${MONERO_INC} /opt/local/include /usr/local/include PKG_INC := $(shell pkg-config \ - "libevent >= 2.1" \ + "libevent_core >= 2.1" \ + "libevent_pthreads >= 2.1" \ + "libevent_extra >= 2.1" \ json-c \ openssl \ libsodium \ diff --git a/README.md b/README.md index 8687095..166b4cc 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,7 @@ mine on. Further information can be found in [stratum-ss.md](./stratum-ss.md). I have tested this quite a bit on the Monero testnet (if you plan to do the same, ensure to use `--testnet` flag when starting your wallet and -daemon) and mainnet, but there is always room for improvement. Please see the -[TODO](./TODO) file for the current list of things that could do with looking -at. +daemon) and mainnet, but there is always room for improvement. There is also a reference mainnet pool setup and running at [http://monerop.com](http://monerop.com). @@ -52,14 +50,13 @@ to build the pool: - liblmdb - libevent - json-c -- libmicrohttpd - uuid As an example, on Ubuntu, these dependencies can be installed with the following command: ``` -sudo apt-get install liblmdb-dev libevent-dev libjson-c-dev libmicrohttpd-dev uuid-dev +sudo apt-get install liblmdb-dev libevent-dev libjson-c-dev uuid-dev ``` ### Compile @@ -129,6 +126,17 @@ There is a minimal web UI that gets served on the port specified in the config file. It's advisable to use either Apache or Nginx as a proxy in front of this with some appropriate caching. +## SSL + +The pool has been tested behind both [HAProxy](http://www.haproxy.org/) and +[stunnel](https://www.stunnel.org/), so if you wish to provide SSL access to the +pool, these are both good options and simple to setup. The [reference +pool](https://monerop.com) makes use of HAProxy and port 4343 for SSL traffic. + +The web UI, as mentioned above, should ideally be placed behind a *caching +proxy*. Therefore SSL termination should be be configured there (i.e. in +Apache/Nginx). + ## Supporting the project This mining pool has **no built-in developer donation** (like *other* mining diff --git a/TODO b/TODO deleted file mode 100644 index 45ace3f..0000000 --- a/TODO +++ /dev/null @@ -1,7 +0,0 @@ -Things to work on: - - Add protection against invalid share flooding - - Offload payout processing to a separate thread (appears not needed, test - more) - - Offload RPC requests to a separate thread (appears not needed, test more) - -[//]: # ( vim: set tw=80: ) diff --git a/src/pool.c b/src/pool.c index a134c21..b1b17eb 100644 --- a/src/pool.c +++ b/src/pool.c @@ -40,6 +40,7 @@ developers. #include #include #include +#include #include @@ -2948,6 +2949,8 @@ cleanup(void) int main(int argc, char **argv) { + int evthread_use_pthreads(void); + static struct option options[] = { {"config-file", required_argument, 0, 'c'}, diff --git a/src/webui.c b/src/webui.c index d63c91d..27f5b70 100644 --- a/src/webui.c +++ b/src/webui.c @@ -35,41 +35,37 @@ developers. #define __STDC_FORMAT_MACROS #include -#include #include #include #include #include -#include -#include -#include -#include -#include #include -#include +#include +#include +#include #include "log.h" #include "pool.h" #include "webui.h" -#define TAG_MAX 17 -#define PAGE_MAX 8192 -#define JSON_MAX 512 - extern unsigned char webui_html[]; extern unsigned int webui_html_len; -static struct MHD_Daemon *mhd_daemon; +static pthread_t handle; +static struct event_base *webui_base; +static struct evhttp *webui_httpd; +static struct evhttp_bound_socket *webui_listener; -int -send_json_stats (void *cls, struct MHD_Connection *connection) + +static void +send_json_stats(struct evhttp_request *req, void *arg) { - struct MHD_Response *response; - int ret; - wui_context_t *context = (wui_context_t*) cls; - char json[JSON_MAX]; + struct evbuffer *buf = evhttp_request_get_output_buffer(req); + wui_context_t *context = (wui_context_t*) arg; + struct evkeyvalq *hdrs_in = NULL; + struct evkeyvalq *hdrs_out = NULL; uint64_t ph = context->pool_stats->pool_hashrate; uint64_t nh = context->pool_stats->network_hashrate; uint64_t height = context->pool_stats->network_height; @@ -79,15 +75,19 @@ send_json_stats (void *cls, struct MHD_Connection *connection) unsigned ss = context->allow_self_select; uint64_t mh = 0; double mb = 0.0; - const char *wa = MHD_lookup_connection_value(connection, - MHD_COOKIE_KIND, "wa"); - if (wa != NULL) + + hdrs_in = evhttp_request_get_input_headers(req); + const char *cookies = evhttp_find_header(hdrs_in, "Cookie"); + char *wa = strstr(cookies, "wa="); + if (wa) { + wa += 3; mh = miner_hr(wa); uint64_t balance = miner_balance(wa); mb = (double) balance / 1000000000000.0; } - snprintf(json, JSON_MAX, "{" + + evbuffer_add_printf(buf, "{" "\"pool_hashrate\":%"PRIu64"," "\"network_hashrate\":%"PRIu64"," "\"network_height\":%"PRIu64"," @@ -105,54 +105,84 @@ send_json_stats (void *cls, struct MHD_Connection *connection) context->payment_threshold, context->pool_fee, context->pool_port, ss, context->pool_stats->connected_miners, mh, mb); - response = MHD_create_response_from_buffer(strlen(json), - (void*) json, MHD_RESPMEM_MUST_COPY); - MHD_add_response_header (response, "Content-Type", "application/json"); - ret = MHD_queue_response(connection, MHD_HTTP_OK, response); - MHD_destroy_response(response); - return ret; + hdrs_out = evhttp_request_get_output_headers(req); + evhttp_add_header(hdrs_out, "Content-Type", "application/json"); + evhttp_send_reply(req, HTTP_OK, "OK", buf); } -int -answer_to_connection (void *cls, struct MHD_Connection *connection, - const char *url, - const char *method, const char *version, - const char *upload_data, - size_t *upload_data_size, void **con_cls) +static void +process_request(struct evhttp_request *req, void *arg) { - if (strstr(url, "/stats") != NULL) - return send_json_stats(cls, connection); + const char *url = evhttp_request_get_uri(req); + struct evbuffer *buf = NULL; + struct evkeyvalq *hdrs_out = NULL; - struct MHD_Response *response; - response = MHD_create_response_from_buffer(webui_html_len, - (void*) webui_html, MHD_RESPMEM_PERSISTENT); - int ret = MHD_queue_response(connection, MHD_HTTP_OK, response); - MHD_destroy_response(response); - return ret; + if (strstr(url, "/stats") != NULL) + { + send_json_stats(req, arg); + return; + } + + buf = evhttp_request_get_output_buffer(req); + evbuffer_add(buf, webui_html, webui_html_len); + hdrs_out = evhttp_request_get_output_headers(req); + evhttp_add_header(hdrs_out, "Content-Type", "text/html"); + evhttp_send_reply(req, HTTP_OK, "OK", buf); +} + +static void * +thread_main(void *ctx) +{ + wui_context_t *context = (wui_context_t*) ctx; + webui_listener = evhttp_bind_socket_with_handle( + webui_httpd, "0.0.0.0", context->port); + if(!webui_listener) + { + log_fatal("Failed to bind for port: %u", context->port); + return 0; + } + evhttp_set_gencb(webui_httpd, process_request, ctx); + event_base_dispatch(webui_base); + event_base_free(webui_base); + return 0; } int start_web_ui(wui_context_t *context) { log_debug("Starting Web UI"); - int use_epoll = MHD_is_feature_supported(MHD_FEATURE_EPOLL) == MHD_YES; - log_debug("MHD will use epoll: %u", use_epoll); - - unsigned flags = MHD_USE_SELECT_INTERNALLY; - if (use_epoll) - flags = MHD_USE_EPOLL_INTERNALLY_LINUX_ONLY; - - mhd_daemon = MHD_start_daemon(flags, - context->port, NULL, NULL, - &answer_to_connection, (void*) context, MHD_OPTION_END); - return mhd_daemon != NULL ? 0 : -1; + if (webui_base || handle) + { + log_error("Already running"); + return -1; + } + webui_base = event_base_new(); + if (!webui_base) + { + log_fatal("Failed to create httpd event base"); + return 0; + } + webui_httpd = evhttp_new(webui_base); + if (!webui_httpd) + { + log_fatal("Failed to create evhttp event"); + return 0; + } + int rc = pthread_create(&handle, NULL, thread_main, context); + if (!rc) + pthread_detach(handle); + return rc; } void stop_web_ui(void) { log_debug("Stopping Web UI"); - if (mhd_daemon != NULL) - MHD_stop_daemon(mhd_daemon); + if (webui_listener && webui_httpd) + evhttp_del_accept_socket(webui_httpd, webui_listener); + if (webui_httpd) + evhttp_free(webui_httpd); + if (webui_base) + event_base_loopbreak(webui_base); }