| @ -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; | |||
| } | |||