From 6ac33e57ac74be61b176451a5bc0765a6c4d724c Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Fri, 16 May 2014 12:42:50 +1000 Subject: [PATCH] Create initial stratum proxy connection --- src/generator.c | 269 +++++++++++++++++++++++++++++++++++++++++------ src/libckpool.c | 2 +- src/libckpool.h | 50 +++++++++ src/stratifier.c | 50 --------- 4 files changed, 290 insertions(+), 81 deletions(-) diff --git a/src/generator.c b/src/generator.c index adc4645e..d3ba1448 100644 --- a/src/generator.c +++ b/src/generator.c @@ -10,6 +10,7 @@ #include "config.h" #include +#include #include #include @@ -18,6 +19,29 @@ #include "generator.h" #include "bitcoin.h" +/* Per proxied pool instance data */ +struct proxy_instance { + char *auth; + char *pass; + + char *enonce1; + char *enonce1bin; + char *sessionid; + + tv_t last_message; + + double diff; + int absolute_shares; + int diff_shares; + tv_t last_share; + + int id; /* Message id for sending stratum messages */ + bool no_sessionid; /* Doesn't support session id resume on subscribe */ + bool no_params; /* Doesn't want any parameters on subscribe */ +}; + +typedef struct proxy_instance proxy_instance_t; + static int gen_loop(proc_instance_t *pi, connsock_t *cs) { unixsock_t *us = &pi->us; @@ -101,17 +125,226 @@ out: return ret; } +static int server_mode(ckpool_t *ckp, proc_instance_t *pi, connsock_t *cs, + const char *auth, const char *pass) +{ + char *userpass = NULL; + gbtbase_t gbt; + int ret = 1; + + memset(&gbt, 0, sizeof(gbt)); + + userpass = strdup(auth); + realloc_strcat(&userpass, ":"); + realloc_strcat(&userpass, pass); + cs->auth = http_base64(userpass); + if (!cs->auth) { + LOGWARNING("Failed to create base64 auth from %s", userpass); + goto out; + } + + cs->fd = connect_socket(cs->url, cs->port); + if (cs->fd < 0) { + LOGWARNING("FATAL: Failed to connect socket to %s:%s !", cs->url, cs->port); + goto out; + } + keep_sockalive(cs->fd); + /* Test we can connect, authorise and get a block template */ + if (!gen_gbtbase(cs, &gbt)) { + LOGWARNING("FATAL: Failed to get test block template from %s:%s auth %s !", + cs->url, cs->port, userpass); + goto out; + } + clear_gbtbase(&gbt); + if (!validate_address(cs, ckp->btcaddress)) { + LOGWARNING("FATAL: Invalid btcaddress: %s !", ckp->btcaddress); + goto out; + } + ret = gen_loop(pi, cs); +out: + close(cs->fd); + dealloc(userpass); + return ret; +} + +static bool send_json_msg(connsock_t *cs, json_t *json_msg) +{ + int len, sent; + char *s; + + s = json_dumps(json_msg, JSON_ESCAPE_SLASH); + realloc_strcat(&s, "\n"); + len = strlen(s); + sent = write_socket(cs->fd, s, len); + dealloc(s); + if (sent != len) { + LOGWARNING("Failed to send %d bytes in send_json_msg", len); + return false; + } + return true; +} + +static bool connect_proxy(connsock_t *cs) +{ + cs->fd = connect_socket(cs->url, cs->port); + if (cs->fd < 0) { + LOGWARNING("Failed to connect socket to %s:%s in connect_proxy", + cs->url, cs->port); + return false; + } + keep_sockalive(cs->fd); + return true; +} + +static bool parse_subscribe(connsock_t *cs, proxy_instance_t *proxi) +{ + bool ret = false; + int size; + + size = read_socket_line(cs); + if (size < 1) { + LOGWARNING("Failed to receive line in parse_subscribe"); + goto out; + } + LOGWARNING("Got message: %s", cs->buf); + ret = true; + +out: + return ret; +} + +static bool subscribe_stratum(connsock_t *cs, proxy_instance_t *proxi) +{ + json_t *req; + bool ret = false; + +retry: + /* Attempt to reconnect if the pool supports resuming */ + if (proxi->sessionid) { + req = json_pack("{s:i,s:s,s:[s,s]}", + "id", proxi->id++, + "method", "mining.subscribe", + "params", "ckproxy", proxi->sessionid); + /* Then attempt to connect with just the client description */ + } else if (!proxi->no_params) { + req = json_pack("{s:i,s:s,s:[s]}", + "id", proxi->id++, + "method", "mining.subscribe", + "params", "ckproxy"); + /* Then try without any parameters */ + } else { + req = json_pack("{s:i,s:s,s:[]}", + "id", proxi->id++, + "method", "mining.subscribe", + "params"); + } + ret = send_json_msg(cs, req); + json_decref(req); + if (!ret) { + LOGWARNING("Failed to send message in subscribe_stratum"); + close(cs->fd); + goto out; + } + ret = parse_subscribe(cs, proxi); + if (ret) + goto out; + + close(cs->fd); + if (proxi->no_params) { + LOGWARNING("Failed all subscription options in subscribe_stratum"); + goto out; + } + if (proxi->sessionid) { + LOGNOTICE("Failed sessionid reconnect in subscribe_stratum, retrying without"); + proxi->no_sessionid = true; + dealloc(proxi->sessionid); + } else { + LOGNOTICE("Failed connecting with parameters in subscribe_stratum, retrying without"); + proxi->no_params = true; + } + ret = connect_proxy(cs); + if (!ret) { + LOGWARNING("Failed to reconnect in subscribe_stratum"); + goto out; + } + goto retry; + +out: + return ret; +} + +static bool auth_stratum(connsock_t *cs, proxy_instance_t *proxi, const char *auth, + const char *pass) +{ + json_t *req; + bool ret; + + req = json_pack("{s:i,s:s,s:[s,s]}", + "id", proxi->id++, + "method", "mining.authorize", + "params", auth, pass); + ret = send_json_msg(cs, req); + json_decref(req); + if (!ret) { + LOGWARNING("Failed to send message in auth_stratum"); + close(cs->fd); + goto out; + } +out: + return ret; +} + +static int proxy_loop(proc_instance_t *pi, connsock_t *cs) +{ + return 0; +} + +static int proxy_mode(ckpool_t *ckp, proc_instance_t *pi, connsock_t *cs, + const char *auth, const char *pass) +{ + proxy_instance_t proxi; + int ret; + + memset(&proxi, 0, sizeof(proxi)); + if (!connect_proxy(cs)) { + LOGWARNING("FATAL: Failed to connect to %s:%s in proxy_mode!", + cs->url, cs->port); + goto out; + } + + /* Test we can connect, authorise and get stratum information */ + if (!subscribe_stratum(cs, &proxi)) { + LOGWARNING("FATAL: Failed initial subscribe to %s:%s !", + cs->url, cs->port); + goto out; + } + + if (!auth_stratum(cs, &proxi, auth, pass)) { + LOGWARNING("FATAL: Failed initial authorise to %s:%s with %s:%s !", + cs->url, cs->port, auth, pass); + goto out; + } + + ret = proxy_loop(pi, cs); +out: + close(cs->fd); + free(proxi.enonce1); + free(proxi.enonce1bin); + free(proxi.sessionid); + + return ret; +} + + /* FIXME: Hard wired to just use config 0 for now */ int generator(proc_instance_t *pi) { char *url, *auth, *pass, *userpass = NULL; ckpool_t *ckp = pi->ckp; - gbtbase_t gbt; connsock_t cs; int ret = 1; memset(&cs, 0, sizeof(cs)); - memset(&gbt, 0, sizeof(gbt)); if (!ckp->proxy) { url = ckp->btcdurl[0]; @@ -126,35 +359,11 @@ int generator(proc_instance_t *pi) LOGWARNING("Failed to extract address from %s", url); goto out; } - userpass = strdup(auth); - realloc_strcat(&userpass, ":"); - realloc_strcat(&userpass, pass); - cs.auth = http_base64(userpass); - if (!cs.auth) { - LOGWARNING("Failed to create base64 auth from %s", userpass); - goto out; - } - dealloc(userpass); - cs.fd = connect_socket(cs.url, cs.port); - if (cs.fd < 0) { - LOGWARNING("Failed to connect socket to %s:%s", cs.url, cs.port); - goto out; - } - keep_sockalive(cs.fd); - - /* Test we can connect, authorise and get a block template */ - if (!gen_gbtbase(&cs, &gbt)) { - LOGWARNING("Failed to get test block template from %s:%s auth %s", - cs.url, cs.port, userpass); - goto out; - } - clear_gbtbase(&gbt); - if (!validate_address(&cs, ckp->btcaddress)) { - LOGWARNING("Invalid btcaddress: %s, unable to start!", ckp->btcaddress); - goto out; - } - ret = gen_loop(pi, &cs); + if (!ckp->proxy) + ret = server_mode(ckp, pi, &cs, auth, pass); + else + ret = proxy_mode(ckp, pi, &cs, auth, pass); out: /* Clean up here */ dealloc(cs.url); diff --git a/src/libckpool.c b/src/libckpool.c index 6412364d..17ddb97f 100644 --- a/src/libckpool.c +++ b/src/libckpool.c @@ -460,7 +460,7 @@ retry: LOGERR("Select failed in write_socket"); goto out; } - ret = write(fd, buf, nbyte); + ret = write_length(fd, buf, nbyte); if (ret < 0) LOGWARNING("Failed to write in write_socket"); out: diff --git a/src/libckpool.h b/src/libckpool.h index 8bd9c299..914943c4 100644 --- a/src/libckpool.h +++ b/src/libckpool.h @@ -202,6 +202,56 @@ typedef struct unixsock unixsock_t; typedef struct proc_instance proc_instance_t; +/* No error checking with these, make sure we know they're valid already! */ +static inline void json_strcpy(char *buf, json_t *val, const char *key) +{ + strcpy(buf, json_string_value(json_object_get(val, key))); +} + +static inline void json_dblcpy(double *dbl, json_t *val, const char *key) +{ + *dbl = json_real_value(json_object_get(val, key)); +} + +static inline void json_uintcpy(uint32_t *u32, json_t *val, const char *key) +{ + *u32 = (uint32_t)json_integer_value(json_object_get(val, key)); +} + +static inline void json_uint64cpy(uint64_t *u64, json_t *val, const char *key) +{ + *u64 = (uint64_t)json_integer_value(json_object_get(val, key)); +} +static inline void json_intcpy(int *i, json_t *val, const char *key) +{ + *i = json_integer_value(json_object_get(val, key)); +} + +static inline void json_strdup(char **buf, json_t *val, const char *key) +{ + *buf = strdup(json_string_value(json_object_get(val, key))); +} + +static inline void json_set_string(json_t *val, const char *key, const char *str) +{ + json_object_set_nocheck(val, key, json_string(str)); +} + +static inline void json_set_int(json_t *val, const char *key, int integer) +{ + json_object_set_new_nocheck(val, key, json_integer(integer)); +} + +static inline void json_set_double(json_t *val, const char *key, double real) +{ + json_object_set_new_nocheck(val, key, json_real(real)); +} + +static inline void json_set_bool(json_t *val, const char *key, bool boolean) +{ + json_object_set_new_nocheck(val, key, json_boolean(boolean)); +} + void rename_proc(const char *name); void create_pthread(pthread_t *thread, void *(*start_routine)(void *), void *arg); void join_pthread(pthread_t thread); diff --git a/src/stratifier.c b/src/stratifier.c index 45ad65f7..937b4064 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -212,56 +212,6 @@ static share_t *shares; static cklock_t share_lock; -/* No error checking with these, make sure we know they're valid already! */ -static inline void json_strcpy(char *buf, json_t *val, const char *key) -{ - strcpy(buf, json_string_value(json_object_get(val, key))); -} - -static inline void json_dblcpy(double *dbl, json_t *val, const char *key) -{ - *dbl = json_real_value(json_object_get(val, key)); -} - -static inline void json_uintcpy(uint32_t *u32, json_t *val, const char *key) -{ - *u32 = (uint32_t)json_integer_value(json_object_get(val, key)); -} - -static inline void json_uint64cpy(uint64_t *u64, json_t *val, const char *key) -{ - *u64 = (uint64_t)json_integer_value(json_object_get(val, key)); -} -static inline void json_intcpy(int *i, json_t *val, const char *key) -{ - *i = json_integer_value(json_object_get(val, key)); -} - -static inline void json_strdup(char **buf, json_t *val, const char *key) -{ - *buf = strdup(json_string_value(json_object_get(val, key))); -} - -static inline void json_set_string(json_t *val, const char *key, const char *str) -{ - json_object_set_nocheck(val, key, json_string(str)); -} - -static inline void json_set_int(json_t *val, const char *key, int integer) -{ - json_object_set_new_nocheck(val, key, json_integer(integer)); -} - -static inline void json_set_double(json_t *val, const char *key, double real) -{ - json_object_set_new_nocheck(val, key, json_real(real)); -} - -static inline void json_set_bool(json_t *val, const char *key, bool boolean) -{ - json_object_set_new_nocheck(val, key, json_boolean(boolean)); -} - static void generate_coinbase(ckpool_t *ckp, workbase_t *wb) { char header[228];