|
|
@ -0,0 +1,795 @@ |
|
|
|
|
|
#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>
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* 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; 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 (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;
|
|
|
|
|
|
}
|