| @ -0,0 +1,802 @@ | |||||
| #include <sys/resource.h> | |||||
| #include <sys/types.h> | |||||
| #include <sys/stat.h> | |||||
| #include <stdlib.h> | |||||
| #include <stdio.h> | |||||
| #include <signal.h> | |||||
| #include <unistd.h> | |||||
| #include <time.h> | |||||
| #include <btc/block.h> | |||||
| #include <btc/net.h> | |||||
| #include <btc/utils.h> | |||||
| #include <btc/serialize.h> | |||||
| #include <btc/memory.h> | |||||
| #include <btc/tx.h> | |||||
| #include <daemonize.h> | |||||
| /* | |||||
| * Les variables globales | |||||
| */ | |||||
| char* simple_db_path = "data/simpledb"; | |||||
| FILE* blockchain_fd = NULL; | |||||
| /* | |||||
| * Les outils | |||||
| */ | |||||
| static void signal_handler(int sig){ | |||||
| char msg[200]; | |||||
| sprintf(msg,"process %d signal %d trapped\n", getpid(), sig); | |||||
| log_signal_message(msg); | |||||
| switch(sig){ | |||||
| case SIGUSR1: | |||||
| // signal USR1 | |||||
| break; | |||||
| case SIGUSR2: | |||||
| // Signal USR2 | |||||
| break; | |||||
| case SIGHUP: | |||||
| // Reload conf and reinitialize processus | |||||
| break; | |||||
| case SIGINT: | |||||
| // CTRL+C | |||||
| case SIGTERM: | |||||
| // Terminate : kill -15 | |||||
| exit(0); | |||||
| break; | |||||
| } | |||||
| } | |||||
| static btc_bool file_exist(const char *filename) | |||||
| { | |||||
| struct stat buffer; | |||||
| return (stat (filename, &buffer) == 0); | |||||
| } | |||||
| /* | |||||
| * Les fonctions spécfiques | |||||
| */ | |||||
| void request_header_or_block_from_hashbin(btc_node *node, uint256* hash, btc_bool blocks) | |||||
| { | |||||
| if (node == NULL) return; | |||||
| if (hash == NULL) return; | |||||
| // request next headers | |||||
| vector* blocklocators = vector_new(1, NULL); | |||||
| cstring* getheader_msg = cstr_new_sz(256); | |||||
| cstring* p2p_msg = NULL; | |||||
| vector_add(blocklocators, (void *)hash); | |||||
| btc_p2p_msg_getheaders(blocklocators, NULL, getheader_msg); | |||||
| /* create p2p message */ | |||||
| p2p_msg = btc_p2p_message_new(node->nodegroup->chainparams->netmagic, (blocks ? BTC_MSG_GETBLOCKS : BTC_MSG_GETHEADERS), getheader_msg->str, getheader_msg->len); | |||||
| /* send message */ | |||||
| btc_node_send(node, p2p_msg); | |||||
| node->state |= ( blocks ? NODE_BLOCKSYNC : NODE_HEADERSYNC); | |||||
| /* remember last headers request time */ | |||||
| if (blocks) { | |||||
| node->time_last_request = time(NULL); | |||||
| } | |||||
| /* cleanup */ | |||||
| vector_free(blocklocators, true); | |||||
| cstr_free(p2p_msg, true); | |||||
| cstr_free(getheader_msg, true); | |||||
| } | |||||
| void request_header_or_block_from_hashhex(btc_node *node, const char* hex, btc_bool blocks) | |||||
| { | |||||
| if (node == NULL) return; | |||||
| if (hex == NULL) return; | |||||
| uint256 *hash = btc_calloc(1, sizeof(uint256)); | |||||
| if (hash == NULL) return; | |||||
| utils_uint256_sethex((char *)hex, (uint8_t *)hash); | |||||
| request_header_or_block_from_hashbin(node,hash,blocks); | |||||
| } | |||||
| uint256 request_headers_or_blocks_hash; | |||||
| btc_bool request_headers_or_blocks_hash_init = false; | |||||
| void request_headers_or_blocks(btc_node *node, btc_bool blocks) | |||||
| { | |||||
| /* Sans initialisation, on repart du Genesis Block */ | |||||
| if (!request_headers_or_blocks_hash_init) | |||||
| { | |||||
| node->nodegroup->log_write_cb("STATUS : Setting requested hash with genesis block\n"); | |||||
| memcpy(&request_headers_or_blocks_hash, node->nodegroup->chainparams->genesisblockhash, sizeof(uint256)); | |||||
| request_headers_or_blocks_hash_init = true; | |||||
| } | |||||
| // On lance la recherche la synchro | |||||
| if (request_headers_or_blocks_hash_init) | |||||
| request_header_or_block_from_hashbin(node, &request_headers_or_blocks_hash, blocks); | |||||
| } | |||||
| static void find_and_add_nodes(btc_node_group* group) | |||||
| { | |||||
| const btc_dns_seed seed = btc_chainparams_main.dnsseeds[0]; | |||||
| vector* ips; | |||||
| /* collect and add some btc nodes to the group */ | |||||
| ips = vector_new(group->desired_amount_connected_nodes + 5, free); | |||||
| btc_get_peers_from_dns(seed.domain, ips, btc_chainparams_main.default_port, AF_INET); | |||||
| for (unsigned int i = 0; i<ips->len; i++) | |||||
| { | |||||
| /* create a node */ | |||||
| btc_node *node = btc_node_new(); | |||||
| char *ip = (char *)vector_idx(ips, i); | |||||
| btc_node_set_ipport(node, ip); | |||||
| btc_node_group_add_node(group, node); | |||||
| } | |||||
| vector_free(ips, true); | |||||
| /* connect to the next node */ | |||||
| btc_node_group_connect_next_nodes(group); | |||||
| } | |||||
| static void check_connected_nodes(btc_node_group* group) | |||||
| { | |||||
| int nb_connected = btc_node_group_amount_of_connected_nodes(group, NODE_CONNECTED); | |||||
| if (nb_connected < group->desired_amount_connected_nodes) | |||||
| { | |||||
| group->log_write_cb("STATUS : new connection needed (%d node connected)\n", nb_connected); | |||||
| find_and_add_nodes(group); | |||||
| } | |||||
| } | |||||
| /* | |||||
| * Les callbacks | |||||
| */ | |||||
| static int cb_default_write_log(const char *format, ...) | |||||
| { | |||||
| va_list args; | |||||
| va_start(args, format); | |||||
| vprintf(format, args); | |||||
| va_end(args); | |||||
| return 1; | |||||
| } | |||||
| FILE* cb_file_write_log_fp = NULL; | |||||
| btc_bool cb_file_write_log_flag = false; | |||||
| char* cb_file_write_log_filter = ""; | |||||
| static int cb_file_write_log(const char *format, ...) | |||||
| { | |||||
| if (cb_file_write_log_flag) | |||||
| { | |||||
| // Appliquer le filtre | |||||
| unsigned int limit = strlen(cb_file_write_log_filter); | |||||
| for(unsigned int i=0; i < limit; i++) | |||||
| if (format[i] != cb_file_write_log_filter[i]) | |||||
| return 1; | |||||
| // Ouvrir le flix si besoin | |||||
| if (cb_file_write_log_fp == NULL) | |||||
| cb_file_write_log_fp = fopen("log.txt","a+"); | |||||
| // Impossible d'ouvrir le fichier de log | |||||
| if (cb_file_write_log_fp == NULL) return 0; | |||||
| va_list args; | |||||
| va_start(args, format); | |||||
| vfprintf(cb_file_write_log_fp, format, args); | |||||
| va_end(args); | |||||
| fflush(cb_file_write_log_fp); | |||||
| } else { | |||||
| if (cb_file_write_log_fp != NULL) | |||||
| { | |||||
| // Fermer le flux | |||||
| fclose(cb_file_write_log_fp); | |||||
| cb_file_write_log_fp = NULL; | |||||
| } | |||||
| } | |||||
| return 1; | |||||
| } | |||||
| static btc_bool cb_timer(btc_node *node, uint64_t *now) | |||||
| { | |||||
| check_connected_nodes(node->nodegroup); | |||||
| /* return true = run internal timer logic (ping, disconnect-timeout, etc.) */ | |||||
| return true; | |||||
| } | |||||
| static btc_bool cb_parse_cmd(struct btc_node_ *node, btc_p2p_msg_hdr *hdr, struct const_buffer *buf) | |||||
| { | |||||
| (void)(node); | |||||
| (void)(hdr); | |||||
| (void)(buf); | |||||
| return true; | |||||
| } | |||||
| static void cb_node_connection_state_changed(struct btc_node_ *node) | |||||
| { | |||||
| int nb_connected = btc_node_group_amount_of_connected_nodes(node->nodegroup, NODE_CONNECTED); | |||||
| node->nodegroup->log_write_cb("STATUS : connexion change status (%d node connected)\n", nb_connected); | |||||
| check_connected_nodes(node->nodegroup); | |||||
| } | |||||
| unsigned int cb_handshake_done_bestknownheight = 0; | |||||
| static void cb_handshake_done(struct btc_node_ *node) | |||||
| { | |||||
| int nb_connected = btc_node_group_amount_of_connected_nodes(node->nodegroup, NODE_CONNECTED); | |||||
| node->nodegroup->log_write_cb("STATUS : handshake (%d node connected)\n", nb_connected); | |||||
| /* make sure only one node is used for header sync */ | |||||
| for(size_t i =0;i< node->nodegroup->nodes->len; i++) | |||||
| { | |||||
| btc_node *check_node = vector_idx(node->nodegroup->nodes, i); | |||||
| if ( ( (check_node->state & NODE_HEADERSYNC) == NODE_HEADERSYNC | |||||
| || | |||||
| (check_node->state & NODE_BLOCKSYNC) == NODE_BLOCKSYNC | |||||
| ) | |||||
| && | |||||
| (check_node->state & NODE_CONNECTED) == NODE_CONNECTED) | |||||
| return; | |||||
| } | |||||
| /* | |||||
| * Si le nouveau noeud a une meilleur connaissance de la blockchain que nous | |||||
| * On relance une synchro de la blockchain | |||||
| * Si possible on repart de 20 blocks en arrière | |||||
| * Cela pour éviter de rester coincés dans un fork | |||||
| * | |||||
| * De même, on garde une marge de manoeuvre dans les positions | |||||
| * du fichier | |||||
| */ | |||||
| if (node->bestknownheight > cb_handshake_done_bestknownheight) | |||||
| { | |||||
| if (blockchain_fd != NULL) | |||||
| { | |||||
| /* | |||||
| * Définir et intialiser un tampon de 20 blocks | |||||
| */ | |||||
| #define MARGE_TAMPON 25 | |||||
| char buffer[MARGE_TAMPON][65]; | |||||
| uint32_t read_height[MARGE_TAMPON]; | |||||
| fpos_t fpos[MARGE_TAMPON]; | |||||
| (void) fseek(blockchain_fd, 0L, SEEK_SET); | |||||
| for(uint i = 0; i < MARGE_TAMPON; i++) | |||||
| { | |||||
| fgetpos(blockchain_fd, fpos+i); | |||||
| strcpy(buffer[i],""); | |||||
| read_height[i] = 0; | |||||
| } | |||||
| request_headers_or_blocks_hash_init = false; | |||||
| while(fscanf(blockchain_fd, "%s %d\n", buffer[0], read_height)) | |||||
| { | |||||
| /* Décaler le tampon d'un cran vers le bas */ | |||||
| for(uint i = (MARGE_TAMPON-1); i > 0 ; i--) | |||||
| { | |||||
| strcpy(buffer[i], buffer[i-1]); | |||||
| read_height[i] = read_height[i-1]; | |||||
| memcpy(fpos+i, fpos+(i-1), sizeof(fpos_t)); | |||||
| } | |||||
| if (feof(blockchain_fd)) break; | |||||
| } | |||||
| if (read_height[0] > MARGE_TAMPON) | |||||
| { | |||||
| int outlen; | |||||
| node->nodegroup->log_write_cb("STATUS : Setting requested hash with %s\n", buffer[19]); | |||||
| utils_reverse_hex(buffer[19], 64); | |||||
| utils_hex_to_bin(buffer[19], request_headers_or_blocks_hash, 64, &outlen); | |||||
| cb_handshake_done_bestknownheight = read_height[19]; | |||||
| request_headers_or_blocks_hash_init = true; | |||||
| fsetpos(blockchain_fd, fpos+(MARGE_TAMPON-1)); | |||||
| } | |||||
| } | |||||
| node->nodegroup->log_write_cb("STATUS : starting header sync from node %d\n", node->nodeid); | |||||
| node->state |= NODE_HEADERSYNC; | |||||
| request_headers_or_blocks(node, false); | |||||
| } else { | |||||
| node->state &= ~NODE_HEADERSYNC; | |||||
| } | |||||
| } | |||||
| int btc_header_hash_compare(uint8_t *hashA, uint8_t *hashB) | |||||
| { | |||||
| /* byte per byte compare */ | |||||
| for (unsigned int i = 0; i < sizeof(uint256); i++) { | |||||
| uint8_t iA = hashA[i]; | |||||
| uint8_t iB = hashB[i]; | |||||
| if (iA > iB) | |||||
| return -1; | |||||
| else if (iA < iB) | |||||
| return 1; | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| static void post_inv_cmd(struct btc_node_ *node, struct const_buffer *buf) | |||||
| { | |||||
| struct const_buffer buf_copy = { buf->p, buf->len }; | |||||
| btc_bool contains_block = false; | |||||
| uint32_t vsize; | |||||
| // directly create a getdata message | |||||
| cstring *p2p_msg = btc_p2p_message_new(node->nodegroup->chainparams->netmagic, BTC_MSG_GETDATA, buf->p, buf->len); | |||||
| if (!deser_varlen(&vsize, &buf_copy)) return; | |||||
| for (unsigned int i = 0; i < vsize; i++) | |||||
| { | |||||
| uint8_t hash[36]; | |||||
| uint32_t type; | |||||
| if (!deser_u32(&type, &buf_copy)) return; | |||||
| if (!deser_u256(hash, &buf_copy)) return; | |||||
| contains_block |= (type == BTC_INV_TYPE_BLOCK); | |||||
| } | |||||
| /* | |||||
| * This a MSG for inventory one block | |||||
| */ | |||||
| if (contains_block && (vsize == 1)) | |||||
| { | |||||
| node->time_last_request = time(NULL); | |||||
| btc_node_send(node, p2p_msg); | |||||
| } | |||||
| /* cleanup */ | |||||
| cstr_free(p2p_msg, true); | |||||
| } | |||||
| static void cb_post_cmd(struct btc_node_ *node, btc_p2p_msg_hdr *hdr, struct const_buffer *buf) | |||||
| { | |||||
| struct const_buffer buf_copy = { buf->p, buf->len }; | |||||
| uint256 hash_bin_val; | |||||
| char hash_hex_str[65]; | |||||
| /* | |||||
| * MSG is inventory | |||||
| */ | |||||
| if (strcmp(hdr->command, BTC_MSG_INV) == 0) | |||||
| { | |||||
| post_inv_cmd(node, buf); | |||||
| } | |||||
| /* | |||||
| * MSG is block data | |||||
| */ | |||||
| if (strcmp(hdr->command, BTC_MSG_BLOCK) == 0) | |||||
| { | |||||
| FILE* block_fd; | |||||
| char block_filename[2000]; | |||||
| char prev_hex_str[65]; | |||||
| btc_bool is_a_known_block = false; | |||||
| const char* coinbase_hash = "0000000000000000000000000000000000000000000000000000000000000000"; | |||||
| btc_block_header header; | |||||
| uint32_t nb_tx; | |||||
| uint32_t block_height = 0; | |||||
| uint64_t total_outputs = 0; | |||||
| uint64_t total_fees = 0; | |||||
| uint64_t reward = 5000000000; | |||||
| /* | |||||
| * La synchro des ent^tes n'est pas terminée | |||||
| * Dans ce cas on ne traite pas le bloc ... | |||||
| */ | |||||
| if (node->bestknownheight > cb_handshake_done_bestknownheight) return; | |||||
| /* | |||||
| * Désérialisation du bloc | |||||
| */ | |||||
| if (!btc_block_header_deserialize(&header, &buf_copy)) return; | |||||
| if (!deser_varlen(&nb_tx, &buf_copy)) return; | |||||
| btc_block_header_hash(&header, (uint8_t *)&hash_bin_val); | |||||
| memset(hash_hex_str, 0, sizeof(hash_hex_str)); | |||||
| utils_bin_to_hex(hash_bin_val, sizeof(hash_bin_val), hash_hex_str); | |||||
| utils_reverse_hex(hash_hex_str, 64); | |||||
| memset(prev_hex_str, 0, sizeof(prev_hex_str)); | |||||
| utils_bin_to_hex(header.prev_block, sizeof(header.prev_block), prev_hex_str); | |||||
| utils_reverse_hex(prev_hex_str, 64); | |||||
| // Nous connaissons déjà ce block comme étant le sommet de la blockchain | |||||
| if (0 == btc_header_hash_compare((uint8_t *)&hash_bin_val, (uint8_t *)&request_headers_or_blocks_hash)) return; | |||||
| // Est-ce un nouveau sommet de la blockchain ? | |||||
| if (0 == btc_header_hash_compare((uint8_t *)&header.prev_block, (uint8_t *)&request_headers_or_blocks_hash)) | |||||
| { | |||||
| cb_handshake_done_bestknownheight += 1; | |||||
| memcpy(&request_headers_or_blocks_hash, &hash_bin_val, sizeof(uint256)); | |||||
| block_height = cb_handshake_done_bestknownheight; | |||||
| node->nodegroup->log_write_cb("STATUS : New chain top detected (height is now %d) !\n",cb_handshake_done_bestknownheight); | |||||
| } else { | |||||
| node->nodegroup->log_write_cb("STATUS : Orphan block detected !\n"); | |||||
| /* | |||||
| * TODO | |||||
| * Conserver une liste des blocks orphelins | |||||
| * Grâce au timer, tenter de les rattacher après coup | |||||
| * | |||||
| * On peut recevoir un nouveau block alors que nous sommes | |||||
| * en train de se sycnhroniser. | |||||
| * | |||||
| * Il peut également s'agir d'un début de fork. | |||||
| */ | |||||
| if (blockchain_fd != NULL) | |||||
| { | |||||
| char buffer[65]; | |||||
| uint32_t read_height = 0; | |||||
| (void) fseek(blockchain_fd, 0L, SEEK_SET); | |||||
| while(fscanf(blockchain_fd, "%s %d\n", buffer, &read_height)) | |||||
| { | |||||
| if (!strcmp(buffer, hash_hex_str)) | |||||
| { | |||||
| /* | |||||
| * Ce block est déjà connu | |||||
| */ | |||||
| is_a_known_block = true; | |||||
| block_height = read_height; | |||||
| (void) fseek(blockchain_fd, 0L, SEEK_END); | |||||
| node->nodegroup->log_write_cb("STATUS : Known block detected (%d) !\n", block_height); | |||||
| break; | |||||
| } | |||||
| if (!strcmp(buffer, prev_hex_str)) | |||||
| { | |||||
| /* | |||||
| * Ce block est potentiellement un début de fork | |||||
| * Il n'est pas au sommet | |||||
| * Mais on connait son parent | |||||
| */ | |||||
| block_height = read_height + 1; | |||||
| } | |||||
| if (feof(blockchain_fd)) break; | |||||
| } | |||||
| (void) fseek(blockchain_fd, 0L, SEEK_END); | |||||
| } | |||||
| if (!is_a_known_block) | |||||
| { | |||||
| if (block_height > 0) | |||||
| { | |||||
| /* | |||||
| * Ce block est potentiellement un début de fork | |||||
| * On le place au sommet | |||||
| * Attention : | |||||
| * Je ne suis pas sûr que c'est ce qu'il faut faire ... | |||||
| */ | |||||
| memcpy(&request_headers_or_blocks_hash, &hash_bin_val, sizeof(uint256)); | |||||
| cb_handshake_done_bestknownheight = block_height; | |||||
| node->nodegroup->log_write_cb("STATUS : Fork block detected (%d) !\n", block_height); | |||||
| } else { | |||||
| /* | |||||
| * Le block a-t-il moins de 2 heures ? | |||||
| */ | |||||
| if (difftime(time(NULL), header.timestamp) < (2*3600)) | |||||
| { | |||||
| /* | |||||
| * Ce block est un orphelin | |||||
| * TODO | |||||
| * Keep in memory and use the timer to try to connect it again to the blockchain | |||||
| */ | |||||
| if (node->bestknownheight > cb_handshake_done_bestknownheight) | |||||
| { | |||||
| /* | |||||
| * La synchronisation n'est pas finie | |||||
| */ | |||||
| node->nodegroup->log_write_cb("STATUS : Orphan block while synchro !\n"); | |||||
| } else { | |||||
| node->nodegroup->log_write_cb("STATUS : Orphan block detected !\n"); | |||||
| } | |||||
| } | |||||
| return; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (!is_a_known_block) | |||||
| { | |||||
| if (blockchain_fd != NULL) | |||||
| { | |||||
| (void) fseek(blockchain_fd, 0L, SEEK_END); | |||||
| fprintf(blockchain_fd, "%s %d\n", hash_hex_str, block_height); | |||||
| fflush(blockchain_fd); | |||||
| } | |||||
| } | |||||
| sprintf(block_filename,"%s/blocks/%u_%s", simple_db_path, block_height, hash_hex_str); | |||||
| if (file_exist(block_filename)) return; | |||||
| // Save the block in a file | |||||
| block_fd = fopen(block_filename,"wb"); | |||||
| if (block_fd == NULL) return; | |||||
| fwrite((const void*) &block_height, sizeof(uint32_t), 1, block_fd); | |||||
| fwrite((const void*) &hash_bin_val, sizeof(uint256), 1, block_fd); | |||||
| fwrite((const void*) &header.version, sizeof(int32_t), 1, block_fd); | |||||
| fwrite((const void*) &header.timestamp, sizeof(uint32_t), 1, block_fd); | |||||
| fwrite((const void*) &header.nonce, sizeof(uint32_t), 1, block_fd); | |||||
| fwrite((const void*) &header.prev_block, sizeof(uint256), 1, block_fd); | |||||
| fwrite((const void*) &header.merkle_root, sizeof(uint256), 1, block_fd); | |||||
| fwrite((const void*) &nb_tx, sizeof(uint32_t), 1, block_fd); | |||||
| for (unsigned int i = 0; i < nb_tx; i++) | |||||
| { | |||||
| uint64_t tx_total_outputs = 0; | |||||
| size_t consummed = 0; | |||||
| btc_tx* tx =btc_tx_new(); //needs to be on the heap | |||||
| btc_tx_deserialize(buf_copy.p, buf_copy.len, tx, &consummed, true); | |||||
| deser_skip(&buf_copy, consummed); | |||||
| btc_tx_hash(tx, hash_bin_val); | |||||
| utils_bin_to_hex(hash_bin_val, sizeof(hash_bin_val), hash_hex_str); | |||||
| utils_reverse_hex(hash_hex_str, 64); | |||||
| for (size_t victx = 0; victx < tx->vin->len; victx++) | |||||
| { | |||||
| btc_tx_in* tx_in = vector_idx(tx->vin, victx); | |||||
| char* hex_txin = utils_uint8_to_hex(tx_in->prevout.hash, 32); | |||||
| utils_reverse_hex(hex_txin, strlen(hex_txin)); | |||||
| if (!strncmp(hex_txin,coinbase_hash,strlen(coinbase_hash))) strcpy(hex_txin,"COINBASE"); | |||||
| } | |||||
| for (size_t voctx = 0; voctx < tx->vout->len; voctx++) | |||||
| { | |||||
| btc_tx_out* tx_out = vector_idx(tx->vout, voctx); | |||||
| tx_total_outputs += tx_out->value; | |||||
| } | |||||
| fwrite((const void*) &hash_bin_val, sizeof(uint256), 1, block_fd); | |||||
| fwrite((const void*) &tx->locktime, sizeof(uint32_t), 1, block_fd); | |||||
| fwrite((const void*) &tx_total_outputs, sizeof(uint64_t), 1, block_fd); | |||||
| if (i == 0) | |||||
| { | |||||
| // Compute fees | |||||
| while(reward > 0) | |||||
| { | |||||
| if (reward < tx_total_outputs) | |||||
| break; | |||||
| reward /= 2; | |||||
| } | |||||
| total_fees = tx_total_outputs - reward; | |||||
| } | |||||
| total_outputs += tx_total_outputs; | |||||
| btc_tx_free(tx); | |||||
| } | |||||
| fclose(block_fd); | |||||
| fflush(stdout); | |||||
| } | |||||
| /* | |||||
| * MSG is headers data | |||||
| */ | |||||
| if (strcmp(hdr->command, BTC_MSG_HEADERS) == 0) | |||||
| { | |||||
| uint32_t amount_of_headers; | |||||
| if (!deser_varlen(&amount_of_headers, &buf_copy)) return; | |||||
| node->nodegroup->log_write_cb("STATUS : (%d) headers received\n", amount_of_headers); | |||||
| while(amount_of_headers--) | |||||
| { | |||||
| uint256 hash_bin_val; | |||||
| btc_block_header header; | |||||
| if (!btc_block_header_deserialize(&header, &buf_copy)) return; | |||||
| if (!deser_skip(&buf_copy, 1)) return; | |||||
| btc_block_header_hash(&header, (uint8_t *)&hash_bin_val); | |||||
| if (0 == btc_header_hash_compare((uint8_t *)&header.prev_block, (uint8_t *)&request_headers_or_blocks_hash)) | |||||
| { | |||||
| cb_handshake_done_bestknownheight += 1; | |||||
| memcpy(&request_headers_or_blocks_hash, &hash_bin_val, sizeof(uint256)); | |||||
| memset(hash_hex_str, 0, sizeof(hash_hex_str)); | |||||
| utils_bin_to_hex(hash_bin_val, sizeof(hash_bin_val), hash_hex_str); | |||||
| utils_reverse_hex(hash_hex_str, 64); | |||||
| if (blockchain_fd != NULL) | |||||
| { | |||||
| // Ne rajouter un bloc que si on ne le connait pas | |||||
| char buffer[65]; | |||||
| uint32_t read_height; | |||||
| btc_bool flag_local = false; | |||||
| /* | |||||
| * Cette ligen a été mise en commentaire | |||||
| * Car elle ralenti énormément le processus de synchro | |||||
| * Elle fait repartir du début du fichier à chaque fois. | |||||
| * Pour accélérer le processus, on se cale en début de synchro (cd_handcheck_done) | |||||
| * Puis on ne fait qu'avancer dans le fichier | |||||
| * Attention : | |||||
| * Cela suppose que le noeud nous envoie les entêtes de blocks dans l'ordre | |||||
| */ | |||||
| // (void) fseek(blockchain_fd, 0L, SEEK_SET); | |||||
| while(fscanf(blockchain_fd, "%s %d\n", buffer, &read_height)) | |||||
| { | |||||
| if (!strcmp(buffer, hash_hex_str)) | |||||
| { | |||||
| flag_local = true; | |||||
| break; | |||||
| } | |||||
| if (feof(blockchain_fd)) break; | |||||
| } | |||||
| if (!flag_local) | |||||
| { | |||||
| (void) fseek(blockchain_fd, 0L, SEEK_END); | |||||
| fprintf(blockchain_fd, "%s %d\n", hash_hex_str,cb_handshake_done_bestknownheight ); | |||||
| } | |||||
| } | |||||
| } else { | |||||
| node->nodegroup->log_write_cb("STATUS : Orphan block header !\n"); | |||||
| } | |||||
| } | |||||
| if (node->bestknownheight > cb_handshake_done_bestknownheight) | |||||
| { | |||||
| /* | |||||
| * La synchronisation n'est pas finie | |||||
| * Demander d'autres blocs | |||||
| */ | |||||
| node->nodegroup->log_write_cb("STATUS : bestknownheigt (%d / %d)\n", cb_handshake_done_bestknownheight, node->bestknownheight); | |||||
| request_headers_or_blocks(node, false); | |||||
| } else { | |||||
| node->nodegroup->log_write_cb("STATUS : Headers sync is finished !\n", cb_handshake_done_bestknownheight, node->bestknownheight); | |||||
| node->state &= ~NODE_HEADERSYNC; | |||||
| if (blockchain_fd != NULL) | |||||
| fflush(blockchain_fd); | |||||
| } | |||||
| } | |||||
| } | |||||
| /* | |||||
| * Gestion du fichier de chaine de blocks | |||||
| */ | |||||
| int init_blockchain_file() | |||||
| { | |||||
| const char* blockchain_filename = "blockchain.txt"; | |||||
| char* blockchain_full_filename = NULL; | |||||
| char buffer[65]; | |||||
| uint32_t block_height; | |||||
| blockchain_full_filename = calloc(strlen(simple_db_path)+strlen(blockchain_filename)+2, sizeof(char)); | |||||
| if (blockchain_full_filename == NULL) return 1; | |||||
| sprintf(blockchain_full_filename,"%s/%s", simple_db_path, blockchain_filename); | |||||
| if (!file_exist(blockchain_full_filename)) | |||||
| { | |||||
| blockchain_fd = fopen(blockchain_full_filename,"w"); | |||||
| if (blockchain_fd != NULL) | |||||
| { | |||||
| int outlen; | |||||
| /* | |||||
| * Init with Genesis Block | |||||
| */ | |||||
| sprintf(buffer, "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); | |||||
| block_height = 0; | |||||
| fprintf(blockchain_fd, "%s %d\n", buffer, block_height); | |||||
| fclose(blockchain_fd); | |||||
| blockchain_fd = NULL; | |||||
| utils_reverse_hex(buffer, 64); | |||||
| utils_hex_to_bin(buffer, request_headers_or_blocks_hash, 64, &outlen); | |||||
| request_headers_or_blocks_hash_init = true; | |||||
| cb_handshake_done_bestknownheight = block_height; | |||||
| } | |||||
| } | |||||
| blockchain_fd = fopen(blockchain_full_filename,"r+"); | |||||
| if (blockchain_fd != NULL) | |||||
| { | |||||
| while(fscanf(blockchain_fd, "%s %d\n", buffer, &block_height)) | |||||
| { | |||||
| if (block_height > cb_handshake_done_bestknownheight) | |||||
| { | |||||
| int outlen; | |||||
| utils_reverse_hex(buffer, 64); | |||||
| utils_hex_to_bin(buffer, request_headers_or_blocks_hash, 64, &outlen); | |||||
| request_headers_or_blocks_hash_init = true; | |||||
| cb_handshake_done_bestknownheight = block_height; | |||||
| } | |||||
| if (feof(blockchain_fd)) break; | |||||
| } | |||||
| } | |||||
| free(blockchain_full_filename); | |||||
| return 0; | |||||
| } | |||||
| #define array_len(a) ( sizeof(a) / sizeof(a[0]) ) | |||||
| int main(int ac, char** av) | |||||
| { | |||||
| btc_node_group* group = NULL; | |||||
| cb_file_write_log_flag = false; | |||||
| cb_file_write_log_filter = "STATUS"; | |||||
| /* | |||||
| * if parameter, change dir to this param | |||||
| */ | |||||
| if (ac > 1) chdir(av[1]); | |||||
| /* | |||||
| * Daemonize | |||||
| */ | |||||
| daemonize(); | |||||
| /* | |||||
| * Trapper les signaux pour fermer correctement le programme | |||||
| */ | |||||
| int les_signaux_ign[] = { SIGCHLD, SIGTSTP, SIGTTOU, SIGTTIN }; | |||||
| int les_signaux[] = { SIGHUP, SIGTERM, SIGINT }; | |||||
| size_t n; | |||||
| n = array_len(les_signaux_ign); | |||||
| for(int i = 0; i < n; i++) | |||||
| { | |||||
| if (signal(les_signaux_ign[i],SIG_IGN) == SIG_ERR) | |||||
| { | |||||
| printf("\ncan't catch signal %d\n", les_signaux_ign[i]); | |||||
| return 1; | |||||
| } | |||||
| } | |||||
| n = array_len(les_signaux); | |||||
| for(int i = 0; i < n; i++) | |||||
| { | |||||
| if (signal(les_signaux[i],signal_handler) == SIG_ERR) | |||||
| { | |||||
| printf("\ncan't catch signal %d\n", les_signaux[i]); | |||||
| return 1; | |||||
| } | |||||
| } | |||||
| /* | |||||
| * Gestion du fichier de chaine de blocks | |||||
| */ | |||||
| if (0 != init_blockchain_file()) return 1; | |||||
| /* | |||||
| * Create a node group | |||||
| */ | |||||
| group = btc_node_group_new(NULL); | |||||
| group->desired_amount_connected_nodes = 1; | |||||
| group->periodic_timer_cb = cb_timer; | |||||
| group->log_write_cb = cb_file_write_log; | |||||
| group->parse_cmd_cb = cb_parse_cmd; | |||||
| group->postcmd_cb = cb_post_cmd; | |||||
| group->node_connection_state_changed_cb = cb_node_connection_state_changed; | |||||
| group->handshake_done_cb = cb_handshake_done; | |||||
| sprintf(group->clientstr,"TOPISTO is using libbtc"); | |||||
| find_and_add_nodes(group); | |||||
| /* connect to the next node */ | |||||
| btc_node_group_connect_next_nodes(group); | |||||
| /* start the event loop */ | |||||
| btc_node_group_event_loop(group); | |||||
| /* cleanup */ | |||||
| btc_node_group_free(group); //will also free the nodes structures from the heap | |||||
| return 0; | |||||
| } | |||||