From d3213678f6f19afcdf6a76a16b9e6ac75c02ef34 Mon Sep 17 00:00:00 2001 From: edtubbs Date: Wed, 21 Feb 2024 21:27:06 -0600 Subject: [PATCH] net: added disconnected state for shutdown headersdb_file: added dogecoin_free to dogecoin_btree_tdestroy headersdb_file: removed loop from chaintip to chainbottom headersdb_file: added check for duplicate header headersdb_file: updated to return blockindex spv: updated invalid header case to free blockindex spv: updated invalid header case for state change spv: updated invalid block case with missbehaved state spv: updated logging in invalid block case utils: updated dogecoin_btree_tdestroy to free every node test: added stale header to spv_tests test: added duplicate header to spv_tests --- include/dogecoin/utils.h | 7 ++-- src/headersdb_file.c | 34 ++++++--------- src/net.c | 91 ++++++++++++++++++++-------------------- src/spv.c | 5 ++- test/spv_tests.c | 74 ++++++++++++++++++++++++++++---- 5 files changed, 132 insertions(+), 79 deletions(-) diff --git a/include/dogecoin/utils.h b/include/dogecoin/utils.h index 3c9a82ed7..d8ad0aa3e 100644 --- a/include/dogecoin/utils.h +++ b/include/dogecoin/utils.h @@ -115,11 +115,10 @@ static inline void dogecoin_btree_tdestroy(void *root, void (*freekey)(void *)) if (r == 0) return; - if (freekey) goto end; - if (r->left && !freekey) dogecoin_btree_tdestroy(r->left, freekey); - if (r->right && !freekey) dogecoin_btree_tdestroy(r->right, freekey); -end: + if (r->left) dogecoin_btree_tdestroy(r->left, freekey); + if (r->right) dogecoin_btree_tdestroy(r->right, freekey); + if (freekey) freekey(r->key); dogecoin_free(r); } diff --git a/src/headersdb_file.c b/src/headersdb_file.c index 4f7cf9d75..b88ca3b56 100644 --- a/src/headersdb_file.c +++ b/src/headersdb_file.c @@ -116,24 +116,10 @@ void dogecoin_headers_db_free(dogecoin_headers_db* db) { } if (db->tree_root) { - dogecoin_btree_tdestroy(db->tree_root, NULL); + dogecoin_btree_tdestroy(db->tree_root, dogecoin_free); db->tree_root = NULL; } - // Free all blockindex structures starting from chaintip to chainbottom - if (db->chaintip) { - dogecoin_blockindex *scan_tip = db->chaintip; - while (scan_tip->prev) { - dogecoin_blockindex *prev = scan_tip->prev; - dogecoin_free(scan_tip); - scan_tip = prev; - } - // If scan_tip is not the genesis block, free it - if (scan_tip != &db->genesis) { - dogecoin_free(scan_tip); - } - } - db->chaintip = NULL; db->chainbottom = NULL; @@ -311,13 +297,19 @@ dogecoin_blockindex * dogecoin_headers_db_connect_hdr(dogecoin_headers_db* db, s dogecoin_blockindex *blockindex = dogecoin_calloc(1, sizeof(dogecoin_blockindex)); if (!dogecoin_block_header_deserialize(&blockindex->header, buf, db->params)) { - dogecoin_free(blockindex); fprintf(stderr, "Error deserializing block header\n"); - return NULL; + return blockindex; } dogecoin_block_header_hash(&blockindex->header, (uint8_t *)&blockindex->hash); + // Check if the block header is already in the database + dogecoin_blockindex *block = dogecoin_headersdb_find(db, blockindex->hash); + if (block) { + // Block header already in database, return blockindex + return blockindex; + } + dogecoin_blockindex *connect_at = NULL; dogecoin_blockindex *fork_from_block = NULL; @@ -343,8 +335,7 @@ dogecoin_blockindex * dogecoin_headers_db_connect_hdr(dogecoin_headers_db* db, s cstr_free(s, true); 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; + return blockindex; } } @@ -383,10 +374,9 @@ dogecoin_blockindex * dogecoin_headers_db_connect_hdr(dogecoin_headers_db* db, s // Break the loop if either reaches the start of the chain if (!common_ancestor || !fork_chain) { fprintf(stderr, "Unable to find common ancestor.\n"); - dogecoin_free(blockindex); dogecoin_free(chaintip_chainwork); dogecoin_free(added_chainwork); - return NULL; + return blockindex; } } @@ -413,7 +403,7 @@ dogecoin_blockindex * dogecoin_headers_db_connect_hdr(dogecoin_headers_db* db, s // Free the dynamically allocated memory dogecoin_free(chaintip_chainwork); dogecoin_free(added_chainwork); - return NULL; + return blockindex; } // Update the chain tip to the previous block diff --git a/src/net.c b/src/net.c index b5f5a85d5..00cf2c632 100644 --- a/src/net.c +++ b/src/net.c @@ -76,9 +76,9 @@ static const int DOGECOIN_CONNECT_TIMEOUT_S = 10; /** * This function is used to print debug messages to the log file - * + * * @param format The format string. - * + * * @return 1 */ int net_write_log_printf(const char* format, ...) @@ -93,9 +93,9 @@ int net_write_log_printf(const char* format, ...) /** * This function is used to write to the log file - * + * * @param format The format string for the printf function. - * + * * @return 1 */ int net_write_log_null(const char* format, ...) @@ -106,10 +106,10 @@ int net_write_log_null(const char* format, ...) /** * If we have a complete message, parse it - * + * * @param bev The bufferevent that is being read from. * @param ctx The node object. - * + * * @return dogecoin_bool (uint8_t) */ void read_cb(struct bufferevent* bev, void* ctx) @@ -181,9 +181,9 @@ void read_cb(struct bufferevent* bev, void* ctx) /** * This function is called when the client sends data to the server - * + * * @param ev The bufferevent object. - * @param ctx The context parameter is a pointer to the context object that was + * @param ctx The context parameter is a pointer to the context object that was * passed to bufferevent_socket_new(). */ void write_cb(struct bufferevent* ev, void* ctx) @@ -193,12 +193,12 @@ void write_cb(struct bufferevent* ev, void* ctx) } /** - * The node_periodical_timer function is called every second by the node_timer_loop function and checks if the node is connected to the network. - * + * The node_periodical_timer function is called every second by the node_timer_loop function and checks if the node is connected to the network. + * * @param fd The file descriptor of the socket. * @param event The event that triggered the callback. * @param ctx The node object. - * + * * @return dogecoin_bool (uint8_t) */ #if defined(_WIN32) && defined(__x86_64__) @@ -237,7 +237,7 @@ void node_periodical_timer(int fd, short int event, void* ctx) /** * When the event callback is called it sets the node's state with the type of event that happened. - * + * * @param ev The bufferevent structure. * @param type The event type. * @param ctx The node object. @@ -279,7 +279,7 @@ void event_cb(struct bufferevent* ev, short type, void* ctx) /** * Initializes a new dogecoin_node - * + * * @return A pointer to a new dogecoin_node object. */ dogecoin_node* dogecoin_node_new() @@ -302,10 +302,10 @@ dogecoin_node* dogecoin_node_new() /** * Given a node and a string, set the node's address to the string - * + * * @param node the node structure to be filled * @param ipport The IP address and port of the node. - * + * * @return dogecoin_bool (uint8_t) */ dogecoin_bool dogecoin_node_set_ipport(dogecoin_node* node, const char* ipport) @@ -317,7 +317,7 @@ dogecoin_bool dogecoin_node_set_ipport(dogecoin_node* node, const char* ipport) /** * Release the event buffer and timer event - * + * * @param node The node that we're releasing the events for. */ void dogecoin_node_release_events(dogecoin_node* node) @@ -336,9 +336,9 @@ void dogecoin_node_release_events(dogecoin_node* node) /** * Mark the node as misbehaved - * + * * @param node The node that is being marked as misbehaved. - * + * * @return dogecoin_bool (uint8_t) */ dogecoin_bool dogecoin_node_misbehave(dogecoin_node* node) @@ -351,7 +351,7 @@ dogecoin_bool dogecoin_node_misbehave(dogecoin_node* node) /** * If the node is connected, disconnect it - * + * * @param node The node that is being disconnected. */ void dogecoin_node_disconnect(dogecoin_node* node) @@ -370,7 +370,7 @@ void dogecoin_node_disconnect(dogecoin_node* node) /** * It frees the memory allocated to the node - * + * * @param node The node to disconnect from. */ void dogecoin_node_free(dogecoin_node* node) @@ -382,7 +382,7 @@ void dogecoin_node_free(dogecoin_node* node) /** * It takes a pointer to a dogecoin_node object and frees it - * + * * @param obj The object to be freed. */ void dogecoin_node_free_cb(void* obj) @@ -393,9 +393,9 @@ void dogecoin_node_free_cb(void* obj) /** * Creates a new dogecoin_node_group object - * + * * @param chainparams The chainparams to use. If NULL, use the mainnet. - * + * * @return A dogecoin_node_group object. */ dogecoin_node_group* dogecoin_node_group_new(const dogecoin_chainparams* chainparams) @@ -415,7 +415,7 @@ dogecoin_node_group* dogecoin_node_group_new(const dogecoin_chainparams* chainpa /* Winsock DLL. */ printf("WSAStartup failed. error code : %d", err); } - + printf("initialized.\n"); /* Confirm that the WinSock DLL supports 2.2.*/ /* Note that if the DLL supports versions greater */ @@ -458,7 +458,7 @@ dogecoin_node_group* dogecoin_node_group_new(const dogecoin_chainparams* chainpa /** * Shut down all the nodes in the group. - * + * * @param group The group to shutdown. */ void dogecoin_node_group_shutdown(dogecoin_node_group *group) { @@ -471,7 +471,7 @@ void dogecoin_node_group_shutdown(dogecoin_node_group *group) { /** * It takes a pointer to a dogecoin_node_group, and frees the memory allocated to it - * + * * @param group The group to free. */ void dogecoin_node_group_free(dogecoin_node_group* group) @@ -491,7 +491,7 @@ void dogecoin_node_group_free(dogecoin_node_group* group) /** * The event loop is the core of the event-driven networking library - * + * * @param group The dogecoin_node_group object. */ void dogecoin_node_group_event_loop(dogecoin_node_group* group) @@ -501,7 +501,7 @@ void dogecoin_node_group_event_loop(dogecoin_node_group* group) /** * Adds a node to a node group - * + * * @param group The node group to add the node to. * @param node The node to add to the group. */ @@ -514,10 +514,10 @@ void dogecoin_node_group_add_node(dogecoin_node_group* group, dogecoin_node* nod /** * The function is used to count the number of nodes in the group that are in the given state - * + * * @param group the group to check * @param state The state of the node. - * + * * @return The number of nodes in the group that are in the given state. */ int dogecoin_node_group_amount_of_connected_nodes(dogecoin_node_group* group, enum NODE_STATE state) @@ -535,9 +535,9 @@ int dogecoin_node_group_amount_of_connected_nodes(dogecoin_node_group* group, en /** * Try to connect to a node that is not connected, not in connecting state, and has not * been connected for more than DOGECOIN_PERIODICAL_NODE_TIMER_S seconds. - * + * * @param group the node group we're connecting to - * + * * @return A boolean value. */ dogecoin_bool dogecoin_node_group_connect_next_nodes(dogecoin_node_group* group) @@ -553,6 +553,7 @@ dogecoin_bool dogecoin_node_group_connect_next_nodes(dogecoin_node_group* group) dogecoin_node* node = vector_idx(group->nodes, i); if ( !((node->state & NODE_CONNECTED) == NODE_CONNECTED) && + !((node->state & NODE_DISCONNECTED) == NODE_DISCONNECTED) && !((node->state & NODE_CONNECTING) == NODE_CONNECTING)) { /* setup buffer event */ node->event_bev = bufferevent_socket_new(group->event_base, -1, BEV_OPT_CLOSE_ON_FREE); @@ -585,9 +586,9 @@ dogecoin_bool dogecoin_node_group_connect_next_nodes(dogecoin_node_group* group) } /** - * If the node is in an error state or misbehaving state we disconnect it. If the - * node is in a connecting state we do nothing. Otherwise we send a version message to it. - * + * If the node is in an error state or misbehaving state we disconnect it. If the + * node is in a connecting state we do nothing. Otherwise we send a version message to it. + * * @param node The node that has changed state. */ void dogecoin_node_connection_state_changed(dogecoin_node* node) @@ -615,10 +616,10 @@ void dogecoin_node_connection_state_changed(dogecoin_node* node) /** * Send a message to a node - * + * * @param node the node that is sending the message * @param data The data to send. - * + * * @return dogecoin_bool (uint8_t) */ void dogecoin_node_send(dogecoin_node* node, cstring* data) @@ -633,7 +634,7 @@ void dogecoin_node_send(dogecoin_node* node, cstring* data) /** * Send a version message to the remote node - * + * * @param node The node that is sending the message. */ void dogecoin_node_send_version(dogecoin_node* node) @@ -671,11 +672,11 @@ void dogecoin_node_send_version(dogecoin_node* node) /** * This function parses a command message received from another node. - * + * * @param node The node that received the message. * @param hdr The header of the message. * @param buf The buffer containing the message. - * + * * @return int */ int dogecoin_node_parse_message(dogecoin_node* node, dogecoin_p2p_msg_hdr* hdr, struct const_buffer* buf) @@ -724,14 +725,14 @@ int dogecoin_node_parse_message(dogecoin_node* node, dogecoin_p2p_msg_hdr* hdr, } /** - * Given a seed DNS name, return a vector of IP addresses and ports. + * Given a seed DNS name, return a vector of IP addresses and ports. * (utility function to get peers (ips/port as char*) from a seed) - * + * * @param seed The seed DNS name. * @param ips_out A vector of strings. * @param port The port to connect to. * @param family The address family of the socket. AF_INET or AF_INET6. - * + * * @return size_t */ size_t dogecoin_get_peers_from_dns(const char* seed, vector* ips_out, int port, int family) @@ -778,10 +779,10 @@ size_t dogecoin_get_peers_from_dns(const char* seed, vector* ips_out, int port, /** * It takes a comma seperated list of IPs and adds them to the group - * + * * @param group the node group to add the nodes to * @param ips comma seperated list of ip addresses - * + * * @return dogecoin_bool (uint8_t) */ dogecoin_bool dogecoin_node_group_add_peers_by_ip_or_seed(dogecoin_node_group *group, const char *ips) { diff --git a/src/spv.c b/src/spv.c index c1ce898a3..04c21b30e 100644 --- a/src/spv.c +++ b/src/spv.c @@ -590,8 +590,9 @@ void dogecoin_net_spv_post_cmd(dogecoin_node *node, dogecoin_p2p_msg_hdr *hdr, s } else { - client->nodegroup->log_write_cb("Got invalid block with hash %s and height %d (not in sequence) from node %d\n", hash_to_string(pindex->hash), pindex->height, node->nodeid); + client->nodegroup->log_write_cb("Got invalid block (not in sequence) from node %d\n", node->nodeid); node->state &= ~NODE_BLOCKSYNC; + node->state |= NODE_MISSBEHAVED; node->nodegroup->node_connection_state_changed_cb(node); dogecoin_free(pindex); return; @@ -638,6 +639,8 @@ void dogecoin_net_spv_post_cmd(dogecoin_node *node, dogecoin_p2p_msg_hdr *hdr, s { client->nodegroup->log_write_cb("Got invalid headers (not in sequence) from node %d\n", node->nodeid); node->state &= ~NODE_HEADERSYNC; + node->nodegroup->node_connection_state_changed_cb(node); + dogecoin_free(pindex); break; } else { if (client->header_connected) { client->header_connected(client); } diff --git a/test/spv_tests.c b/test/spv_tests.c index f0fb72207..4ef8ea359 100644 --- a/test/spv_tests.c +++ b/test/spv_tests.c @@ -109,6 +109,7 @@ void test_reorg() { dogecoin_block_header* header2 = dogecoin_block_header_new(); dogecoin_block_header* header3 = dogecoin_block_header_new(); dogecoin_block_header* header4 = dogecoin_block_header_new(); + dogecoin_block_header* header2_stale = dogecoin_block_header_new(); dogecoin_block_header* header2_fork = dogecoin_block_header_new(); dogecoin_block_header* header3_fork = dogecoin_block_header_new(); dogecoin_block_header* header4_fork = dogecoin_block_header_new(); @@ -163,6 +164,14 @@ void test_reorg() { utils_reverse_hex(merkleroot_hex4, 64); utils_hex_to_bin(merkleroot_hex4, (uint8_t*) header4->merkle_root, 64, &outlen); + // Initialize header2_stale + header2_stale->version = 1; // 2 + header2_stale->timestamp = 1386474933; // 2 + header2_stale->nonce = 3404481231; // 2 + header2_stale->bits = 0x1e0ffff0; // 2 + utils_hex_to_bin(prevblock_hex2, (uint8_t*) header2_stale->prev_block, 64, &outlen); + utils_hex_to_bin(merkleroot_hex2, (uint8_t*) header2_stale->merkle_root, 64, &outlen); + // Initialize header2_fork header2_fork->version = 1; // 2 header2_fork->timestamp = 1386474933; // 2 @@ -193,11 +202,12 @@ void test_reorg() { utils_hex_to_bin(merkleroot_hex2, (uint8_t*) header5_fork->merkle_root, 64, &outlen); // merkle is a don't care // Calculate the chainwork for each header - uint256 chainwork1, chainwork2, chainwork3, chainwork4, chainwork2_fork, chainwork3_fork, chainwork4_fork, chainwork5_fork; + uint256 chainwork1, chainwork2, chainwork3, chainwork4, chainwork2_stale, chainwork2_fork, chainwork3_fork, chainwork4_fork, chainwork5_fork; arith_uint256* target1 = init_arith_uint256(); arith_uint256* target2 = init_arith_uint256(); arith_uint256* target3 = init_arith_uint256(); arith_uint256* target4 = init_arith_uint256(); + arith_uint256* target2_stale = init_arith_uint256(); arith_uint256* target2_fork = init_arith_uint256(); arith_uint256* target3_fork = init_arith_uint256(); arith_uint256* target4_fork = init_arith_uint256(); @@ -251,6 +261,43 @@ void test_reorg() { arith_uint256* arith_chainwork2 = init_arith_uint256(); memcpy(arith_chainwork2, &chainwork2, sizeof(arith_uint256)); + arith_uint256* arith_chainwork2_stale = init_arith_uint256(); + + // Mine the stale block header 2 + // loop until the chainwork of the stale is equal 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_stale); + dogecoin_block_header_scrypt_hash(s, hash); + cstr_free(s, true); + + // Compute the chainwork 2 + target2_stale = set_compact(target2_stale, header2_stale->bits, &f_negative, &f_overflow); + bool pow_passed = check_pow(hash, header2_stale->bits, chain, &chainwork2_stale); + + // Update the arith_uint256 chainwork of the stale + memcpy(arith_chainwork2_stale, &chainwork2_stale, sizeof(uint256)); + + // Check if the chainwork of the stale is equal and the hash passes PoW + if (arith_uint256_equal(arith_chainwork2_stale, arith_chainwork2) && pow_passed) { + debug_print("Nonce: %u\n", header2_stale->nonce); + debug_print("Hash: %s\n", hash_to_string((uint8_t*) hash)); + debug_print("Chainwork: %s\n", hash_to_string((uint8_t*) arith_chainwork2_stale)); + break; + } + + // Increment the nonce + header2_stale->nonce++; + + } + + // Free the arith_uint256 chainwork of the stale + dogecoin_free(arith_chainwork2_stale); + + // Free the target and hash + dogecoin_free(target2_stale); + arith_uint256* arith_chainwork2_fork = init_arith_uint256(); // Mine the forked block header 2 @@ -403,7 +450,7 @@ void test_reorg() { // Compute the chainwork 5 target5_fork = set_compact(target5_fork, header5_fork->bits, &f_negative, &f_overflow); - bool pow_passed = check_pow(hash, header5_fork->bits, chain, &chainwork4_fork); + bool pow_passed = check_pow(hash, header5_fork->bits, chain, &chainwork5_fork); // Update the arith_uint256 chainwork of the fork memcpy(arith_chainwork5_fork, &chainwork5_fork, sizeof(uint256)); @@ -448,6 +495,9 @@ void test_reorg() { // Serialize header5_fork into cbuf_all dogecoin_block_header_serialize(cbuf_all, header5_fork); + // Serialize header2_stale into cbuf_all + dogecoin_block_header_serialize(cbuf_all, header2_stale); + // Serialize header2_fork into cbuf_all dogecoin_block_header_serialize(cbuf_all, header2_fork); @@ -457,7 +507,10 @@ void test_reorg() { // Serialize header4_fork into cbuf_all dogecoin_block_header_serialize(cbuf_all, header4_fork); - // Serialize header5_fork into cbuf_all + // Serialize header5_fork into cbuf_all again + dogecoin_block_header_serialize(cbuf_all, header5_fork); + + // Serialize header5_fork into cbuf_all yet again (duplicate) dogecoin_block_header_serialize(cbuf_all, header5_fork); // Define a constant buffer for each header @@ -466,10 +519,12 @@ void test_reorg() { struct const_buffer cbuf_header3 = {cbuf_all->str + 160, 80}; struct const_buffer cbuf_header4 = {cbuf_all->str + 240, 80}; struct const_buffer cbuf_header5_fork = {cbuf_all->str + 320, 80}; - struct const_buffer cbuf_header2_fork = {cbuf_all->str + 400, 80}; - struct const_buffer cbuf_header3_fork = {cbuf_all->str + 480, 80}; - struct const_buffer cbuf_header4_fork = {cbuf_all->str + 560, 80}; - struct const_buffer cbuf_header5_fork_again = {cbuf_all->str + 640, 80}; + struct const_buffer cbuf_header2_stale = {cbuf_all->str + 400, 80}; + struct const_buffer cbuf_header2_fork = {cbuf_all->str + 480, 80}; + struct const_buffer cbuf_header3_fork = {cbuf_all->str + 560, 80}; + struct const_buffer cbuf_header4_fork = {cbuf_all->str + 640, 80}; + struct const_buffer cbuf_header5_fork_again = {cbuf_all->str + 720, 80}; + struct const_buffer cbuf_header5_fork_duplicate = {cbuf_all->str + 800, 80}; // Connect the headers to the database dogecoin_bool connected; @@ -484,6 +539,8 @@ void test_reorg() { u_assert_true (connected); dogecoin_free(dogecoin_headers_db_connect_hdr(db, &cbuf_header5_fork, false, &connected)); u_assert_true (!connected); + dogecoin_headers_db_connect_hdr(db, &cbuf_header2_stale, false, &connected); + u_assert_true (connected); dogecoin_headers_db_connect_hdr(db, &cbuf_header2_fork, false, &connected); u_assert_true (connected); dogecoin_headers_db_connect_hdr(db, &cbuf_header3_fork, false, &connected); @@ -492,6 +549,8 @@ void test_reorg() { u_assert_true (connected); dogecoin_headers_db_connect_hdr(db, &cbuf_header5_fork_again, false, &connected); u_assert_true (connected); + dogecoin_free(dogecoin_headers_db_connect_hdr(db, &cbuf_header5_fork_duplicate, false, &connected)); + u_assert_true (!connected); // Cleanup cstr_free(cbuf_all, true); @@ -499,6 +558,7 @@ void test_reorg() { dogecoin_block_header_free(header2); dogecoin_block_header_free(header3); dogecoin_block_header_free(header4); + dogecoin_block_header_free(header2_stale); dogecoin_block_header_free(header2_fork); dogecoin_block_header_free(header3_fork); dogecoin_block_header_free(header4_fork);