From 0de7b56373b7e2c5b4145c2c7a3897c3754ace41 Mon Sep 17 00:00:00 2001 From: edtubbs Date: Tue, 12 Dec 2023 13:56:54 -0600 Subject: [PATCH] headersdb_file: updated dogecoin_headers_db_connect_hdr to reorg arith_uint256: added negate, arithmetic and comparisions from xanimo arith_uint256: updated div_arith_uint256 for long division arith_uint256: updated get_low62 byte order block: updated dogecoin_block_header with chainwork block: updated dogecoin_block_header_copy to copy chainwork block: updated dogecoin_block_header_new to initialize chainwork block: updated dogecoin_block_header_new to initialize auxpow flag block: removed duplicate initialization from dogecoin_auxpow_block_new headersdb_file: updated dogecoin_headers_db_free check for genesis pow: added nhashes_done to check_pow for chainwork validation: updated check_pow for chainwork spv: added escape characters to dogecoin_net_spv_post_cmd for shutdown spvnode: moved "done" print after dogecoin_spv_client_free test: updated block_test to check chainwork of genesis test: added test_reorg to spv_test test: updated unittester to test_reorg --- include/dogecoin/arith_uint256.h | 12 +- include/dogecoin/block.h | 1 + include/dogecoin/pow.h | 2 +- src/arith_uint256.c | 123 ++++++++++++++++- src/block.c | 6 +- src/cli/spvnode.c | 1 + src/headersdb_file.c | 136 ++++++++++++++---- src/pow.c | 43 +++++- src/spv.c | 29 +++- src/validation.c | 4 +- test/block_tests.c | 39 +++++- test/spv_tests.c | 229 +++++++++++++++++++++++++++++++ test/unittester.c | 2 + 13 files changed, 582 insertions(+), 45 deletions(-) diff --git a/include/dogecoin/arith_uint256.h b/include/dogecoin/arith_uint256.h index 06462eee5..c48b2017b 100644 --- a/include/dogecoin/arith_uint256.h +++ b/include/dogecoin/arith_uint256.h @@ -51,11 +51,21 @@ typedef struct base_uint_ { typedef base_uint_ arith_uint256; +void arith_negate(arith_uint256* input); arith_uint256* init_arith_uint256(); arith_uint256* set_compact(arith_uint256* hash, uint32_t compact, dogecoin_bool *pf_negative, dogecoin_bool *pf_overflow); uint8_t* arith_to_uint256(const arith_uint256* a); arith_uint256* uint_to_arith(const uint256* a); -uint64_t get_low64(arith_uint256 pn); +uint64_t get_low64(arith_uint256* a); +arith_uint256* div_arith_uint256(arith_uint256* a, arith_uint256* b); +arith_uint256* add_arith_uint256(arith_uint256* a, arith_uint256* b); +arith_uint256* sub_arith_uint256(arith_uint256* a, arith_uint256* b); +dogecoin_bool arith_uint256_is_zero(const arith_uint256* a); +dogecoin_bool arith_uint256_equal(const arith_uint256* a, const arith_uint256* b); +dogecoin_bool arith_uint256_greater_than(const arith_uint256* a, const arith_uint256* b); +dogecoin_bool arith_uint256_greater_than_or_equal(const arith_uint256* a, const arith_uint256* b); +dogecoin_bool arith_uint256_less_than(const arith_uint256* a, const arith_uint256* b); +dogecoin_bool arith_uint256_less_than_or_equal(const arith_uint256* a, const arith_uint256* b); LIBDOGECOIN_END_DECL diff --git a/include/dogecoin/block.h b/include/dogecoin/block.h index b31caf6a2..07c20c9d5 100644 --- a/include/dogecoin/block.h +++ b/include/dogecoin/block.h @@ -55,6 +55,7 @@ typedef struct dogecoin_block_header_ { uint32_t bits; uint32_t nonce; auxpow auxpow[1]; + uint256 chainwork; } dogecoin_block_header; typedef struct dogecoin_auxpow_block_ { diff --git a/include/dogecoin/pow.h b/include/dogecoin/pow.h index 6dbf00fce..3508985a9 100644 --- a/include/dogecoin/pow.h +++ b/include/dogecoin/pow.h @@ -39,7 +39,7 @@ LIBDOGECOIN_BEGIN_DECL -dogecoin_bool check_pow(uint256* hash, unsigned int nbits, const dogecoin_chainparams *params); +dogecoin_bool check_pow(uint256* hash, unsigned int nbits, const dogecoin_chainparams *params, uint256* nhashes_done); LIBDOGECOIN_END_DECL diff --git a/src/arith_uint256.c b/src/arith_uint256.c index 7d725b833..b9bc77fed 100644 --- a/src/arith_uint256.c +++ b/src/arith_uint256.c @@ -25,6 +25,13 @@ #include +void arith_negate(arith_uint256* input) { + // Inverting all bits (one's complement) + for (int i = 0; i < WIDTH; i++) { + input->pn[i] = ~input->pn[i]; + } +} + arith_uint256* init_arith_uint256() { arith_uint256* x = dogecoin_calloc(8, sizeof(uint32_t)); int i = 0; @@ -95,17 +102,119 @@ arith_uint256* set_compact(arith_uint256* hash, uint32_t compact, dogecoin_bool arith_uint256* uint_to_arith(const uint256* a) { - static arith_uint256 b; - memcpy_safe(b.pn, a, sizeof(b.pn)); - return &b; + arith_uint256* b = (arith_uint256*)calloc(1, sizeof(arith_uint256)); + if (!b) { + return NULL; + } + memcpy_safe(b->pn, a, sizeof(b->pn)); + return b; } uint8_t* arith_to_uint256(const arith_uint256* a) { - static uint256 b = {0}; + uint8_t* b = (uint8_t*)calloc(1, sizeof(uint256)); + if (!b) { + return NULL; + } memcpy_safe(b, a->pn, sizeof(uint256)); - return &b[0]; + return b; +} + +arith_uint256* div_arith_uint256(arith_uint256* a, arith_uint256* b) { + if (arith_uint256_is_zero(b)) { + // Handle division by zero if necessary + return NULL; + } + + arith_uint256* quotient = init_arith_uint256(); + arith_uint256* remainder = init_arith_uint256(); + + for (int i = WIDTH * 32 - 1; i >= 0; i--) { + // Left shift remainder by 1 bit + arith_shift_left(remainder, 1); + + // Set the least significant bit of remainder to bit i of a + int word_idx = i / 32; + int bit_idx = i % 32; + if ((a->pn[word_idx] & (1 << bit_idx)) != 0) { + remainder->pn[0] |= 1; + } + + // Compare remainder with b + if (arith_uint256_greater_than_or_equal(remainder, b)) { + // Subtract b from remainder + arith_uint256* temp = sub_arith_uint256(remainder, b); + memcpy(remainder, temp, sizeof(arith_uint256)); + free(temp); + + // Set bit i of quotient + quotient->pn[word_idx] |= (1 << bit_idx); + } + } + + free(remainder); + return quotient; +} + +arith_uint256* add_arith_uint256(arith_uint256* a, arith_uint256* b) { + arith_uint256* result = init_arith_uint256(); + uint64_t carry = 0; + for (int i = WIDTH - 1; i >= 0; i--) { + uint64_t sum = (uint64_t)a->pn[i] + b->pn[i] + carry; + result->pn[i] = sum; // This will only keep the lower 32 bits + carry = sum >> 32; // This will keep the upper 32 bits (carry) + } + return result; +} + +arith_uint256* sub_arith_uint256(arith_uint256* a, arith_uint256* b) { + arith_uint256* result = init_arith_uint256(); + int64_t carry = 0; + for (int i = WIDTH - 1; i >= 0; i--) { + int64_t diff = (uint64_t)a->pn[i] - b->pn[i] - carry; + result->pn[i] = diff; // This will only keep the lower 32 bits + carry = (diff < 0) ? 1 : 0; // If diff is negative, there's a borrow + } + return result; +} + +dogecoin_bool arith_uint256_is_zero(const arith_uint256* a) { + for (int i = 0; i < WIDTH; i++) { + if (a->pn[i] != 0) return false; + } + return true; +} + +dogecoin_bool arith_uint256_less_than(const arith_uint256* a, const arith_uint256* b) { + for (int i = WIDTH - 1; i >= 0; i--) { + if (a->pn[i] < b->pn[i]) return true; + else if (a->pn[i] > b->pn[i]) return false; + } + return false; +} + +dogecoin_bool arith_uint256_equal(const arith_uint256* a, const arith_uint256* b) { + for (int i = 0; i < WIDTH; i++) { + if (a->pn[i] != b->pn[i]) return false; + } + return true; +} + +dogecoin_bool arith_uint256_less_than_or_equal(const arith_uint256* a, const arith_uint256* b) { + return arith_uint256_less_than(a, b) || arith_uint256_equal(a, b); +} + +dogecoin_bool arith_uint256_greater_than(const arith_uint256* a, const arith_uint256* b) { + for (int i = 0; i < WIDTH; i++) { + if (a->pn[i] > b->pn[i]) return true; + else if (a->pn[i] < b->pn[i]) return false; + } + return false; +} + +dogecoin_bool arith_uint256_greater_than_or_equal(const arith_uint256* a, const arith_uint256* b) { + return !arith_uint256_less_than(a, b); } -uint64_t get_low64(arith_uint256 a) { - return a.pn[0] | (uint64_t)a.pn[1] << 32; +uint64_t get_low64(arith_uint256* a) { + return ((uint64_t)a->pn[WIDTH - 2]) | (((uint64_t)a->pn[WIDTH - 1]) << 32); } diff --git a/src/block.c b/src/block.c index fb0aaf3d0..57d1e7719 100644 --- a/src/block.c +++ b/src/block.c @@ -167,6 +167,8 @@ dogecoin_block_header* dogecoin_block_header_new() { header->nonce = 0; header->auxpow->check = check; header->auxpow->ctx = header; + header->auxpow->is = false; + dogecoin_mem_zero(&header->chainwork, DOGECOIN_HASH_LENGTH); return header; } @@ -187,7 +189,6 @@ dogecoin_auxpow_block* dogecoin_auxpow_block_new() { block->aux_merkle_branch = NULL; block->aux_merkle_index = 0; block->parent_header = dogecoin_block_header_new(); - block->header->auxpow->check = check; block->header->auxpow->ctx = block; return block; } @@ -332,6 +333,7 @@ int dogecoin_block_header_deserialize(dogecoin_block_header* header, struct cons printf("%s:%d:%s:%s\n", __FILE__, __LINE__, __func__, strerror(errno)); return false; } + dogecoin_block_header_copy(header, block->header); } dogecoin_auxpow_block_free(block); return true; @@ -470,6 +472,8 @@ void dogecoin_block_header_copy(dogecoin_block_header* dest, const dogecoin_bloc dest->nonce = src->nonce; dest->auxpow->check = src->auxpow->check; dest->auxpow->ctx = src->auxpow->ctx; + dest->auxpow->is = src->auxpow->is; + memcpy_safe(&dest->chainwork, &src->chainwork, sizeof(src->chainwork)); } /** diff --git a/src/cli/spvnode.c b/src/cli/spvnode.c index 87b15536b..d1c1c88ac 100644 --- a/src/cli/spvnode.c +++ b/src/cli/spvnode.c @@ -462,6 +462,7 @@ int main(int argc, char* argv[]) { printf("Connecting to the p2p network...\n"); dogecoin_spv_client_runloop(client); dogecoin_spv_client_free(client); + printf("done\n"); ret = EXIT_SUCCESS; #if WITH_WALLET dogecoin_wallet_free(wallet); diff --git a/src/headersdb_file.c b/src/headersdb_file.c index 1fc9db4bf..319c1d9b4 100644 --- a/src/headersdb_file.c +++ b/src/headersdb_file.c @@ -124,18 +124,16 @@ void dogecoin_headers_db_free(dogecoin_headers_db* db) { // Free all blockindex structures starting from chaintip to chainbottom if (db->chaintip) { dogecoin_blockindex *scan_tip = db->chaintip; - while (scan_tip && scan_tip != db->chainbottom) { + while (scan_tip->prev) { dogecoin_blockindex *prev = scan_tip->prev; dogecoin_free(scan_tip); scan_tip = prev; } -#ifndef __APPLE__ // If scan_tip is chainbottom, free it - if (scan_tip == db->chainbottom) { + if (scan_tip == db->chainbottom && scan_tip != &db->genesis) { dogecoin_free(scan_tip); db->chainbottom = NULL; } -#endif } db->chaintip = NULL; @@ -310,21 +308,21 @@ dogecoin_blockindex * dogecoin_headers_db_connect_hdr(dogecoin_headers_db* db, s // check if we know the prevblock fork_from_block = dogecoin_headersdb_find(db, blockindex->header.prev_block); if (fork_from_block) { - printf("Block found on a fork...\n"); connect_at = fork_from_block; } } if (connect_at != NULL) { - /* check claimed PoW */ + // Check the proof of work if (!is_auxpow(blockindex->header.version)) { uint256 hash = {0}; cstring* s = cstr_new_sz(64); - dogecoin_block_header_serialize(s, &blockindex->header); + dogecoin_block_header_serialize(s, (const dogecoin_block_header*) &blockindex->header); dogecoin_block_header_scrypt_hash(s, &hash); cstr_free(s, true); - if (!check_pow(&hash, blockindex->header.bits, db->params)) { + if (!check_pow(&hash, blockindex->header.bits, db->params, &blockindex->header.chainwork)) { printf("%s:%d:%s : non-AUX proof of work failed : %s\n", __FILE__, __LINE__, __func__, strerror(errno)); + dogecoin_free(blockindex); return false; } } @@ -332,23 +330,108 @@ dogecoin_blockindex * dogecoin_headers_db_connect_hdr(dogecoin_headers_db* db, s blockindex->prev = connect_at; blockindex->height = connect_at->height+1; - /* TODO: check if we should switch to the fork with most work (instead of height) */ - if (blockindex->height > db->chaintip->height) { - if (fork_from_block) { - /* TODO: walk back to the fork point and call reorg callback */ - printf("Switch to the fork!\n"); + if (blockindex->height >= db->chaintip->height) { + + arith_uint256* connect_at_chainwork = uint_to_arith((const uint256*) &connect_at->header.chainwork); + arith_uint256* blockindex_chainwork = uint_to_arith((const uint256*) &blockindex->header.chainwork); + arith_uint256* chaintip_chainwork = uint_to_arith((const uint256*) &db->chaintip->header.chainwork); + + arith_uint256* added_chainwork = add_arith_uint256(connect_at_chainwork, blockindex_chainwork); + uint8_t* final_chainwork = arith_to_uint256(added_chainwork); + + memcpy(blockindex->header.chainwork, final_chainwork, sizeof(blockindex->header.chainwork)); + + // Free the dynamically allocated memory + dogecoin_free(connect_at_chainwork); + dogecoin_free(blockindex_chainwork); + dogecoin_free(final_chainwork); + + // Chain reorganization if necessary + if (fork_from_block && arith_uint256_greater_than(added_chainwork, chaintip_chainwork)) { + + // Identify the common ancestor + dogecoin_blockindex* common_ancestor = connect_at; + dogecoin_blockindex* chain_tip = db->chaintip; + + // Find the common ancestor + while (common_ancestor && chain_tip && common_ancestor->height != chain_tip->height) { + if (common_ancestor->height > chain_tip->height) { + common_ancestor = common_ancestor->prev; + } else { + chain_tip = chain_tip->prev; + } + + // Break the loop if either reaches the start of the chain + if (!common_ancestor || !chain_tip) { + fprintf(stderr, "Unable to find common ancestor.\n"); + dogecoin_free(blockindex); + dogecoin_free(chaintip_chainwork); + dogecoin_free(added_chainwork); + return NULL; + } + } + + // Disconnect blocks from the current chain + while (memcmp(db->chaintip->hash, common_ancestor->hash, DOGECOIN_HASH_LENGTH) != 0) { + dogecoin_headersdb_disconnect_tip(db); + } + + // Connect blocks from the new chain + dogecoin_blockindex* current_block = blockindex; + while (memcmp(current_block->header.prev_block, common_ancestor->hash, DOGECOIN_HASH_LENGTH) != 0) { + // Find the block in the database that has the current block as its previous block + dogecoin_blockindex* next_block = dogecoin_headersdb_find(db, current_block->header.prev_block); + + if (!next_block) { + fprintf(stderr, "Next block in the new chain not found.\n"); + + // Add the block to the tree + if (db->use_binary_tree) { + dogecoin_btree_tsearch(blockindex, &db->tree_root, dogecoin_header_compare); + } + + // Free the dynamically allocated memory + dogecoin_free(chaintip_chainwork); + dogecoin_free(added_chainwork); + return NULL; + } + + // Set the next block as the new chain tip + db->chaintip = next_block; + current_block = next_block->prev; // Move to the previous block in the chain + } + + printf("Chain reorganization: %d blocks disconnected, %d blocks connected\n", blockindex->height - common_ancestor->height, blockindex->height - db->chaintip->height); + + // Set the new block as the new chain tip + db->chaintip = blockindex; + + // Set connected to true as the block is now part of the main chain + *connected = true; } - db->chaintip = blockindex; - } - if (!load_process && db->read_write_file) - { - if (!dogecoin_headers_db_write(db, blockindex)) { - fprintf(stderr, "Error writing blockheader to database\n"); + else if (load_process || arith_uint256_greater_than(added_chainwork, chaintip_chainwork)) { + // Set the new block as the new chain tip + db->chaintip = blockindex; + + // Block is valid and part of the main chain, so add it to the tree + if (db->use_binary_tree) { + dogecoin_btree_tsearch(blockindex, &db->tree_root, dogecoin_header_compare); + } + + *connected = true; } + else { + // Block is valid but not part of the main chain, so free it + dogecoin_free(blockindex); + } + + // Free the dynamically allocated memory + dogecoin_free(chaintip_chainwork); + dogecoin_free(added_chainwork); } - if (db->use_binary_tree) { - /* TODO: update when fork handling is implemented */ - dogecoin_btree_tfind(blockindex, &db->tree_root, dogecoin_header_compare); + else { + // Block is valid but not a new chaintip, so free it + dogecoin_free(blockindex); } if (db->max_hdr_in_mem > 0) { @@ -374,7 +457,14 @@ dogecoin_blockindex * dogecoin_headers_db_connect_hdr(dogecoin_headers_db* db, s } } } - *connected = true; + + if (!load_process && db->read_write_file && *connected) + { + if (!dogecoin_headers_db_write(db, blockindex)) { + fprintf(stderr, "Error writing blockheader to database\n"); + } + } + return blockindex; } else { // Connection not established, free allocated memory diff --git a/src/pow.c b/src/pow.c index e62b47ed1..a64026274 100644 --- a/src/pow.c +++ b/src/pow.c @@ -40,18 +40,20 @@ dogecoin_bool uint256_cmp(const uint256 a, const uint256 b) { return false; // Return false if all bytes are equal } -dogecoin_bool check_pow(uint256* hash, unsigned int nbits, const dogecoin_chainparams *params) { +dogecoin_bool check_pow(uint256* hash, unsigned int nbits, const dogecoin_chainparams *params, uint256* nhashes_done) { dogecoin_bool f_negative, f_overflow; arith_uint256* target = init_arith_uint256(); target = set_compact(target, nbits, &f_negative, &f_overflow); swap_bytes((uint8_t*)target, sizeof (arith_uint256)); - if (f_negative || (const uint8_t*)target == 0 || f_overflow || uint256_cmp((const uint8_t*) arith_to_uint256(target), params->pow_limit)) { + uint8_t* target_uint256 = arith_to_uint256(target); + if (f_negative || (const uint8_t*)target == 0 || f_overflow || uint256_cmp(target_uint256, params->pow_limit)) { printf("%d:%s: f_negative: %d target == 0: %d f_overflow: %d\n", __LINE__, __func__, f_negative, (const uint8_t*)target == 0, f_overflow); dogecoin_free(target); + dogecoin_free(target_uint256); return false; } - if (uint256_cmp((const uint8_t*)hash, arith_to_uint256(target))) { + if (uint256_cmp((const uint8_t*)hash, target_uint256)) { char* rtn_str = utils_uint8_to_hex((const uint8_t*)hash, 32); char hash_str[65] = ""; strncpy(hash_str, rtn_str, 64); @@ -59,8 +61,43 @@ dogecoin_bool check_pow(uint256* hash, unsigned int nbits, const dogecoin_chainp printf("%d:%s: hash: %s target: %s\n", __LINE__, __func__, hash_str, target_str); dogecoin_free(target); + dogecoin_free(target_uint256); return false; } + dogecoin_free(target_uint256); + + // Calculate number of hashes done + // hashes = ~target / (target + 1) + 1 + arith_uint256* neg_target = init_arith_uint256(); + memcpy(neg_target, target, sizeof(arith_uint256)); + arith_negate(neg_target); + + arith_uint256* one = init_arith_uint256(); + one->pn[0] = 1; // Set the lowest word to 1 + + swap_bytes((uint8_t*)neg_target, sizeof(arith_uint256)); + swap_bytes((uint8_t*)target, sizeof(arith_uint256)); + + // hashes = ~target / (target + 1) + arith_uint256* target_plus_one = add_arith_uint256(target, one); + arith_uint256* hashes = div_arith_uint256(neg_target, target_plus_one); + + // Add one to hashes for ~target / (target + 1) + 1 + arith_uint256* final_hashes = add_arith_uint256(hashes, one); + swap_bytes((uint8_t*)final_hashes, sizeof(arith_uint256)); + uint8_t* final_hashes_uint256 = arith_to_uint256(final_hashes); + + if (nhashes_done != NULL) { + memcpy(nhashes_done, (const uint8_t*) final_hashes_uint256, sizeof(uint256)); + } + + // Clean up + dogecoin_free(neg_target); + dogecoin_free(one); + dogecoin_free(target_plus_one); + dogecoin_free(hashes); + dogecoin_free(final_hashes); + dogecoin_free(final_hashes_uint256); dogecoin_free(target); return true; } diff --git a/src/spv.c b/src/spv.c index 8e97c6cda..8e0454a89 100644 --- a/src/spv.c +++ b/src/spv.c @@ -29,6 +29,7 @@ #ifdef _WIN32 #include #include +#include #else #include #include @@ -38,6 +39,7 @@ #endif #include +#include #include #include #include @@ -77,6 +79,12 @@ void dogecoin_net_set_spv(dogecoin_node_group *nodegroup) nodegroup->handshake_done_cb = dogecoin_net_spv_node_handshake_done; nodegroup->node_connection_state_changed_cb = NULL; nodegroup->periodic_timer_cb = dogecoin_net_spv_node_timer_callback; + +#ifndef _WIN32 + // set stdin to non-blocking for quit command + int stdin_flags = fcntl(STDIN_FILENO, F_GETFL); + fcntl(STDIN_FILENO, F_SETFL, stdin_flags | O_NONBLOCK); +#endif } /** @@ -544,7 +552,9 @@ void dogecoin_net_spv_post_cmd(dogecoin_node *node, dogecoin_p2p_msg_hdr *hdr, s { client->nodegroup->log_write_cb("Got invalid block (not in sequence) from node %d\n", node->nodeid); node->state &= ~NODE_BLOCKSYNC; - dogecoin_node_send(node, dogecoin_p2p_message_new(node->nodegroup->chainparams->netmagic, DOGECOIN_MSG_REJECT, NULL, 0)); + cstring *reject_msg = dogecoin_p2p_message_new(node->nodegroup->chainparams->netmagic, DOGECOIN_MSG_REJECT, NULL, 0); + dogecoin_node_send(node, reject_msg); + cstr_free(reject_msg, true); return; } @@ -615,4 +625,21 @@ void dogecoin_net_spv_post_cmd(dogecoin_node *node, dogecoin_p2p_msg_hdr *hdr, s dogecoin_net_spv_node_request_headers_or_blocks(node, false); } } + + // Check for a 'Q' or 'q' on stdin, to quit. +#ifdef _WIN32 + if (_kbhit()) { + char c = fgetc(stdin); + if (c == 'Q' || c == 'q') { + printf("Disconnecting...\n"); + dogecoin_node_group_shutdown(client->nodegroup); + } + } +#else + char c = fgetc(stdin); + if (c == 'Q' || c == 'q') { + printf("Disconnecting...\n"); + dogecoin_node_group_shutdown(client->nodegroup); + } +#endif } diff --git a/src/validation.c b/src/validation.c index 51302c817..24d708713 100644 --- a/src/validation.c +++ b/src/validation.c @@ -86,7 +86,7 @@ dogecoin_bool check_auxpow(dogecoin_auxpow_block* block, dogecoin_chainparams* p dogecoin_block_header_scrypt_hash(s, &hash); cstr_free(s, true); - if (!check_pow(&hash, block->header->bits, params)) { + if (!check_pow(&hash, block->header->bits, params, &block->header->chainwork)) { printf("%s:%d:%s : non-AUX proof of work failed : %s\n", __FILE__, __LINE__, __func__, strerror(errno)); return false; } @@ -108,7 +108,7 @@ dogecoin_bool check_auxpow(dogecoin_auxpow_block* block, dogecoin_chainparams* p dogecoin_block_header_serialize(s2, block->parent_header); dogecoin_block_header_scrypt_hash(s2, &parent_hash); cstr_free(s2, true); - if (!check_pow(&parent_hash, block->header->bits, params)) { + if (!check_pow(&parent_hash, block->header->bits, params, &block->header->chainwork)) { printf("%s:%d:%s : AUX proof of work failed: %s\n", __FILE__, __LINE__, __func__, strerror(errno)); return false; } diff --git a/test/block_tests.c b/test/block_tests.c index 436d68f93..fe9cba8db 100644 --- a/test/block_tests.c +++ b/test/block_tests.c @@ -11,12 +11,15 @@ #include #include +#include #include #include #include #include +#include #include +#include #include @@ -28,17 +31,18 @@ struct blockheadertest { uint32_t bits; uint32_t nonce; const dogecoin_chainparams* params; + char chainwork[64]; }; static const struct blockheadertest block_header_tests[] = { - {"010000000000000000000000000000000000000000000000000000000000000000000000696ad20e2dd4365c7459b4a4a5af743d5e92c6da3229e6532cd605f6533f2a5b24a6a152f0ff0f1e67860100", "1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691", 1, 1386325540, 504365040, 99943, &dogecoin_chainparams_main}, // genesis hash - {"020162000d6f03470d329026cd1fc720c0609cd378ca8691a117bd1aa46f01fb09b1a8468a15bf6f0b0e83f2e5036684169eafb9406468d4f075c999fb5b2a78fbb827ee41fb11548441361b00000000", "60323982f9c5ff1b5a954eac9dc1269352835f47c2c5222691d80f0d50dcf053", 6422786, 1410464577, 456540548, 0, &dogecoin_chainparams_main}, // 331337 - {"020162002107cd08bec145c55ba8ffcbb4a9c0e836dfca383aa6ca1b380259a670aeb56fe5ea77d4f004afc5a0d31af1b89d5ebd9fd60cd7da7f4dcd96b0db1096a5bb1a7afb115488632e1b00000000","aff80f7b4dc8c667ebf4c76a6a62f9c4479844a37421ca2bf5abb485f4579fb6", 6422786, 1410464634, 456024968, 0, &dogecoin_chainparams_main}, // 331339 - {"03016200c96fd9d1b98330440082bcc1e58a39fe5a522f42defc501bff9b68f7b67ed99e1144e430166c54e9b911d8e059c03d0f972e7ab971c51f5505ff0bb21fee6fb1d88a9d5be132051a00000000", "c91f5a44a752c7549c1c689af5aeb42639582011d887282f976d550477abe25a", 6422787, 1537051352, 436548321, 0, &dogecoin_chainparams_main}, // 2391337 - {"0401620057bd4aa5170622b624bff774a087ea879a288226925c7cd5f3ead6ca4b6146e227b0e3699361bf58440971cfb28e16d9bab909769668ef3aac26220c6c0dc5fbda52595f9a97031a00000000", "8d7e4e91b571025ca109f2a0aeaf114ecc6aa2feec7f8bf23d405ac026c65d5e", 6422788, 1599689434, 436443034, 0, &dogecoin_chainparams_main}, // 3391337 + {"010000000000000000000000000000000000000000000000000000000000000000000000696ad20e2dd4365c7459b4a4a5af743d5e92c6da3229e6532cd605f6533f2a5b24a6a152f0ff0f1e67860100", "1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691", 1, 1386325540, 504365040, 99943, &dogecoin_chainparams_main, "0000000000000000000000000000000000000000000000000000000000100010"}, // genesis hash + {"020162000d6f03470d329026cd1fc720c0609cd378ca8691a117bd1aa46f01fb09b1a8468a15bf6f0b0e83f2e5036684169eafb9406468d4f075c999fb5b2a78fbb827ee41fb11548441361b00000000", "60323982f9c5ff1b5a954eac9dc1269352835f47c2c5222691d80f0d50dcf053", 6422786, 1410464577, 456540548, 0, &dogecoin_chainparams_main, ""}, // 331337 + {"020162002107cd08bec145c55ba8ffcbb4a9c0e836dfca383aa6ca1b380259a670aeb56fe5ea77d4f004afc5a0d31af1b89d5ebd9fd60cd7da7f4dcd96b0db1096a5bb1a7afb115488632e1b00000000","aff80f7b4dc8c667ebf4c76a6a62f9c4479844a37421ca2bf5abb485f4579fb6", 6422786, 1410464634, 456024968, 0, &dogecoin_chainparams_main, ""}, // 331339 + {"03016200c96fd9d1b98330440082bcc1e58a39fe5a522f42defc501bff9b68f7b67ed99e1144e430166c54e9b911d8e059c03d0f972e7ab971c51f5505ff0bb21fee6fb1d88a9d5be132051a00000000", "c91f5a44a752c7549c1c689af5aeb42639582011d887282f976d550477abe25a", 6422787, 1537051352, 436548321, 0, &dogecoin_chainparams_main, ""}, // 2391337 + {"0401620057bd4aa5170622b624bff774a087ea879a288226925c7cd5f3ead6ca4b6146e227b0e3699361bf58440971cfb28e16d9bab909769668ef3aac26220c6c0dc5fbda52595f9a97031a00000000", "8d7e4e91b571025ca109f2a0aeaf114ecc6aa2feec7f8bf23d405ac026c65d5e", 6422788, 1599689434, 436443034, 0, &dogecoin_chainparams_main, ""}, // 3391337 // end mainnet blocks - {"020162002770a8b89647bbb542f044754a07dc6e56545793f5dcecdf43826ae0cb7192a12466d048e51b0f8a3cbaaf8a624b9aa1212ce4c2a4feba0750f7ad14feb75f54c69de053837b091e00000000", "8afc65a42c47b5ed5862194fb846171ba4afb999a1b4cce149f56c328d8a90e4", 6422786, 1407229382, 503937923, 0, &dogecoin_chainparams_test} // 158391 + {"020162002770a8b89647bbb542f044754a07dc6e56545793f5dcecdf43826ae0cb7192a12466d048e51b0f8a3cbaaf8a624b9aa1212ce4c2a4feba0750f7ad14feb75f54c69de053837b091e00000000", "8afc65a42c47b5ed5862194fb846171ba4afb999a1b4cce149f56c328d8a90e4", 6422786, 1407229382, 503937923, 0, &dogecoin_chainparams_test, ""} // 158391 }; void test_block_header() @@ -83,6 +87,29 @@ void test_block_header() assert(header->bits == test->bits); assert(header->nonce == test->nonce); + // Check chainwork (genesis block only) + if (i == 0) { + arith_uint256* target = init_arith_uint256(); + uint256 chainwork = {0}; + cstring* s = cstr_new_sz(64); + dogecoin_bool f_negative, f_overflow; + uint256* hash = dogecoin_uint256_vla(1); + + // Compute the hash of the block header + dogecoin_block_header_serialize(s, header); + dogecoin_block_header_scrypt_hash(s, hash); + + // Compute the chainwork + target = set_compact(target, block_header_tests[i].bits, &f_negative, &f_overflow); + check_pow(hash, block_header_tests[i].bits, block_header_tests[i].params, &chainwork); + + // Check the chainwork matches + u_assert_mem_eq(utils_uint8_to_hex(chainwork, DOGECOIN_HASH_LENGTH), test->chainwork, 64); + cstr_free(s, true); + dogecoin_free(hash); + dogecoin_free(target); + } + dogecoin_block_header_free(header); dogecoin_block_header_free(header_copy); cstr_free(serialized, true); diff --git a/test/spv_tests.c b/test/spv_tests.c index 2fc96761a..ef4466be8 100644 --- a/test/spv_tests.c +++ b/test/spv_tests.c @@ -34,10 +34,13 @@ #include +#include #include +#include #include #include #include +#include void test_spv_sync_completed(dogecoin_spv_client* client) { printf("Sync completed, at height %d\n", client->headers_db->getchaintip(client->headers_db_ctx)->height); @@ -82,3 +85,229 @@ void test_spv() remove_all_hashes(); remove_all_maps(); } + +void test_reorg() { + // Initialize the chain parameters + const dogecoin_chainparams* chain = &dogecoin_chainparams_main; + + // Setup headers database file path + char* header_suffix = "_headers.db"; + char* header_prefix = (char*)chain->chainname; + char* headersfile = concat(header_prefix, header_suffix); + + // Unlink the headers database file + unlink(headersfile); + + // Initialize SPV client + dogecoin_spv_client* client = dogecoin_spv_client_new(chain, false, true, false, false); + client->header_message_processed = test_spv_header_message_processed; + client->sync_completed = test_spv_sync_completed; + dogecoin_spv_client_load(client, headersfile); + dogecoin_free(headersfile); + + // Create headers for the main chain and new chain + dogecoin_block_header* header1 = dogecoin_block_header_new(); + dogecoin_block_header* header2 = dogecoin_block_header_new(); + dogecoin_block_header* header2_fork = dogecoin_block_header_new(); + dogecoin_block_header* header3_fork = dogecoin_block_header_new(); + size_t outlen; + + // Initialize header1 + header1->version = 1; // 1 + header1->timestamp = 1386474927; // 1 + header1->nonce = 1417875456; // 1 + header1->bits = 0x1e0ffff0; // 1 + char prevblock_hex1[65] = "1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691"; + utils_reverse_hex(prevblock_hex1, 64); + utils_hex_to_bin(prevblock_hex1, (uint8_t*) header1->prev_block, 64, &outlen); + char merkleroot_hex1[65] = "5f7e779f7600f54e528686e91d5891f3ae226ee907f461692519e549105f521c"; + utils_reverse_hex(merkleroot_hex1, 64); + utils_hex_to_bin(merkleroot_hex1, (uint8_t*) header1->merkle_root, 64, &outlen); + + // Initialize header2 + header2->version = 1; // 2 + header2->timestamp = 1386474933; // 2 + header2->nonce = 3404207872; // 2 + header2->bits = 0x1e0ffff0; // 2 + char prevblock_hex2[65] = "82bc68038f6034c0596b6e313729793a887fded6e92a31fbdf70863f89d9bea2"; + utils_reverse_hex(prevblock_hex2, 64); + utils_hex_to_bin(prevblock_hex2, (uint8_t*) header2->prev_block, 64, &outlen); + char merkleroot_hex2[65] = "3b14b76d22a3f2859d73316002bc1b9bfc7f37e2c3393be9b722b62bbd786983"; + utils_reverse_hex(merkleroot_hex2, 64); + utils_hex_to_bin(merkleroot_hex2, (uint8_t*) header2->merkle_root, 64, &outlen); + + // Initialize header2_fork + header2_fork->version = 1; // 2 + header2_fork->timestamp = 1386474933; // 2 + header2_fork->nonce = 3406419112; // 2 header2_fork->nonce = 3404481231; // 2 + header2_fork->bits = 0x1e0ffef0; // 2 + utils_hex_to_bin(prevblock_hex2, (uint8_t*) header2_fork->prev_block, 64, &outlen); + utils_hex_to_bin(merkleroot_hex2, (uint8_t*) header2_fork->merkle_root, 64, &outlen); + + // Initialize header3_fork + header3_fork->version = 1; + header3_fork->timestamp = 1386474934; // 2 + 1 + header3_fork->nonce = 3407274091; // + header3_fork->bits = 0x1e0ffef0; // + utils_hex_to_bin(merkleroot_hex2, (uint8_t*) header3_fork->merkle_root, 64, &outlen); // merkle is a don't care + + // Calculate the chainwork for each header + uint256 chainwork1, chainwork2, chainwork2_fork, chainwork3_fork = {0}; + arith_uint256* target1 = init_arith_uint256(); + arith_uint256* target2 = init_arith_uint256(); + arith_uint256* target2_fork = init_arith_uint256(); + arith_uint256* target3_fork = init_arith_uint256(); + cstring* s = cstr_new_sz(64); + dogecoin_bool f_negative, f_overflow; + uint256* hash = dogecoin_uint256_vla(1); + + // Compute the hash of the block header 1 + dogecoin_block_header_serialize(s, header1); + dogecoin_block_header_scrypt_hash(s, hash); + cstr_free(s, true); + + // Compute the chainwork 1 + target1 = set_compact(target1, header1->bits, &f_negative, &f_overflow); + check_pow(hash, header1->bits, chain, &chainwork1); + dogecoin_free(target1); + + // Compute the hash of the block header 2 + s = cstr_new_sz(64); + dogecoin_block_header_serialize(s, header2); + dogecoin_block_header_scrypt_hash(s, hash); + cstr_free(s, true); + + // Compute the chainwork 2 + target2 = set_compact(target2, header2->bits, &f_negative, &f_overflow); + check_pow(hash, header2->bits, chain, &chainwork2); + dogecoin_free(target2); + + arith_uint256* arith_chainwork2 = uint_to_arith((const uint256*) &chainwork2); + arith_uint256* arith_chainwork2_fork; + + // Mine the forked block header 2 + // loop until the chainwork of the fork is greater and the hash passes PoW + while (true) { + // Compute the hash of the block header 2 + s = cstr_new_sz(64); + dogecoin_block_header_serialize(s, header2_fork); + dogecoin_block_header_scrypt_hash(s, hash); + cstr_free(s, true); + + // Compute the chainwork 2 + target2_fork = set_compact(target2_fork, header2_fork->bits, &f_negative, &f_overflow); + bool pow_passed = check_pow(hash, header2_fork->bits, chain, &chainwork2_fork); + + // Update the arith_uint256 chainwork of the fork + arith_chainwork2_fork = uint_to_arith((const uint256*) &chainwork2_fork); + + // Check if the chainwork of the fork is greater and the hash passes PoW + if (arith_uint256_greater_than(arith_chainwork2_fork, arith_chainwork2) && pow_passed) { + debug_print("Nonce: %u\n", header2_fork->nonce); + debug_print("Hash: %s\n", hash_to_string((uint8_t*) hash)); + debug_print("Chainwork: %s\n", hash_to_string((uint8_t*) arith_chainwork2_fork)); + break; + } + + // Increment the nonce + header2_fork->nonce++; + + // Free the arith_uint256 chainwork + dogecoin_free(arith_chainwork2_fork); + } + + // Free the arith_uint256 chainwork of the fork + dogecoin_free(arith_chainwork2); + dogecoin_free(arith_chainwork2_fork); + + // Free the target and hash + dogecoin_free(target2_fork); + + // Compute the sha256d hash of the header2_fork + s = cstr_new_sz(64); + dogecoin_block_header_serialize(s, header2_fork); + dogecoin_block_header_hash(header2_fork, (uint8_t*) hash); + cstr_free(s, true); + + // Set header3_fork's previous block to header2_fork's hash + memcpy(&header3_fork->prev_block, hash, DOGECOIN_HASH_LENGTH); + + arith_uint256* arith_chainwork3_fork; + + // Mine the forked block header 3 + // loop until the chainwork of the fork is greater and the hash passes PoW + while (true) { + // Compute the hash of the block header 3 + s = cstr_new_sz(64); + dogecoin_block_header_serialize(s, header3_fork); + dogecoin_block_header_scrypt_hash(s, hash); + cstr_free(s, true); + + // Compute the chainwork 3 + target3_fork = set_compact(target3_fork, header3_fork->bits, &f_negative, &f_overflow); + bool pow_passed = check_pow(hash, header3_fork->bits, chain, &chainwork3_fork); + + // Update the arith_uint256 chainwork of the fork + arith_chainwork3_fork = uint_to_arith((const uint256*) &chainwork3_fork); + + // Check if the hash passes PoW + if (pow_passed) { + debug_print("Nonce: %u\n", header3_fork->nonce); + debug_print("Hash: %s\n", hash_to_string((uint8_t*) hash)); + debug_print("Chainwork: %s\n", hash_to_string((uint8_t*) arith_chainwork3_fork)); + break; + } + + // Increment the nonce + header3_fork->nonce++; + + // Free the arith_uint256 chainwork + dogecoin_free(arith_chainwork3_fork); + } + + // Free the arith_uint256 chainwork of the fork + dogecoin_free(arith_chainwork3_fork); + + // Free the target and hash + dogecoin_free(target3_fork); + dogecoin_free(hash); + + // Create a cstring for the new block headers + cstring* cbuf_all = cstr_new_sz(80 * 3); + + // Serialize header1 into cbuf_all + dogecoin_block_header_serialize(cbuf_all, header1); + + // Serialize header2 into cbuf_all + dogecoin_block_header_serialize(cbuf_all, header2); + + // Serialize header2_fork into cbuf_all + dogecoin_block_header_serialize(cbuf_all, header2_fork); + + // Serialize header3_fork into cbuf_all + dogecoin_block_header_serialize(cbuf_all, header3_fork); + + // Define a constant buffer for each header + struct const_buffer cbuf_header1 = {cbuf_all->str, 80}; + struct const_buffer cbuf_header2 = {cbuf_all->str + 80, 80}; + struct const_buffer cbuf_header2_fork = {cbuf_all->str + 160, 80}; + struct const_buffer cbuf_header3_fork = {cbuf_all->str + 240, 80}; + + // Connect the headers to the database + dogecoin_bool connected; + dogecoin_headers_db *db = client->headers_db_ctx; + dogecoin_headers_db_connect_hdr(db, &cbuf_header1, true, &connected); + dogecoin_headers_db_connect_hdr(db, &cbuf_header2, true, &connected); + dogecoin_headers_db_connect_hdr(db, &cbuf_header2_fork, true, &connected); + dogecoin_headers_db_connect_hdr(db, &cbuf_header3_fork, true, &connected); + + // Cleanup + cstr_free(cbuf_all, true); + dogecoin_block_header_free(header1); + dogecoin_block_header_free(header2); + dogecoin_block_header_free(header2_fork); + dogecoin_block_header_free(header3_fork); + dogecoin_spv_client_free(client); + remove_all_hashes(); + remove_all_maps(); +} diff --git a/test/unittester.c b/test/unittester.c index b3f606cce..98c0a0af9 100644 --- a/test/unittester.c +++ b/test/unittester.c @@ -97,6 +97,7 @@ extern void test_tool(); extern void test_net_basics_plus_download_block(); extern void test_protocol(); extern void test_net_flag_defined(); +extern void test_reorg(); extern void test_spv(); #else extern void test_net_flag_not_defined(); @@ -175,6 +176,7 @@ int main() u_run_test(test_net_flag_defined); u_run_test(test_net_basics_plus_download_block); u_run_test(test_protocol); + u_run_test(test_reorg); u_run_test(test_spv); #else u_run_test(test_net_flag_not_defined);