pool/stratum/client_submit.cpp
Tanguy Pruvot 6d6fd9337f stratum: new algos, attempt to allow old sha merkleroot
blake (SFR) and whirlpool (J) tested ok with a sha256d merkleroot

So add a stratum setting to allow single sha256 merkle root...
The shares are now valid on the pool side, but remains to find
an old wallet to test... like Whirlcoin or blakecoin

keccak not tested, could have the same problem about merkle/miners
2015-10-07 05:04:04 +02:00

325 lines
9.9 KiB
C++

#include "stratum.h"
//#define MERKLE_DEBUGLOG
#define HASH_DEBUGLOG_
//#define DONTSUBMIT
void build_submit_values(YAAMP_JOB_VALUES *submitvalues, YAAMP_JOB_TEMPLATE *templ,
const char *nonce1, const char *nonce2, const char *ntime, const char *nonce)
{
sprintf(submitvalues->coinbase, "%s%s%s%s", templ->coinb1, nonce1, nonce2, templ->coinb2);
int coinbase_len = strlen(submitvalues->coinbase);
unsigned char coinbase_bin[1024];
memset(coinbase_bin, 0, 1024);
binlify(coinbase_bin, submitvalues->coinbase);
char doublehash[128];
memset(doublehash, 0, 128);
// some (old) wallet/algos need a simple SHA256 (blakecoin, whirlcoin, groestlcoin...)
YAAMP_HASH_FUNCTION merkle_hash = sha256_double_hash_hex;
if (g_current_algo->merkle_func)
merkle_hash = g_current_algo->merkle_func;
merkle_hash((char *)coinbase_bin, doublehash, coinbase_len/2);
string merkleroot = merkle_with_first(templ->txsteps, doublehash);
ser_string_be(merkleroot.c_str(), submitvalues->merkleroot_be, 8);
#ifdef MERKLE_DEBUGLOG
printf("merkle root %s\n", merkleroot.c_str());
#endif
sprintf(submitvalues->header, "%s%s%s%s%s%s", templ->version, templ->prevhash_be, submitvalues->merkleroot_be,
ntime, templ->nbits, nonce);
ser_string_be(submitvalues->header, submitvalues->header_be, 20);
binlify(submitvalues->header_bin, submitvalues->header_be);
// printf("%s\n", submitvalues->header_be);
int header_len = strlen(submitvalues->header)/2;
g_current_algo->hash_function((char *)submitvalues->header_bin, (char *)submitvalues->hash_bin, header_len);
hexlify(submitvalues->hash_hex, submitvalues->hash_bin, 32);
string_be(submitvalues->hash_hex, submitvalues->hash_be);
}
/////////////////////////////////////////////////////////////////////////////////
void client_do_submit(YAAMP_CLIENT *client, YAAMP_JOB *job, YAAMP_JOB_VALUES *submitvalues, char *extranonce2, char *ntime, char *nonce)
{
YAAMP_COIND *coind = job->coind;
YAAMP_JOB_TEMPLATE *templ = job->templ;
if(job->block_found) return;
if(job->deleted) return;
uint64_t hash_int = get_hash_difficulty(submitvalues->hash_bin);
uint64_t coin_target = decode_compact(templ->nbits);
int block_size = YAAMP_SMALLBUFSIZE;
vector<string>::const_iterator i;
for(i = templ->txdata.begin(); i != templ->txdata.end(); ++i)
block_size += strlen((*i).c_str());
char *block_hex = (char *)malloc(block_size);
if(!block_hex) return;
// do aux first
for(int i=0; i<templ->auxs_size; i++)
{
if(!templ->auxs[i]) continue;
YAAMP_COIND *coind_aux = templ->auxs[i]->coind;
if(!coind_aux || !strcmp(coind->symbol, coind_aux->symbol2))
continue;
unsigned char target_aux[1024];
binlify(target_aux, coind_aux->aux.target);
uint64_t coin_target_aux = get_hash_difficulty(target_aux);
if(hash_int <= coin_target_aux)
{
memset(block_hex, 0, block_size);
strcat(block_hex, submitvalues->coinbase); // parent coinbase
strcat(block_hex, submitvalues->hash_be); // parent hash
////////////////////////////////////////////////// parent merkle steps
sprintf(block_hex+strlen(block_hex), "%02x", (unsigned char)templ->txsteps.size());
vector<string>::const_iterator i;
for(i = templ->txsteps.begin(); i != templ->txsteps.end(); ++i)
sprintf(block_hex + strlen(block_hex), "%s", (*i).c_str());
strcat(block_hex, "00000000");
////////////////////////////////////////////////// auxs merkle steps
vector<string> lresult = coind_aux_merkle_branch(templ->auxs, templ->auxs_size, coind_aux->aux.index);
sprintf(block_hex+strlen(block_hex), "%02x", (unsigned char)lresult.size());
for(i = lresult.begin(); i != lresult.end(); ++i)
sprintf(block_hex+strlen(block_hex), "%s", (*i).c_str());
sprintf(block_hex+strlen(block_hex), "%02x000000", (unsigned char)coind_aux->aux.index);
////////////////////////////////////////////////// parent header
strcat(block_hex, submitvalues->header_be);
bool b = coind_submitgetauxblock(coind_aux, coind_aux->aux.hash, block_hex);
if(b)
{
debuglog("*** ACCEPTED %s %d\n", coind_aux->name, coind_aux->height+1);
block_add(client->userid, coind_aux->id, coind_aux->height, target_to_diff(coin_target_aux),
target_to_diff(hash_int), coind_aux->aux.hash, "");
}
else
debuglog("%s %d rejected\n", coind_aux->name, coind_aux->height+1);
}
}
if(hash_int <= coin_target)
{
memset(block_hex, 0, block_size);
sprintf(block_hex, "%s%02x%s", submitvalues->header_be, (unsigned char)templ->txcount, submitvalues->coinbase);
vector<string>::const_iterator i;
for(i = templ->txdata.begin(); i != templ->txdata.end(); ++i)
sprintf(block_hex+strlen(block_hex), "%s", (*i).c_str());
if(coind->txmessage)
strcat(block_hex, "00");
bool b = coind_submit(coind, block_hex);
if(b)
{
debuglog("*** ACCEPTED %s %d\n", coind->name, templ->height);
job->block_found = true;
char doublehash2[128];
memset(doublehash2, 0, 128);
YAAMP_HASH_FUNCTION merkle_hash = sha256_double_hash_hex;
//if (g_current_algo->merkle_func)
// merkle_hash = g_current_algo->merkle_func;
merkle_hash((char *)submitvalues->header_bin, doublehash2, strlen(submitvalues->header_be)/2);
char hash1[1024];
memset(hash1, 0, 1024);
string_be(doublehash2, hash1);
block_add(client->userid, coind->id, templ->height,
target_to_diff(coin_target), target_to_diff(hash_int),
hash1, submitvalues->hash_be);
#ifdef HASH_DEBUGLOG_
debuglog("--------------------------------------------------------------\n");
debuglog("hash1 %s\n", hash1);
debuglog("hash2 %s\n", submitvalues->hash_be);
#endif
}
else
debuglog("%s %d rejected\n", coind->name, templ->height);
}
free(block_hex);
}
bool dump_submit_debug(const char *title, YAAMP_CLIENT *client, YAAMP_JOB *job, char *extranonce2, char *ntime, char *nonce)
{
debuglog("ERROR %s, %s subs %d, job %x, %s, id %x, %d, %s, %s %s\n",
title, client->sock->ip, client->extranonce_subscribe, job? job->id: 0, client->extranonce1,
client->extranonce1_id, client->extranonce2size, extranonce2, ntime, nonce);
}
void client_submit_error(YAAMP_CLIENT *client, YAAMP_JOB *job, int id, const char *message, char *extranonce2, char *ntime, char *nonce)
{
// if(job->templ->created+2 > time(NULL))
if(job && job->deleted)
client_send_result(client, "true");
else
{
client_send_error(client, id, message);
share_add(client, job, false, extranonce2, ntime, nonce, id);
client->submit_bad++;
#ifdef HASH_DEBUGLOG_
dump_submit_debug(message, client, job, extranonce2, ntime, nonce);
#endif
}
object_unlock(job);
}
bool client_submit(YAAMP_CLIENT *client, json_value *json_params)
{
// submit(worker_name, jobid, extranonce2, ntime, nonce):
if(json_params->u.array.length<5)
{
debuglog("%s - %s bad message\n", client->username, client->sock->ip);
client->submit_bad++;
return false;
}
char extranonce2[32];
char ntime[32];
char nonce[32];
memset(extranonce2, 0, 32);
memset(ntime, 0, 32);
memset(nonce, 0, 32);
int jobid = htoi(json_params->u.array.values[1]->u.string.ptr);
strncpy(extranonce2, json_params->u.array.values[2]->u.string.ptr, 31);
strncpy(ntime, json_params->u.array.values[3]->u.string.ptr, 31);
strncpy(nonce, json_params->u.array.values[4]->u.string.ptr, 31);
#ifdef HASH_DEBUGLOG_
debuglog("submit %s %d, %s, %s, %s\n", client->sock->ip, jobid, extranonce2, ntime, nonce);
#endif
string_lower(extranonce2);
string_lower(ntime);
string_lower(nonce);
YAAMP_JOB *job = (YAAMP_JOB *)object_find(&g_list_job, jobid, true);
if(!job)
{
client_submit_error(client, NULL, 21, "Invalid job id", extranonce2, ntime, nonce);
return true;
}
if(job->deleted)
{
client_send_result(client, "true");
object_unlock(job);
return true;
}
YAAMP_JOB_TEMPLATE *templ = job->templ;
if(strlen(nonce) != YAAMP_NONCE_SIZE*2)
{
client_submit_error(client, job, 20, "Invalid nonce size", extranonce2, ntime, nonce);
return true;
}
// if(strcmp(ntime, templ->ntime))
// {
// client_submit_error(client, job, 23, "Invalid time rolling", extranonce2, ntime, nonce);
// return true;
// }
YAAMP_SHARE *share = share_find(job->id, extranonce2, ntime, nonce, client->extranonce1);
if(share)
{
client_submit_error(client, job, 22, "Duplicate share", extranonce2, ntime, nonce);
return true;
}
if(strlen(extranonce2) != client->extranonce2size*2)
{
client_submit_error(client, job, 24, "Invalid extranonce2 size", extranonce2, ntime, nonce);
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////
YAAMP_JOB_VALUES submitvalues;
memset(&submitvalues, 0, sizeof(submitvalues));
build_submit_values(&submitvalues, templ, client->extranonce1, extranonce2, ntime, nonce);
// minimum hash diff begins with 0000, for all...
uint8_t pfx = submitvalues.hash_bin[30] | submitvalues.hash_bin[31];
if(pfx) {
#ifdef HASH_DEBUGLOG_
debuglog("Possible %s error, hash starts with %02x%02x%02x%02x\n", g_current_algo->name,
(int) submitvalues.hash_bin[31], (int) submitvalues.hash_bin[30],
(int) submitvalues.hash_bin[29], (int) submitvalues.hash_bin[28]);
#endif
client_submit_error(client, job, 25, "Invalid share", extranonce2, ntime, nonce);
return true;
}
uint64_t hash_int = get_hash_difficulty(submitvalues.hash_bin);
uint64_t user_target = diff_to_target(client->difficulty_actual);
uint64_t coin_target = decode_compact(templ->nbits);
#ifdef HASH_DEBUGLOG_
debuglog("%016llx actual\n", hash_int);
debuglog("%016llx target\n", user_target);
debuglog("%016llx coin\n", coin_target);
#endif
if(hash_int > user_target && hash_int > coin_target)
{
client_submit_error(client, job, 26, "Low difficulty share", extranonce2, ntime, nonce);
return true;
}
if(job->coind)
client_do_submit(client, job, &submitvalues, extranonce2, ntime, nonce);
else
remote_submit(client, job, &submitvalues, extranonce2, ntime, nonce);
client_send_result(client, "true");
client_record_difficulty(client);
client->submit_bad = 0;
share_add(client, job, true, extranonce2, ntime, nonce, 0);
object_unlock(job);
return true;
}