pool/stratum/client.cpp
Tanguy Pruvot 9d4c4d41c7 stratum: reduce defines, prepare the autoexchange variable
also improve the user wallet addresses checks

and allow to mine aux coins in normal (gbt) + aux at the same time
2015-09-26 06:51:14 +02:00

495 lines
12 KiB
C++

#include "stratum.h"
//#define CLIENT_DEBUGLOG_
bool client_suggest_difficulty(YAAMP_CLIENT *client, json_value *json_params)
{
if(json_params->u.array.length>0)
{
double diff = client_normalize_difficulty(json_params->u.array.values[0]->u.dbl);
uint64_t user_target = diff_to_target(diff);
if(user_target >= YAAMP_MINDIFF && user_target <= YAAMP_MAXDIFF)
client->difficulty_actual = diff;
}
client_send_result(client, "true");
return true;
}
bool client_suggest_target(YAAMP_CLIENT *client, json_value *json_params)
{
client_send_result(client, "true");
return true;
}
bool client_subscribe(YAAMP_CLIENT *client, json_value *json_params)
{
//if(client_find_my_ip(client->sock->ip)) return false;
get_next_extraonce1(client->extranonce1_default);
client->extranonce2size_default = YAAMP_EXTRANONCE2_SIZE;
client->difficulty_actual = g_stratum_difficulty;
strcpy(client->extranonce1, client->extranonce1_default);
client->extranonce2size = client->extranonce2size_default;
get_random_key(client->notify_id);
if(json_params->u.array.length>0)
{
strncpy(client->version, json_params->u.array.values[0]->u.string.ptr, 1023);
// if(!strcmp(client->version, "stratum-proxy/0.0.1")) return false;
if(strstr(client->version, "NiceHash") || strstr(client->version, "proxy") || strstr(client->version, "/3."))
client->reconnectable = false;
}
if(json_params->u.array.length>1)
{
char notify_id[1024];
strncpy(notify_id, json_params->u.array.values[1]->u.string.ptr, 1023);
YAAMP_CLIENT *client1 = client_find_notify_id(notify_id, true);
if(client1)
{
strncpy(client->notify_id, notify_id, 1023);
client->jobid_locked = client1->jobid_locked;
// client->jobid_next = client1->jobid_next;
client->difficulty_actual = client1->difficulty_actual;
client->extranonce2size_default = client1->extranonce2size_default;
strcpy(client->extranonce1_default, client1->extranonce1_default);
client->extranonce2size = client1->extranonce2size_reconnect;
strcpy(client->extranonce1, client1->extranonce1_reconnect);
client->speed = client1->speed;
client->extranonce1_id = client1->extranonce1_id;
client->userid = client1->userid;
client->workerid = client1->workerid;
memcpy(client->job_history, client1->job_history, sizeof(client->job_history));
client1->lock_count = 0;
#ifdef CLIENT_DEBUGLOG_
debuglog("reconnecting client locked to %x\n", client->jobid_next);
#endif
}
else
{
YAAMP_CLIENT *client1 = client_find_notify_id(notify_id, false);
if(client1)
{
strncpy(client->notify_id, notify_id, 1023);
client->difficulty_actual = client1->difficulty_actual;
client->speed = client1->speed;
memcpy(client->job_history, client1->job_history, sizeof(client->job_history));
client1->lock_count = 0;
#ifdef CLIENT_DEBUGLOG_
debuglog("reconnecting2 client\n");
#endif
}
}
}
strcpy(client->extranonce1_last, client->extranonce1);
client->extranonce2size_last = client->extranonce2size;
#ifdef CLIENT_DEBUGLOG_
debuglog("new client with nonce %s\n", client->extranonce1);
#endif
client_send_result(client, "[[[\"mining.set_difficulty\",\"%s\"],[\"mining.notify\",\"%s\"]],\"%s\",%d]",
client->notify_id, client->notify_id, client->extranonce1, client->extranonce2size);
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////
bool client_validate_user_address(YAAMP_CLIENT *client)
{
if (!client->coinid) {
for(CLI li = g_list_coind.first; li; li = li->next) {
YAAMP_COIND *coind = (YAAMP_COIND *)li->data;
// debuglog("user %s testing on coin %s ...\n", client->username, coind->symbol);
if(!coind_can_mine(coind)) continue;
if(coind->pos) continue;
if(strlen(g_current_algo->name) && strcmp(g_current_algo->name, coind->algo)) continue;
if(coind_validate_user_address(coind, client->username)) {
debuglog("new user %s for coin %s\n", client->username, coind->symbol);
client->coinid = coind->id;
// update the db now to prevent addresses conflicts
CommonLock(&g_db_mutex);
db_init_user_coinid(g_db, client);
CommonUnlock(&g_db_mutex);
return true;
}
}
}
if (!client->coinid) {
return false;
}
YAAMP_COIND *coind = (YAAMP_COIND *)object_find(&g_list_coind, client->coinid);
if (!coind) {
clientlog(client, "unable to find the wallet for coinid %d...", client->coinid);
return false;
} else {
if(g_current_algo && strlen(g_current_algo->name) && strcmp(g_current_algo->name, coind->algo)) {
clientlog(client, "%s address is on the wrong coin %s, reset to auto...", client->username, coind->symbol);
client->coinid = 0;
CommonLock(&g_db_mutex);
db_init_user_coinid(g_db, client);
CommonUnlock(&g_db_mutex);
return false;
}
}
bool isvalid = coind_validate_user_address(coind, client->username);
if (isvalid) {
client->coinid = coind->id;
} else {
clientlog(client, "unable to verify %s address for user coinid %d...", coind->symbol, client->coinid);
}
return isvalid;
}
///////////////////////////////////////////////////////////////////////////////////////////
bool client_authorize(YAAMP_CLIENT *client, json_value *json_params)
{
if(json_params->u.array.length>1)
strncpy(client->password, json_params->u.array.values[1]->u.string.ptr, 1023);
if(json_params->u.array.length>0)
{
strncpy(client->username, json_params->u.array.values[0]->u.string.ptr, 1023);
client->username[34] = 0;
strncpy(client->worker, client->username+35, 1023);
// debuglog("%s\n", client->username);
// debuglog("%s\n", client->worker);
}
bool reset = client_initialize_multialgo(client);
if(reset) return false;
client_initialize_difficulty(client);
if(!client->userid || !client->workerid)
{
CommonLock(&g_db_mutex);
db_add_user(g_db, client);
if(client->userid == -1)
{
CommonUnlock(&g_db_mutex);
// client_block_ip(client, "account locked");
clientlog(client, "account locked");
return false;
}
db_add_worker(g_db, client);
CommonUnlock(&g_db_mutex);
}
#ifdef CLIENT_DEBUGLOG_
debuglog("new client %s, %s, %s\n", client->username, client->password, client->version);
#endif
// when auto exchange is disabled, only authorize good wallet address...
if (!g_autoexchange && !client_validate_user_address(client)) {
clientlog(client, "bad mining address %s", client->username);
client_send_result(client, "false");
CommonLock(&g_db_mutex);
db_clear_worker(g_db, client);
CommonUnlock(&g_db_mutex);
return false;
}
client_send_result(client, "true");
client_send_difficulty(client, client->difficulty_actual);
if(client->jobid_locked)
job_send_jobid(client, client->jobid_locked);
else
job_send_last(client);
g_list_client.AddTail(client);
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////
bool client_update_block(YAAMP_CLIENT *client, json_value *json_params)
{
// password, id, block
if(json_params->u.array.length < 3)
{
clientlog(client, "update block, bad params");
return false;
}
if(strcmp(g_tcp_password, json_params->u.array.values[0]->u.string.ptr))
{
clientlog(client, "update block, bad password");
return false;
}
YAAMP_COIND *coind = (YAAMP_COIND *)object_find(&g_list_coind, json_params->u.array.values[1]->u.integer, true);
if(!coind) return false;
#ifdef CLIENT_DEBUGLOG_
debuglog("new block for %s ", coind->name);
debuglog("%s\n", json_params->u.array.values[2]->u.string.ptr);
#endif
coind->newblock = true;
coind->notreportingcounter = 0;
block_confirm(coind->id, json_params->u.array.values[2]->u.string.ptr);
coind_create_job(coind);
object_unlock(coind);
if(coind->isaux) for(CLI li = g_list_coind.first; li; li = li->next)
{
YAAMP_COIND *coind = (YAAMP_COIND *)li->data;
if(!coind_can_mine(coind)) continue;
if(coind->pos) continue;
coind_create_job(coind);
}
job_signal();
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////
//YAAMP_SOURCE *source_init(YAAMP_CLIENT *client)
//{
// YAAMP_SOURCE *source = NULL;
// g_list_source.Enter();
//
// for(CLI li = g_list_source.first; li; li = li->next)
// {
// YAAMP_SOURCE *source1 = (YAAMP_SOURCE *)li->data;
// if(!strcmp(source1->ip, client->sock->ip))
// {
// source = source1;
// break;
// }
// }
//
// if(!source)
// {
// source = new YAAMP_SOURCE;
// memset(source, 0, sizeof(YAAMP_SOURCE));
//
// strncpy(source->ip, client->sock->ip, 1024);
// source->speed = 1;
//
// g_list_source.AddTail(source);
// }
//
// source->count++;
//
// g_list_source.Leave();
// return source;
//}
//
//void source_close(YAAMP_SOURCE *source)
//{
// g_list_source.Enter();
// source->count--;
//
// if(source->count <= 0)
// {
// g_list_source.Delete(source);
// delete source;
// }
//
// g_list_source.Leave();
//}
//
//void source_prune()
//{
//// debuglog("source_prune() %d\n", g_list_source.count);
// g_list_source.Enter();
// for(CLI li = g_list_source.first; li; li = li->next)
// {
// YAAMP_SOURCE *source = (YAAMP_SOURCE *)li->data;
// source->speed *= 0.8;
//
// double idx = source->speed/source->count;
// if(idx < 0.0005)
// {
// stratumlog("disconnect all ip %s, %s, count %d, %f, %f\n", source->ip, g_current_algo->name, source->count, source->speed, idx);
// for(CLI li = g_list_client.first; li; li = li->next)
// {
// YAAMP_CLIENT *client = (YAAMP_CLIENT *)li->data;
// if(client->deleted) continue;
// if(!client->workerid) continue;
//
// if(!strcmp(source->ip, client->sock->ip))
// shutdown(client->sock->sock, SHUT_RDWR);
// }
// }
//
// else if(source->count > 500)
// stratumlog("over 500 ip %s, %s, %d, %f, %f\n", source->ip, g_current_algo->name, source->count, source->speed, idx);
// }
//
// g_list_source.Leave();
//}
///////////////////////////////////////////////////////////////////////////////////////////
void *client_thread(void *p)
{
YAAMP_CLIENT *client = new YAAMP_CLIENT;
memset(client, 0, sizeof(YAAMP_CLIENT));
client->reconnectable = true;
client->speed = 1;
client->created = time(NULL);
client->last_best = time(NULL);
client->sock = socket_initialize((int)(long)p);
// client->source = source_init(client);
client->shares_per_minute = YAAMP_SHAREPERSEC;
client->last_submit_time = current_timestamp();
while(1)
{
if(client->submit_bad > 1024)
{
clientlog(client, "bad submits");
break;
}
json_value *json = socket_nextjson(client->sock, client);
if(!json)
{
// clientlog(client, "bad json");
break;
}
client->id_int = json_get_int(json, "id");
client->id_str = json_get_string(json, "id");
const char *method = json_get_string(json, "method");
if(!method)
{
json_value_free(json);
clientlog(client, "bad json, no method");
break;
}
json_value *json_params = json_get_array(json, "params");
if(!json_params)
{
json_value_free(json);
clientlog(client, "bad json, no params");
break;
}
#ifdef CLIENT_DEBUGLOG_
debuglog("client %s %d %s\n", method, client->id_int, client->id_str? client->id_str: "null");
#endif
bool b = false;
if(!strcmp(method, "mining.subscribe"))
b = client_subscribe(client, json_params);
else if(!strcmp(method, "mining.authorize"))
b = client_authorize(client, json_params);
else if(!strcmp(method, "mining.submit"))
b = client_submit(client, json_params);
else if(!strcmp(method, "mining.suggest_difficulty"))
b = client_suggest_difficulty(client, json_params);
else if(!strcmp(method, "mining.suggest_target"))
b = client_suggest_target(client, json_params);
else if(!strcmp(method, "mining.get_transactions"))
b = client_send_result(client, "[]");
else if(!strcmp(method, "mining.extranonce.subscribe"))
{
client->extranonce_subscribe = true;
b = client_send_result(client, "true");
}
else if(!strcmp(method, "mining.update_block"))
client_update_block(client, json_params);
else if(!strcmp(method, "getwork"))
{
clientlog(client, "using getwork");
break;
}
else
{
b = client_send_error(client, 20, "Not supported");
client->submit_bad++;
stratumlog("unknown method %s %s\n", method, client->sock->ip);
}
json_value_free(json);
if(!b) break;
}
// source_close(client->source);
#ifdef CLIENT_DEBUGLOG_
debuglog("client terminate\n");
#endif
if(client->sock->total_read == 0)
clientlog(client, "no data");
if(g_list_client.Find(client))
{
if(client->workerid && !client->reconnecting)
{
CommonLock(&g_db_mutex);
db_clear_worker(g_db, client);
CommonUnlock(&g_db_mutex);
}
object_delete(client);
}
else
client_delete(client);
pthread_exit(NULL);
}