diff --git a/README.md b/README.md index acebe6a..02199c2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ # btcspy -a tool to observe btc blokcchain \ No newline at end of file +a tool to observe btc blokcchain + diff --git a/src/spy2.c b/src/spy2.c index a76669f..2fe59fb 100644 --- a/src/spy2.c +++ b/src/spy2.c @@ -20,6 +20,7 @@ */ char* simple_db_path = "data/simpledb"; FILE* blockchain_fd = NULL; +FILE* emptyblock_fd = NULL; /* * Les outils @@ -234,6 +235,7 @@ static void cb_handshake_done(struct btc_node_ *node) #define MARGE_TAMPON 20 char buffer[MARGE_TAMPON][65]; uint32_t read_height[MARGE_TAMPON]; + uint32_t read_nb_tx[MARGE_TAMPON]; fpos_t fpos[MARGE_TAMPON]; (void) fseek(blockchain_fd, 0L, SEEK_SET); for(uint i = 0; i < MARGE_TAMPON; i++) @@ -245,13 +247,14 @@ static void cb_handshake_done(struct btc_node_ *node) request_headers_or_blocks_hash_init = false; - while(fscanf(blockchain_fd, "%s %d\n", buffer[0], read_height)) + while(fscanf(blockchain_fd, "%s %d %d\n", buffer[0], read_height, read_nb_tx)) { /* 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]; + read_nb_tx[i] = read_nb_tx[i-1]; memcpy(fpos+i, fpos+(i-1), sizeof(fpos_t)); } @@ -260,7 +263,7 @@ static void cb_handshake_done(struct btc_node_ *node) if (read_height[0] > MARGE_TAMPON) { /* - * On se sert du haut d ela pile comme point de départ + * On se sert du haut de la pile comme point de départ */ int outlen; node->nodegroup->log_write_cb("STATUS : Setting requested hash with %s\n", buffer[19]); @@ -366,7 +369,7 @@ static void cb_post_cmd(struct btc_node_ *node, btc_p2p_msg_hdr *hdr, struct con uint64_t reward = 5000000000; /* - * La synchro des ent^tes n'est pas terminée + * 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; @@ -489,6 +492,17 @@ static void cb_post_cmd(struct btc_node_ *node, btc_p2p_msg_hdr *hdr, struct con } } + + if (nb_tx == 1) + { + if (emptyblock_fd != NULL) + { + (void) fseek(emptyblock_fd, 0L, SEEK_END); + fprintf(emptyblock_fd, "%s %d\n", hash_hex_str, block_height); + fflush(emptyblock_fd); + } + } + sprintf(block_filename,"%s/blocks/%u_%s", simple_db_path, block_height, hash_hex_str); if (file_exist(block_filename)) return; @@ -613,7 +627,7 @@ static void cb_post_cmd(struct btc_node_ *node, btc_p2p_msg_hdr *hdr, struct con if (!flag_local) { (void) fseek(blockchain_fd, 0L, SEEK_END); - fprintf(blockchain_fd, "%s %d\n", hash_hex_str,cb_handshake_done_bestknownheight ); + fprintf(blockchain_fd, "%s %d\n", hash_hex_str,cb_handshake_done_bestknownheight); } } } else { @@ -641,24 +655,30 @@ static void cb_post_cmd(struct btc_node_ *node, btc_p2p_msg_hdr *hdr, struct con } /* - * Gestion du fichier de chaine de blocks + * Init d'un eliste de blocs */ -int init_blockchain_file() +FILE* init_blocklist_file(char* filename) { - const char* blockchain_filename = "blockchain.txt"; + FILE* fd = NULL; 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; + blockchain_full_filename = calloc(strlen(simple_db_path)+strlen(filename)+2, sizeof(char)); + if (blockchain_full_filename == NULL) return NULL; + + /* + * + * init de la liste des blocs vides + * + */ - sprintf(blockchain_full_filename,"%s/%s", simple_db_path, blockchain_filename); + sprintf(blockchain_full_filename,"%s/%s", simple_db_path, filename); if (!file_exist(blockchain_full_filename)) { - blockchain_fd = fopen(blockchain_full_filename,"w"); - if (blockchain_fd != NULL) + fd = fopen(blockchain_full_filename,"w"); + if (fd != NULL) { int outlen; @@ -668,9 +688,9 @@ int init_blockchain_file() sprintf(buffer, "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); block_height = 0; - fprintf(blockchain_fd, "%s %d\n", buffer, block_height); - fclose(blockchain_fd); - blockchain_fd = NULL; + fprintf(fd, "%s %d\n", buffer, block_height); + fclose(fd); + fd = NULL; utils_reverse_hex(buffer, 64); utils_hex_to_bin(buffer, request_headers_or_blocks_hash, 64, &outlen); @@ -680,7 +700,30 @@ int init_blockchain_file() } } - blockchain_fd = fopen(blockchain_full_filename,"r+"); + fd = fopen(blockchain_full_filename,"r+"); + return fd; +} + +/* + * Gestion du fichier de chaine de blocks + */ +int init_blockchain_file() +{ + const char* blockchain_filename = "blockchain.txt"; + const char* emptyblock_filename = "emptyblocks.txt"; + char* blockchain_full_filename = NULL; + char buffer[65]; + uint32_t block_height; + + /* + * Init de la liste des blocks vides + */ + emptyblock_fd = init_blocklist_file("emptyblocks.txt"); + + /* + * Init de la liste complete des blocks + */ + blockchain_fd = init_blocklist_file("blockchain.txt"); if (blockchain_fd != NULL) { while(fscanf(blockchain_fd, "%s %d\n", buffer, &block_height)) diff --git a/src/spy3.c b/src/spy3.c new file mode 100644 index 0000000..2fe59fb --- /dev/null +++ b/src/spy3.c @@ -0,0 +1,795 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * Les variables globales + */ +char* simple_db_path = "data/simpledb"; +FILE* blockchain_fd = NULL; +FILE* emptyblock_fd = NULL; + +/* + * Les outils + */ +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; ilen; 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 (blockchain_fd != NULL) + { + /* + * Définir et initialiser un tampon de 20 blocks + * Ca veut dire qu'on considère qu'il n'y a pas de fork + * de plus de 20 blocks + */ +#define MARGE_TAMPON 20 + char buffer[MARGE_TAMPON][65]; + uint32_t read_height[MARGE_TAMPON]; + uint32_t read_nb_tx[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 %d\n", buffer[0], read_height, read_nb_tx)) + { + /* 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]; + read_nb_tx[i] = read_nb_tx[i-1]; + memcpy(fpos+i, fpos+(i-1), sizeof(fpos_t)); + } + + if (feof(blockchain_fd)) break; + } + if (read_height[0] > MARGE_TAMPON) + { + /* + * On se sert du haut de la pile comme point de départ + */ + 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)); + } + } + + if (node->bestknownheight > cb_handshake_done_bestknownheight) + { + 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); + } + } + + + if (nb_tx == 1) + { + if (emptyblock_fd != NULL) + { + (void) fseek(emptyblock_fd, 0L, SEEK_END); + fprintf(emptyblock_fd, "%s %d\n", hash_hex_str, block_height); + fflush(emptyblock_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); + // TRES TRES SALE ... + exit(0); + } + } +} + +/* + * Init d'un eliste de blocs + */ +FILE* init_blocklist_file(char* filename) +{ + FILE* fd = NULL; + char* blockchain_full_filename = NULL; + char buffer[65]; + uint32_t block_height; + + blockchain_full_filename = calloc(strlen(simple_db_path)+strlen(filename)+2, sizeof(char)); + if (blockchain_full_filename == NULL) return NULL; + + /* + * + * init de la liste des blocs vides + * + */ + + sprintf(blockchain_full_filename,"%s/%s", simple_db_path, filename); + + if (!file_exist(blockchain_full_filename)) + { + fd = fopen(blockchain_full_filename,"w"); + if (fd != NULL) + { + int outlen; + + /* + * Init with Genesis Block + */ + sprintf(buffer, "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); + block_height = 0; + + fprintf(fd, "%s %d\n", buffer, block_height); + fclose(fd); + 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; + } + } + + fd = fopen(blockchain_full_filename,"r+"); + return fd; +} + +/* + * Gestion du fichier de chaine de blocks + */ +int init_blockchain_file() +{ + const char* blockchain_filename = "blockchain.txt"; + const char* emptyblock_filename = "emptyblocks.txt"; + char* blockchain_full_filename = NULL; + char buffer[65]; + uint32_t block_height; + + /* + * Init de la liste des blocks vides + */ + emptyblock_fd = init_blocklist_file("emptyblocks.txt"); + + /* + * Init de la liste complete des blocks + */ + blockchain_fd = init_blocklist_file("blockchain.txt"); + 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]); + + /* + * 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; +}