From c7de2c0795b4a05678f910b3f65c99f4708b5246 Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Wed, 7 Jun 2023 22:00:49 -0400 Subject: [PATCH] bm1397: add multiple midstates using version mask Job packets can have more that 1 midstate. If the stratum supports version-rolling, calculate additional midstates by incrementing the version with the version_mask provided by the stratum server. Currently does 4 midstates for each extranonce2. --- components/bm1397/include/bm1397.h | 3 ++ components/bm1397/test/test_job_command.c | 15 ++++-- components/stratum/include/mining.h | 11 +++- components/stratum/include/stratum_api.h | 4 +- components/stratum/mining.c | 51 +++++++++++++++++-- components/stratum/stratum_api.c | 7 +-- components/stratum/test/test_mining.c | 25 ++++++++-- main/global_state.h | 3 +- main/miner.c | 4 +- main/tasks/asic_task.c | 61 ++++++++++++++--------- main/tasks/create_jobs_task.c | 5 +- main/tasks/stratum_task.c | 2 +- 12 files changed, 138 insertions(+), 53 deletions(-) diff --git a/components/bm1397/include/bm1397.h b/components/bm1397/include/bm1397.h index 5bba1d62..fc334752 100644 --- a/components/bm1397/include/bm1397.h +++ b/components/bm1397/include/bm1397.h @@ -52,6 +52,9 @@ struct __attribute__((__packed__)) job_packet { uint8_t ntime[4]; uint8_t merkle4[4]; uint8_t midstate[32]; + uint8_t midstate1[32]; + uint8_t midstate2[32]; + uint8_t midstate3[32]; }; struct __attribute__((__packed__)) nonce_response { diff --git a/components/bm1397/test/test_job_command.c b/components/bm1397/test/test_job_command.c index 3d10b252..4bbf3484 100644 --- a/components/bm1397/test/test_job_command.c +++ b/components/bm1397/test/test_job_command.c @@ -21,17 +21,20 @@ TEST_CASE("Check known working midstate + job command", "[bm1397]") } - uint8_t work1[50] = { - 0xA3, // job id - 0x01, // number of midstates + uint8_t work1[146] = { + 0x18, // job id + 0x04, // number of midstates 0x9B, 0x04, 0x4C, 0x0A, // starting nonce 0x3A, 0xAE, 0x05, 0x17, // nbits 0xA0, 0x84, 0x73, 0x64, // ntime 0x50, 0xE3, 0x71, 0x61, // merkle 4 - 0x7E, 0x02, 0x70, 0x35, 0xB1, 0xAC, 0xBA, 0xF2, 0x3E, 0xA0, 0x1A, 0x52, 0x73, 0x44, 0xFA, 0xF7, 0x6A, 0xB4, 0x76, 0xD3, 0x28, 0x21, 0x61, 0x18, 0xB7, 0x76, 0x0F, 0x7B, 0x1B, 0x22, 0xD2, 0x29 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7E, 0x02, 0x70, 0x35, 0xB1, 0xAC, 0xBA, 0xF2, 0x3E, 0xA0, 0x1A, 0x52, 0x73, 0x44, 0xFA, 0xF7, 0x6A, 0xB4, 0x76, 0xD3, 0x28, 0x21, 0x61, 0x18, 0xB7, 0x76, 0x0F, 0x7B, 0x1B, 0x22, 0xD2, 0x29, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; struct job_packet test_job; - memcpy((uint8_t *) &test_job, work1, 50); + memcpy((uint8_t *) &test_job, work1, 146); uint8_t buf[1024]; memset(buf, 0, 1024); @@ -51,4 +54,6 @@ TEST_CASE("Check known working midstate + job command", "[bm1397]") memcpy((void *) &nonce, buf + i, sizeof(struct nonce_response)); // expected nonce 9B 04 4C 0A TEST_ASSERT_EQUAL_UINT32(0x0a4c049b, nonce.nonce); + TEST_ASSERT_EQUAL_UINT8(0x18, nonce.job_id & 0xfc); + TEST_ASSERT_EQUAL_UINT8(2, nonce.job_id & 0x03); } \ No newline at end of file diff --git a/components/stratum/include/mining.h b/components/stratum/include/mining.h index 72236f25..787afb78 100644 --- a/components/stratum/include/mining.h +++ b/components/stratum/include/mining.h @@ -6,13 +6,18 @@ typedef struct { uint32_t version; + uint32_t version_mask; uint8_t prev_block_hash[32]; uint8_t merkle_root[32]; uint32_t ntime; uint32_t target; // aka difficulty, aka nbits uint32_t starting_nonce; + uint8_t num_midstates; uint8_t midstate[32]; + uint8_t midstate1[32]; + uint8_t midstate2[32]; + uint8_t midstate3[32]; uint32_t pool_diff; char * jobid; char * extranonce2; @@ -25,10 +30,12 @@ char * construct_coinbase_tx(const char * coinbase_1, const char * coinbase_2, char * calculate_merkle_root_hash(const char * coinbase_tx, const uint8_t merkle_branches[][32], const int num_merkle_branches); -bm_job construct_bm_job(mining_notify * params, const char * merkle_root); +bm_job construct_bm_job(mining_notify * params, const char * merkle_root, const uint32_t version_mask); -double test_nonce_value(bm_job * job, uint32_t nonce); +double test_nonce_value(const bm_job * job, const uint32_t nonce, const uint8_t midstate_index); char * extranonce_2_generate(uint32_t extranonce_2, uint32_t length); +uint32_t increment_bitmask(const uint32_t value, const uint32_t mask); + #endif // MINING_H \ No newline at end of file diff --git a/components/stratum/include/stratum_api.h b/components/stratum/include/stratum_api.h index be273a02..d079e14e 100644 --- a/components/stratum/include/stratum_api.h +++ b/components/stratum/include/stratum_api.h @@ -27,6 +27,7 @@ typedef struct { uint8_t * merkle_branches; size_t n_merkle_branches; uint32_t version; + uint32_t version_mask; uint32_t target; uint32_t ntime; uint32_t difficulty; @@ -69,7 +70,8 @@ void STRATUM_V1_configure_version_rolling(int socket); int STRATUM_V1_suggest_difficulty(int socket, uint32_t difficulty); void STRATUM_V1_submit_share(int socket, const char * username, const char * jobid, - const char * extranonce_2, const uint32_t ntime, const uint32_t nonce); + const char * extranonce_2, const uint32_t ntime, const uint32_t nonce, + const uint32_t version); #endif // STRATUM_API_H \ No newline at end of file diff --git a/components/stratum/mining.c b/components/stratum/mining.c index 014085a5..92e9fb84 100644 --- a/components/stratum/mining.c +++ b/components/stratum/mining.c @@ -53,7 +53,7 @@ char * calculate_merkle_root_hash(const char * coinbase_tx, const uint8_t merkle } //take a mining_notify struct with ascii hex strings and convert it to a bm_job struct -bm_job construct_bm_job(mining_notify * params, const char * merkle_root) { +bm_job construct_bm_job(mining_notify * params, const char * merkle_root, const uint32_t version_mask) { bm_job new_job; new_job.version = params->version; @@ -76,6 +76,26 @@ bm_job construct_bm_job(mining_notify * params, const char * merkle_root) { midstate_sha256_bin(midstate_data, 64, new_job.midstate); //make the midstate hash reverse_bytes(new_job.midstate, 32); //reverse the midstate bytes for the BM job packet + if (version_mask != 0) { + uint32_t rolled_version = increment_bitmask(new_job.version, version_mask); + memcpy(midstate_data, &rolled_version, 4); + midstate_sha256_bin(midstate_data, 64, new_job.midstate1); + reverse_bytes(new_job.midstate1, 32); + + rolled_version = increment_bitmask(rolled_version, version_mask); + memcpy(midstate_data, &rolled_version, 4); + midstate_sha256_bin(midstate_data, 64, new_job.midstate2); + reverse_bytes(new_job.midstate2, 32); + + rolled_version = increment_bitmask(rolled_version, version_mask); + memcpy(midstate_data, &rolled_version, 4); + midstate_sha256_bin(midstate_data, 64, new_job.midstate3); + reverse_bytes(new_job.midstate3, 32); + new_job.num_midstates = 4; + } else { + new_job.num_midstates = 1; + } + return new_job; } @@ -97,14 +117,18 @@ char * extranonce_2_generate(uint32_t extranonce_2, uint32_t length) static const double truediffone = 26959535291011309493156476344723991336010898738574164086137773096960.0; /* testing a nonce and return the diff - 0 means invalid */ -double test_nonce_value(bm_job * job, uint32_t nonce) { +double test_nonce_value(const bm_job * job, const uint32_t nonce, const uint8_t midstate_index) { double d64, s64, ds; unsigned char header[80]; - //TODO: use the midstate hash instead of hashing the whole header + // TODO: use the midstate hash instead of hashing the whole header + uint32_t rolled_version = job->version; + for (int i = 0; i < midstate_index; i++) { + rolled_version = increment_bitmask(rolled_version, job->version_mask); + } - //copy data from job to header - memcpy(header, &job->version, 4); + // copy data from job to header + memcpy(header, &rolled_version, 4); memcpy(header + 4, job->prev_block_hash, 32); memcpy(header + 36, job->merkle_root, 32); memcpy(header + 68, &job->ntime, 4); @@ -124,3 +148,20 @@ double test_nonce_value(bm_job * job, uint32_t nonce) { return ds; } + +uint32_t increment_bitmask(const uint32_t value, const uint32_t mask) { + // if mask is zero, just return the original value + if (mask == 0) return value; + + uint32_t carry = (value & mask) + (mask & -mask); // increment the least significant bit of the mask + uint32_t overflow = carry & ~mask; // find overflowed bits that are not in the mask + uint32_t new_value = (value & ~mask) | (carry & mask); // set bits according to the mask + + // Handle carry propagation + if (overflow > 0) { + uint32_t carry_mask = (overflow << 1); // shift left to get the mask where carry should be propagated + new_value = increment_bitmask(new_value, carry_mask); // recursively handle carry propagation + } + + return new_value; +} diff --git a/components/stratum/stratum_api.c b/components/stratum/stratum_api.c index d382b6f4..23cb70c8 100644 --- a/components/stratum/stratum_api.c +++ b/components/stratum/stratum_api.c @@ -305,11 +305,12 @@ int STRATUM_V1_authenticate(int socket, const char * username) /// @param extranonce_2 The hex-encoded value of extra nonce 2. /// @param nonce The hex-encoded nonce value to use in the block header. void STRATUM_V1_submit_share(int socket, const char * username, const char * jobid, - const char * extranonce_2, const uint32_t ntime, const uint32_t nonce) + const char * extranonce_2, const uint32_t ntime, const uint32_t nonce, + const uint32_t version) { char submit_msg[BUFFER_SIZE]; - sprintf(submit_msg, "{\"id\": %d, \"method\": \"mining.submit\", \"params\": [\"%s\", \"%s\", \"%s\", \"%08x\", \"%08x\"]}\n", - send_uid++, username, jobid, extranonce_2, ntime, nonce); + sprintf(submit_msg, "{\"id\": %d, \"method\": \"mining.submit\", \"params\": [\"%s\", \"%s\", \"%s\", \"%08x\", \"%08x\", \"%08x\"]}\n", + send_uid++, username, jobid, extranonce_2, ntime, nonce, version); debug_stratum_tx(submit_msg); write(socket, submit_msg, strlen(submit_msg)); diff --git a/components/stratum/test/test_mining.c b/components/stratum/test/test_mining.c index c4b323d6..137e801e 100644 --- a/components/stratum/test/test_mining.c +++ b/components/stratum/test/test_mining.c @@ -66,7 +66,7 @@ TEST_CASE("Validate bm job construction", "[mining]") notify_message.target = 0x1705dd01; notify_message.ntime = 0x64658bd8; const char * merkle_root = "cd1be82132ef0d12053dcece1fa0247fcfdb61d4dbd3eb32ea9ef9b4c604a846"; - bm_job job = construct_bm_job(¬ify_message, merkle_root); + bm_job job = construct_bm_job(¬ify_message, merkle_root, 0); uint8_t expected_midstate_bin[32]; hex2bin("91DFEA528A9F73683D0D495DD6DD7415E1CA21CB411759E3E05D7D5FF285314D", expected_midstate_bin, 32); @@ -75,6 +75,21 @@ TEST_CASE("Validate bm job construction", "[mining]") TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_midstate_bin, job.midstate, 32); } +TEST_CASE("Validate version mask incrementing", "[mining]") +{ + uint32_t version = 0x20000004; + uint32_t version_mask = 0x00ffff00; + + uint32_t rolled_version = increment_bitmask(version, version_mask); + TEST_ASSERT_EQUAL_UINT32(0x20000104, rolled_version); + rolled_version = increment_bitmask(rolled_version, version_mask); + TEST_ASSERT_EQUAL_UINT32(0x20000204, rolled_version); + rolled_version = increment_bitmask(rolled_version, version_mask); + TEST_ASSERT_EQUAL_UINT32(0x20000304, rolled_version); + rolled_version = increment_bitmask(rolled_version, version_mask); + TEST_ASSERT_EQUAL_UINT32(0x20000404, rolled_version); +} + // Values calculated from esp-miner/components/stratum/test/verifiers/bm1397.py // TEST_CASE("Validate bm job construction 2", "[mining]") // { @@ -131,10 +146,10 @@ TEST_CASE("Test nonce diff checking", "[mining test_nonce]") notify_message.target = 0x1705ae3a; notify_message.ntime = 0x646ff1a9; const char * merkle_root = "6d0359c451434605c52a5a9ce074340be47c2c63840731f9edf1db3f26b1cdd9a9f16f64"; - bm_job job = construct_bm_job(¬ify_message, merkle_root); + bm_job job = construct_bm_job(¬ify_message, merkle_root, 0); uint32_t nonce = 0x276E8947; - double diff = test_nonce_value(&job, nonce); + double diff = test_nonce_value(&job, nonce, 0); TEST_ASSERT_EQUAL_INT(18, (int) diff); } @@ -167,9 +182,9 @@ TEST_CASE("Test nonce diff checking 2", "[mining test_nonce]") char * merkle_root = calculate_merkle_root_hash(coinbase_tx, merkles, num_merkles); TEST_ASSERT_EQUAL_STRING("5bdc1968499c3393873edf8e07a1c3a50a97fc3a9d1a376bbf77087dd63778eb", merkle_root); - bm_job job = construct_bm_job(¬ify_message, merkle_root); + bm_job job = construct_bm_job(¬ify_message, merkle_root, 0); uint32_t nonce = 0x0a029ed1; - double diff = test_nonce_value(&job, nonce); + double diff = test_nonce_value(&job, nonce, 0); TEST_ASSERT_EQUAL_INT(683, (int) diff); } diff --git a/main/global_state.h b/main/global_state.h index ba91f2f4..6d6938fe 100644 --- a/main/global_state.h +++ b/main/global_state.h @@ -27,12 +27,11 @@ typedef struct { pthread_mutex_t valid_jobs_lock; uint32_t stratum_difficulty; + uint32_t version_mask; int sock; } GlobalState; - - #endif /* GLOBAL_STATE_H_ */ \ No newline at end of file diff --git a/main/miner.c b/main/miner.c index 6ff4ebb3..884eed5d 100755 --- a/main/miner.c +++ b/main/miner.c @@ -15,7 +15,8 @@ static GlobalState GLOBAL_STATE = { .extranonce_str = NULL, .extranonce_2_len = 0, - .abandon_work = 0 + .abandon_work = 0, + .version_mask = 0 }; @@ -40,7 +41,6 @@ void app_main(void) queue_init(&GLOBAL_STATE.stratum_queue); queue_init(&GLOBAL_STATE.ASIC_jobs_queue); - SERIAL_init(); BM1397_init(); diff --git a/main/tasks/asic_task.c b/main/tasks/asic_task.c index 4c4d977b..f5673a7a 100644 --- a/main/tasks/asic_task.c +++ b/main/tasks/asic_task.c @@ -7,7 +7,7 @@ static const char *TAG = "ASIC_task"; -// static bm_job ** active_jobs; is required to keep track of the active jobs since the +// static bm_job ** active_jobs; is required to keep track of the active jobs since the // ASIC may not return the nonce in the same order as the jobs were sent // it also may return a previous nonce under some circumstances // so we keep a list of jobs indexed by the job id @@ -16,8 +16,7 @@ static bm_job ** active_jobs; void ASIC_task(void * pvParameters) { - GlobalState *GLOBAL_STATE = (GlobalState*)pvParameters; - + GlobalState *GLOBAL_STATE = (GlobalState*) pvParameters; uint8_t buf[CHUNK_SIZE]; memset(buf, 0, 1024); @@ -50,23 +49,31 @@ void ASIC_task(void * pvParameters) struct job_packet job; // max job number is 128 - // there is still some really weird logic with the job id bits for the asic to sort out + // there is still some really weird logic with the job id bits for the asic to sort out // so we have it limited to 128 and it has to increment by 4 id = (id + 4) % 128; + job.job_id = id; - job.num_midstates = 1; + job.num_midstates = next_bm_job->num_midstates; memcpy(&job.starting_nonce, &next_bm_job->starting_nonce, 4); memcpy(&job.nbits, &next_bm_job->target, 4); memcpy(&job.ntime, &next_bm_job->ntime, 4); memcpy(&job.merkle4, next_bm_job->merkle_root + 28, 4); memcpy(job.midstate, next_bm_job->midstate, 32); + if (job.num_midstates == 4) + { + memcpy(job.midstate1, next_bm_job->midstate1, 32); + memcpy(job.midstate2, next_bm_job->midstate2, 32); + memcpy(job.midstate3, next_bm_job->midstate3, 32); + } + if (active_jobs[job.job_id] != NULL) { free_bm_job(active_jobs[job.job_id]); } - + active_jobs[job.job_id] = next_bm_job; - + pthread_mutex_lock(&GLOBAL_STATE->valid_jobs_lock); GLOBAL_STATE-> valid_jobs[job.job_id] = 1; pthread_mutex_unlock(&GLOBAL_STATE->valid_jobs_lock); @@ -84,7 +91,7 @@ void ASIC_task(void * pvParameters) // Didn't find a solution, restart and try again continue; } - + if(received != 9 || buf[0] != 0xAA || buf[1] != 0x55){ ESP_LOGI(TAG, "Serial RX invalid %i", received); ESP_LOG_BUFFER_HEX(TAG, buf, received); @@ -97,11 +104,13 @@ void ASIC_task(void * pvParameters) struct nonce_response nonce; memcpy((void *) &nonce, buf, sizeof(struct nonce_response)); - if (GLOBAL_STATE->valid_jobs[nonce.job_id] == 0) { - ESP_LOGI(TAG, "Invalid job nonce found"); + uint8_t rx_job_id = nonce.job_id & 0xfc; + uint8_t rx_midstate_index = nonce.job_id & 0x03; + + if (GLOBAL_STATE->valid_jobs[rx_job_id] == 0) { + ESP_LOGI(TAG, "Invalid job nonce found, id=%d", nonce.job_id); } - // ASIC may return the same nonce multiple times // or one that was already found // most of the time it behavies however @@ -120,24 +129,28 @@ void ASIC_task(void * pvParameters) } // check the nonce difficulty - double nonce_diff = test_nonce_value(active_jobs[nonce.job_id], nonce.nonce); + double nonce_diff = test_nonce_value(active_jobs[rx_job_id], nonce.nonce, rx_midstate_index); - ESP_LOGI(TAG, "Nonce difficulty %.2f of %d.", nonce_diff, active_jobs[nonce.job_id]->pool_diff); - - if (nonce_diff > active_jobs[nonce.job_id]->pool_diff) + ESP_LOGI(TAG, "Nonce difficulty %.2f of %d.", nonce_diff, active_jobs[rx_job_id]->pool_diff); + + if (nonce_diff > active_jobs[rx_job_id]->pool_diff) { - SYSTEM_notify_found_nonce(&GLOBAL_STATE->SYSTEM_MODULE, active_jobs[nonce.job_id]->pool_diff, nonce_diff, next_bm_job->target); + SYSTEM_notify_found_nonce(&GLOBAL_STATE->SYSTEM_MODULE, active_jobs[rx_job_id]->pool_diff, nonce_diff, next_bm_job->target); + + uint32_t rolled_version = active_jobs[rx_job_id]->version; + for (int i = 0; i < rx_midstate_index; i++) { + rolled_version = increment_bitmask(rolled_version, active_jobs[rx_job_id]->version_mask); + } STRATUM_V1_submit_share( - GLOBAL_STATE->sock, - STRATUM_USER, - active_jobs[nonce.job_id]->jobid, - active_jobs[nonce.job_id]->extranonce2, - active_jobs[nonce.job_id]->ntime, - nonce.nonce + GLOBAL_STATE->sock, + STRATUM_USER, + active_jobs[rx_job_id]->jobid, + active_jobs[rx_job_id]->extranonce2, + active_jobs[rx_job_id]->ntime, + nonce.nonce, + rolled_version ^ active_jobs[rx_job_id]->version ); - } - } } diff --git a/main/tasks/create_jobs_task.c b/main/tasks/create_jobs_task.c index 61b13679..ba0316a6 100644 --- a/main/tasks/create_jobs_task.c +++ b/main/tasks/create_jobs_task.c @@ -23,7 +23,6 @@ void create_jobs_task(void * pvParameters) uint32_t extranonce_2 = 0; while (extranonce_2 < UINT_MAX && GLOBAL_STATE->abandon_work == 0) { - struct timeval tv; gettimeofday(&tv, NULL); int job_time_sec = tv.tv_sec - mining_notification->ntime; @@ -38,16 +37,16 @@ void create_jobs_task(void * pvParameters) char * extranonce_2_str = extranonce_2_generate(extranonce_2, GLOBAL_STATE->extranonce_2_len); - char *coinbase_tx = construct_coinbase_tx(mining_notification->coinbase_1, mining_notification->coinbase_2, GLOBAL_STATE->extranonce_str, extranonce_2_str); char *merkle_root = calculate_merkle_root_hash(coinbase_tx, (uint8_t(*)[32])mining_notification->merkle_branches, mining_notification->n_merkle_branches); - bm_job next_job = construct_bm_job(mining_notification, merkle_root); + bm_job next_job = construct_bm_job(mining_notification, merkle_root, GLOBAL_STATE->version_mask); bm_job * queued_next_job = malloc(sizeof(bm_job)); memcpy(queued_next_job, &next_job, sizeof(bm_job)); queued_next_job->extranonce2 = strdup(extranonce_2_str); queued_next_job->jobid = strdup(mining_notification->job_id); + queued_next_job->version_mask = GLOBAL_STATE->version_mask; queue_enqueue(&GLOBAL_STATE->ASIC_jobs_queue, queued_next_job); diff --git a/main/tasks/stratum_task.c b/main/tasks/stratum_task.c index eb9e7739..67cd88ad 100644 --- a/main/tasks/stratum_task.c +++ b/main/tasks/stratum_task.c @@ -113,7 +113,7 @@ void stratum_task(void * pvParameters) } pthread_mutex_unlock(&GLOBAL_STATE->valid_jobs_lock); } - if ( GLOBAL_STATE->stratum_queue.count == QUEUE_SIZE) { + if (GLOBAL_STATE->stratum_queue.count == QUEUE_SIZE) { mining_notify * next_notify_json_str = (mining_notify *) queue_dequeue(&GLOBAL_STATE->stratum_queue); STRATUM_V1_free_mining_notify(next_notify_json_str); }