| @ -0,0 +1,2 @@ | |||
| obj/* | |||
| bin/* | |||
| @ -0,0 +1,39 @@ | |||
| EXE=net_test listen_blockchain spv spv_full tx read_block btcspy block2json | |||
| include Makefile.inc | |||
| $(BINDIR)/net_test : $(OBJDIR)/net_test.o | |||
| $(CC) -o $@ $^ $(LDFLAGS) | |||
| net_test : $(BINDIR)/net_test | |||
| $(BINDIR)/listen_blockchain : $(OBJDIR)/listen_blockchain.o | |||
| $(CC) -o $@ $^ $(LDFLAGS) | |||
| listen_blockchain : $(BINDIR)/listen_blockchain | |||
| $(BINDIR)/spv : $(OBJDIR)/spv.o | |||
| $(CC) -o $@ $^ $(LDFLAGS) | |||
| spv : $(BINDIR)/spv | |||
| $(BINDIR)/spv_full : $(OBJDIR)/spv_full.o | |||
| $(CC) -o $@ $^ $(LDFLAGS) | |||
| spv_full : $(BINDIR)/spv_full | |||
| $(BINDIR)/read_block : $(OBJDIR)/read_block.o | |||
| $(CC) -o $@ $^ $(LDFLAGS) | |||
| read_block : $(BINDIR)/read_block | |||
| $(BINDIR)/btcspy : $(OBJDIR)/spy.o $(OBJDIR)/daemonize.o | |||
| $(CC) -o $@ $^ $(LDFLAGS) | |||
| btcspy : $(BINDIR)/btcspy | |||
| $(BINDIR)/block2json : $(OBJDIR)/block2json.o | |||
| $(CC) -o $@ $^ $(LDFLAGS) | |||
| block2json : $(BINDIR)/block2json | |||
| @ -0,0 +1,27 @@ | |||
| CC=gcc | |||
| IFLAGS=-I /usr/local/include/btc -I include | |||
| CFLAGS=-g | |||
| LDFLAGS=-lbtc | |||
| OBJDIR=obj | |||
| BINDIR=bin | |||
| SRCDIR=src | |||
| all: $(OBJDIR) $(BINDIR) $(EXE) | |||
| $(OBJDIR): | |||
| mkdir -p $(OBJDIR) | |||
| $(BINDIR): | |||
| mkdir -p $(BINDIR) | |||
| $(OBJDIR)/%.o : $(SRCDIR)/%.c | $(OBJDIR) $(BINDIR) | |||
| $(CC) $(IFLAGS) -o $@ -c $< $(CFLAGS) | |||
| .PHONY: clean mrproper $(EXE) | |||
| clean: | |||
| rm -f $(OBJDIR)/*.o | |||
| mrproper : clean | |||
| rm -rf $(OBJDIR) | |||
| rm -rf $(BINDIR) | |||
| @ -0,0 +1,6 @@ | |||
| #ifndef __MY_DAEMONIZE_H__ | |||
| #define __MY_DAEMONIZE_H__ | |||
| void daemonize(); | |||
| #endif | |||
| @ -0,0 +1,8 @@ | |||
| #!/bin/bash | |||
| scp -p Makefile root@topisto.net:/root/projets/my_libbtc_tools | |||
| scp -p -r include root@topisto.net:/root/projets/my_libbtc_tools | |||
| scp -p -r src root@topisto.net:/root/projets/my_libbtc_tools | |||
| scp -p data/block_ref root@topisto.net:/root/projets/my_libbtc_tools/data | |||
| @ -0,0 +1,17 @@ | |||
| { | |||
| "configurations": [ | |||
| { | |||
| "name": "Linux", | |||
| "includePath": [ | |||
| "/home/tibo/projets/libbtc/include/**", | |||
| "/home/tibo/projets/my_libbtc_tools/include" | |||
| ], | |||
| "defines": [], | |||
| "compilerPath": "/usr/bin/gcc", | |||
| "cStandard": "c99", | |||
| "cppStandard": "c++17", | |||
| "intelliSenseMode": "clang-x64" | |||
| } | |||
| ], | |||
| "version": 4 | |||
| } | |||
| @ -0,0 +1,25 @@ | |||
| #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> | |||
| int main(int ac, char** av) | |||
| { | |||
| printf("\nuint32_t : %ld", sizeof(uint32_t)); | |||
| printf("\nuint64_t : %ld", sizeof(uint64_t)); | |||
| printf("\n"); | |||
| } | |||
| @ -0,0 +1,108 @@ | |||
| #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 <btc/block.h> | |||
| #include <btc/net.h> | |||
| #include <btc/utils.h> | |||
| #include <btc/serialize.h> | |||
| #include <btc/tx.h> | |||
| #include <btc/utils.h> | |||
| btc_bool read_(FILE* block_fd) | |||
| { | |||
| uint8_t val_uint8_t; | |||
| uint32_t val_uint32_t; | |||
| uint64_t val_uint64_t; | |||
| uint256 val_uint256; | |||
| uint32_t nb_tx = 0; | |||
| uint64_t amount = 0; | |||
| uint64_t reward = 5000000000; | |||
| uint64_t fees = 0; | |||
| char hash_hex_str[65]; | |||
| printf("{\n"); | |||
| fread((void*) &val_uint32_t, sizeof(int32_t), 1, block_fd); | |||
| printf("\n\"Height\" : \"%u\",", val_uint32_t); | |||
| fread((void*) &val_uint256, sizeof(uint256), 1, block_fd); | |||
| utils_bin_to_hex(val_uint256, sizeof(uint256), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| printf("\n\"Hash\" : \"%s\",", hash_hex_str); | |||
| fread((void*) &val_uint32_t, sizeof(int32_t), 1, block_fd); | |||
| printf("\n\"Version\" : \"%u\",", val_uint32_t); | |||
| fread((void*) &val_uint32_t, sizeof(uint32_t), 1, block_fd); | |||
| printf("\n\"Timestamp\" : \"%u\",", val_uint32_t); | |||
| fread((void*) &val_uint32_t, sizeof(uint32_t), 1, block_fd); | |||
| printf("\n\"Nonce\" : \"%u\",", val_uint32_t); | |||
| fread((void*) &val_uint256, sizeof(uint256), 1, block_fd); | |||
| utils_bin_to_hex(val_uint256, sizeof(uint256), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| printf("\n\"Previous\" : \"%s\",", hash_hex_str); | |||
| fread((void*) &val_uint256, sizeof(uint256), 1, block_fd); | |||
| utils_bin_to_hex(val_uint256, sizeof(uint256), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| printf("\n\"Merkle\" : \"%s\",", hash_hex_str); | |||
| fread((void*) &nb_tx, sizeof(int32_t), 1, block_fd); | |||
| printf("\n\"TX\" : [ "); | |||
| for (unsigned int i = 0; i < nb_tx; i++) | |||
| { | |||
| if (!fread((void*) &val_uint256, sizeof(uint256), 1, block_fd)) break; | |||
| if (!fread((void*) &val_uint32_t, sizeof(uint32_t), 1, block_fd)) break; | |||
| if (!fread((void*) &val_uint64_t, sizeof(uint64_t), 1, block_fd)) break; | |||
| utils_bin_to_hex((unsigned char*)val_uint256, sizeof(uint256), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| if (i>0) printf(","); | |||
| printf("\n{\"Hash\":\"%s\", \"Timelock\":\"%u\",\"Amount\":\"%ju\"}", hash_hex_str, val_uint32_t, val_uint64_t); | |||
| amount += val_uint64_t; | |||
| // Compute reward and fees | |||
| if (i == 0) | |||
| { | |||
| while(reward > 0) | |||
| { | |||
| if (reward < val_uint64_t) break; | |||
| reward /= 2; | |||
| } | |||
| fees = val_uint64_t - reward; | |||
| } | |||
| } | |||
| printf("\n],"); | |||
| printf("\n\"Amount\" : \"%ju\",", amount); | |||
| printf("\n\"Reward\" : \"%ju\",", reward); | |||
| printf("\n\"Fees\" : \"%ju\"", fees); | |||
| printf("\n}\n"); | |||
| return true; | |||
| } | |||
| int main(int ac, char** av) | |||
| { | |||
| // Un fichier pour lire le block | |||
| FILE* block_fd; | |||
| block_fd = fopen(av[1],"rb"); | |||
| if (block_fd == NULL) return 1; | |||
| read_(block_fd); | |||
| fclose(block_fd); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,67 @@ | |||
| #include <stdio.h> | |||
| #include <fcntl.h> | |||
| #include <signal.h> | |||
| #include <unistd.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <sys/types.h> | |||
| #include <sys/stat.h> | |||
| #define RUNNING_DIR "/tmp" | |||
| #define LOCK_FILE "/tmp/daemond.lock" | |||
| #define LOG_FILE "daemond.log" | |||
| /* | |||
| * daemon.c | |||
| * | |||
| * Copyright 2010 Vasudev Kamath <kamathvasudev@gmail.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or modify | |||
| * it under the terms of the GNU Lesser General Public License as published by | |||
| * the Free Software Foundation; either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This program is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with this program; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
| * MA 02110-1301, USA. | |||
| */ | |||
| void daemonize(){ | |||
| int i,lfp; | |||
| char str[10]; | |||
| if(getppid() == 1) | |||
| return; | |||
| i = fork(); | |||
| if(i < 0) | |||
| exit(1); | |||
| if(i > 0) | |||
| exit(0); | |||
| setsid(); | |||
| for(i = getdtablesize(); i >= 0; --i) | |||
| close(i); | |||
| i = open("/dev/null",O_RDWR); | |||
| dup(i); | |||
| dup(i); | |||
| umask(022); | |||
| lfp = open(LOCK_FILE,O_RDWR|O_CREAT,0640); | |||
| if(lfp < 0) | |||
| exit(1); | |||
| if(lockf(lfp,F_TLOCK,0) < 0) | |||
| exit(1); | |||
| sprintf(str,"%d\n",getpid()); | |||
| write(lfp,str,strlen(str)); | |||
| } | |||
| @ -0,0 +1,290 @@ | |||
| #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 <btc/block.h> | |||
| #include <btc/net.h> | |||
| #include <btc/utils.h> | |||
| #include <btc/serialize.h> | |||
| #include <btc/tx.h> | |||
| btc_node_group* group = NULL; | |||
| /* | |||
| * Fermer correctement le réseau en cas d'interuption | |||
| */ | |||
| static void sig_handler(int signo) | |||
| { | |||
| if (signo == SIGINT) | |||
| { | |||
| printf("\nSIGINT catched\n"); | |||
| btc_node_group_shutdown(group); | |||
| } | |||
| } | |||
| static int default_write_log(const char *format, ...) | |||
| { | |||
| va_list args; | |||
| va_start(args, format); | |||
| vprintf(format, args); | |||
| va_end(args); | |||
| return 1; | |||
| } | |||
| FILE *fp = NULL; | |||
| btc_bool log_flag = false; | |||
| static int file_write_log(const char *format, ...) | |||
| { | |||
| if (log_flag) | |||
| { | |||
| if (fp == NULL) fp = fopen("log.txt","a+"); | |||
| va_list args; | |||
| va_start(args, format); | |||
| vfprintf(fp, format, args); | |||
| va_end(args); | |||
| } else { | |||
| if (fp != NULL) | |||
| { | |||
| fclose(fp); | |||
| fp = NULL; | |||
| } | |||
| } | |||
| return 1; | |||
| } | |||
| int nb_points = 0; | |||
| void init_nb_points() | |||
| { | |||
| printf("\n");fflush(stdout); | |||
| nb_points = 0; | |||
| } | |||
| void print_point(char c) | |||
| { | |||
| printf("%c",c);fflush(stdout); | |||
| nb_points += 1; | |||
| if (nb_points == 80) init_nb_points(); | |||
| } | |||
| btc_bool file_exist(const char *filename) | |||
| { | |||
| struct stat buffer; | |||
| return (stat (filename, &buffer) == 0); | |||
| } | |||
| /* | |||
| * Les callbacks | |||
| */ | |||
| static btc_bool timer_cb(btc_node *node, uint64_t *now) | |||
| { | |||
| /* return true = run internal timer logic (ping, disconnect-timeout, etc.) */ | |||
| return true; | |||
| } | |||
| btc_bool parse_cmd(struct btc_node_ *node, btc_p2p_msg_hdr *hdr, struct const_buffer *buf) | |||
| { | |||
| (void)(node); | |||
| (void)(hdr); | |||
| (void)(buf); | |||
| return true; | |||
| } | |||
| void post_cmd(struct btc_node_ *node, btc_p2p_msg_hdr *hdr, struct const_buffer *buf) | |||
| { | |||
| char point = '.'; | |||
| struct const_buffer buf_copy = { buf->p, buf->len }; | |||
| if (strcmp(hdr->command, BTC_MSG_INV) == 0) | |||
| { | |||
| point = 'i'; | |||
| 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); | |||
| } | |||
| /* send message */ | |||
| if (contains_block) btc_node_send(node, p2p_msg); | |||
| /* cleanup */ | |||
| cstr_free(p2p_msg, true); | |||
| } | |||
| if (strcmp(hdr->command, BTC_MSG_BLOCK) == 0) | |||
| { | |||
| FILE* block_fd; | |||
| char block_filename[85]; | |||
| const char* coinbase_hash = "0000000000000000000000000000000000000000000000000000000000000000"; | |||
| point = 'b'; | |||
| btc_block_header header; | |||
| uint256 hash_bin_val; | |||
| char hash_hex_str[65]; | |||
| uint32_t nb_tx; | |||
| unsigned long total_outputs = 0; | |||
| 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); | |||
| sprintf(block_filename,"data/simpledb/block_%s",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*)&(buf->len), sizeof(size_t), 1, block_fd); | |||
| fwrite((const void*)buf->p, sizeof(char), buf->len, block_fd); | |||
| fclose(block_fd); | |||
| printf("\nHash : %s", hash_hex_str); | |||
| printf("\nVersion : %d", header.version); | |||
| printf("\nTimestamp : %u", header.timestamp); | |||
| printf("\nNonce : %u", header.nonce); | |||
| memset(hash_hex_str, 0, sizeof(hash_hex_str)); | |||
| utils_bin_to_hex(header.prev_block, sizeof(header.prev_block), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| printf("\nPrev : %s", hash_hex_str); | |||
| printf("\nNb TX : %u", nb_tx); | |||
| memset(hash_hex_str, 0, sizeof(hash_hex_str)); | |||
| utils_bin_to_hex(header.merkle_root, sizeof(header.merkle_root), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| printf("\nMerkle : %s", hash_hex_str); | |||
| for (unsigned int i = 0; i < nb_tx; i++) | |||
| { | |||
| unsigned long 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; | |||
| } | |||
| /* | |||
| printf("\nTx : %d", i); | |||
| printf("\n\tVersion : %d", tx->version); | |||
| printf("\n\tLocktime : %d", tx->locktime); | |||
| printf("\n\tHash : %s", hash_hex_str); | |||
| printf("\n\tTotal (%ld) outputs : %ld", tx->vout->len, tx_total_outputs); | |||
| printf("\n"); | |||
| */ | |||
| fflush(stdout); | |||
| total_outputs += tx_total_outputs; | |||
| btc_tx_free(tx); | |||
| } | |||
| printf("\nTotal Block Outputs Amount : %ld", total_outputs); | |||
| printf("\n"); | |||
| fflush(stdout); | |||
| } | |||
| // print_point(point); | |||
| } | |||
| void node_connection_state_changed(struct btc_node_ *node) | |||
| { | |||
| int nb_connected = btc_node_group_amount_of_connected_nodes(node->nodegroup, NODE_CONNECTED); | |||
| /* | |||
| init_nb_points(); | |||
| printf("STATUS : connexion change status (%d node connected)\n", nb_connected); | |||
| */ | |||
| (void)(node); | |||
| } | |||
| void handshake_done(struct btc_node_ *node) | |||
| { | |||
| int nb_connected = btc_node_group_amount_of_connected_nodes(node->nodegroup, NODE_CONNECTED); | |||
| init_nb_points(); | |||
| // printf("STATUS : connexion change status (%d node connected)\n", nb_connected); | |||
| } | |||
| void do_the_main_job() | |||
| { | |||
| const btc_dns_seed seed = btc_chainparams_main.dnsseeds[0]; | |||
| vector* ips; | |||
| /* create a node group */ | |||
| group = btc_node_group_new(NULL); | |||
| group->desired_amount_connected_nodes = 1; | |||
| group->periodic_timer_cb = timer_cb; | |||
| group->log_write_cb = file_write_log; | |||
| group->parse_cmd_cb = parse_cmd; | |||
| group->postcmd_cb = post_cmd; | |||
| group->node_connection_state_changed_cb = node_connection_state_changed; | |||
| group->handshake_done_cb = handshake_done; | |||
| /* 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); | |||
| /* 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 | |||
| } | |||
| int main(int ac, char** av) | |||
| { | |||
| if (signal(SIGINT, sig_handler) == SIG_ERR) | |||
| { | |||
| printf("\ncan't catch SIGINT\n"); | |||
| return 1; | |||
| } | |||
| init_nb_points(); | |||
| do_the_main_job(); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,204 @@ | |||
| #include <btc/block.h> | |||
| #include <btc/net.h> | |||
| #include <btc/utils.h> | |||
| #include <btc/serialize.h> | |||
| #include <btc/tx.h> | |||
| static btc_bool timer_cb(btc_node *node, uint64_t *now) | |||
| { | |||
| /* | |||
| if (node->time_started_con + 300 < *now) | |||
| btc_node_disconnect(node); | |||
| */ | |||
| /* return true = run internal timer logic (ping, disconnect-timeout, etc.) */ | |||
| return true; | |||
| } | |||
| static int default_write_log(const char *format, ...) | |||
| { | |||
| va_list args; | |||
| va_start(args, format); | |||
| vprintf(format, args); | |||
| va_end(args); | |||
| return 1; | |||
| } | |||
| btc_bool parse_cmd(struct btc_node_ *node, btc_p2p_msg_hdr *hdr, struct const_buffer *buf) | |||
| { | |||
| (void)(node); | |||
| (void)(hdr); | |||
| (void)(buf); | |||
| return true; | |||
| } | |||
| void postcmd(struct btc_node_ *node, btc_p2p_msg_hdr *hdr, struct const_buffer *buf) | |||
| { | |||
| if (strcmp(hdr->command, "block") == 0) | |||
| { | |||
| btc_block_header header; | |||
| if (!btc_block_header_deserialize(&header, buf)) return; | |||
| uint32_t vsize; | |||
| if (!deser_varlen(&vsize, buf)) return; | |||
| for (unsigned int i = 0; i < vsize; i++) | |||
| { | |||
| btc_tx *tx = btc_tx_new(); //needs to be on the heep | |||
| btc_tx_deserialize(buf->p, buf->len, tx, NULL, true); | |||
| btc_tx_free(tx); | |||
| } | |||
| btc_node_disconnect(node); | |||
| } | |||
| if (strcmp(hdr->command, "inv") == 0) | |||
| { | |||
| // directly create a getdata message | |||
| cstring *p2p_msg = btc_p2p_message_new(node->nodegroup->chainparams->netmagic, "getdata", buf->p, buf->len); | |||
| uint32_t vsize; | |||
| uint8_t hash[36]; | |||
| uint32_t type; | |||
| if (!deser_varlen(&vsize, buf)) return; | |||
| for (unsigned int i = 0; i < vsize; i++) | |||
| { | |||
| if (!deser_u32(&type, buf)) return; | |||
| if (!deser_u256(hash, buf)) return; | |||
| } | |||
| /* send message */ | |||
| btc_node_send(node, p2p_msg); | |||
| /* cleanup */ | |||
| cstr_free(p2p_msg, true); | |||
| } | |||
| if (strcmp(hdr->command, "headers") == 0) | |||
| { | |||
| /* send getblock command */ | |||
| /* request some headers (from the genesis block) */ | |||
| vector *blocklocators = vector_new(1, NULL); | |||
| uint256 from_hash; | |||
| utils_uint256_sethex("000000000000000001e67f0781f5e31a62863e6d7a1a1f786c7f666a9954a648", from_hash); // height 428694 | |||
| uint256 stop_hash; | |||
| utils_uint256_sethex("00000000000000000378be785f464ef19243baba187cb3791ac92a69ca46bb46", stop_hash); // height 428695 | |||
| vector_add(blocklocators, from_hash); | |||
| cstring *getheader_msg = cstr_new_sz(256); | |||
| btc_p2p_msg_getheaders(blocklocators, stop_hash, getheader_msg); | |||
| /* create p2p message */ | |||
| cstring *p2p_msg = btc_p2p_message_new(node->nodegroup->chainparams->netmagic, "getblocks", getheader_msg->str, getheader_msg->len); | |||
| cstr_free(getheader_msg, true); | |||
| /* send message */ | |||
| btc_node_send(node, p2p_msg); | |||
| /* cleanup */ | |||
| vector_free(blocklocators, true); | |||
| cstr_free(p2p_msg, true); | |||
| } | |||
| } | |||
| void node_connection_state_changed(struct btc_node_ *node) | |||
| { | |||
| (void)(node); | |||
| } | |||
| void handshake_done(struct btc_node_ *node) | |||
| { | |||
| /* 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) | |||
| return; | |||
| } | |||
| // request some headers (from the genesis block) | |||
| vector *blocklocators = vector_new(1, NULL); | |||
| vector_add(blocklocators, (void *)node->nodegroup->chainparams->genesisblockhash); | |||
| cstring *getheader_msg = cstr_new_sz(256); | |||
| btc_p2p_msg_getheaders(blocklocators, NULL, getheader_msg); | |||
| /* create p2p message */ | |||
| cstring *p2p_msg = btc_p2p_message_new(node->nodegroup->chainparams->netmagic, "getheaders", getheader_msg->str, getheader_msg->len); | |||
| cstr_free(getheader_msg, true); | |||
| /* send message */ | |||
| node->state |= NODE_HEADERSYNC; | |||
| btc_node_send(node, p2p_msg); | |||
| /* cleanup */ | |||
| vector_free(blocklocators, true); | |||
| cstr_free(p2p_msg, true); | |||
| } | |||
| void test_net_basics_plus_download_block() | |||
| { | |||
| vector *ips = vector_new(10, free); | |||
| const btc_dns_seed seed = btc_chainparams_main.dnsseeds[0]; | |||
| btc_get_peers_from_dns(seed.domain, ips, btc_chainparams_main.default_port, AF_INET); | |||
| for (unsigned int i = 0; i<ips->len; i++) | |||
| { | |||
| char *ip = (char *)vector_idx(ips, i); | |||
| printf("dns seed ip %d: %s\n", i, ip); | |||
| } | |||
| vector_free(ips, true); | |||
| /* create a invalid node */ | |||
| btc_node *node_wrong = btc_node_new(); | |||
| btc_node_set_ipport(node_wrong, "0.0.0.1:1"); | |||
| /* create a invalid node to will run directly into a timeout */ | |||
| btc_node *node_timeout_direct = btc_node_new(); | |||
| btc_node_set_ipport(node_timeout_direct, "127.0.0.1:1234"); | |||
| /* create a invalid node to will run indirectly into a timeout */ | |||
| btc_node *node_timeout_indirect = btc_node_new(); | |||
| btc_node_set_ipport(node_timeout_indirect, "8.8.8.8:8333"); | |||
| /* create a node */ | |||
| btc_node *node = btc_node_new(); | |||
| btc_node_set_ipport(node, "192.99.8.123:8333"); | |||
| /* create a node group */ | |||
| btc_node_group* group = btc_node_group_new(NULL); | |||
| group->desired_amount_connected_nodes = 1; | |||
| /* add the node to the group */ | |||
| btc_node_group_add_node(group, node); | |||
| /* set the timeout callback */ | |||
| group->periodic_timer_cb = timer_cb; | |||
| /* set a individual log print function */ | |||
| group->log_write_cb = net_write_log_printf; | |||
| group->parse_cmd_cb = parse_cmd; | |||
| group->postcmd_cb = postcmd; | |||
| group->node_connection_state_changed_cb = node_connection_state_changed; | |||
| group->handshake_done_cb = handshake_done; | |||
| /* 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 | |||
| } | |||
| int main(int ac, char** av) | |||
| { | |||
| test_net_basics_plus_download_block(); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,99 @@ | |||
| #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 <btc/block.h> | |||
| #include <btc/net.h> | |||
| #include <btc/utils.h> | |||
| #include <btc/serialize.h> | |||
| #include <btc/tx.h> | |||
| #include <btc/utils.h> | |||
| btc_bool file_exist(const char *filename) | |||
| { | |||
| struct stat buffer; | |||
| return (stat (filename, &buffer) == 0); | |||
| } | |||
| btc_bool read_(FILE* block_fd) | |||
| { | |||
| uint8_t val_uint8_t; | |||
| uint32_t val_uint32_t; | |||
| uint64_t val_uint64_t; | |||
| uint256 val_uint256; | |||
| uint32_t nb_tx = 0; | |||
| uint64_t amount = 0; | |||
| char hash_hex_str[65]; | |||
| fread((void*) &val_uint32_t, sizeof(int32_t), 1, block_fd); | |||
| printf("\nHeight : %u", val_uint32_t); | |||
| fread((void*) &val_uint256, sizeof(uint256), 1, block_fd); | |||
| utils_bin_to_hex(val_uint256, sizeof(uint256), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| printf("\nHash : %s", hash_hex_str); | |||
| fread((void*) &val_uint32_t, sizeof(int32_t), 1, block_fd); | |||
| printf("\nVersion : %u", val_uint32_t); | |||
| fread((void*) &val_uint32_t, sizeof(uint32_t), 1, block_fd); | |||
| printf("\nTimestamp : %u", val_uint32_t); | |||
| fread((void*) &val_uint32_t, sizeof(uint32_t), 1, block_fd); | |||
| printf("\nNonce : %u", val_uint32_t); | |||
| fread((void*) &val_uint256, sizeof(uint256), 1, block_fd); | |||
| utils_bin_to_hex(val_uint256, sizeof(uint256), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| printf("\nPrevious : %s", hash_hex_str); | |||
| fread((void*) &val_uint256, sizeof(uint256), 1, block_fd); | |||
| utils_bin_to_hex(val_uint256, sizeof(uint256), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| printf("\nMerkle : %s", hash_hex_str); | |||
| fread((void*) &nb_tx, sizeof(int32_t), 1, block_fd); | |||
| printf("\nNb TX : %u", nb_tx); | |||
| for (unsigned int i = 0; i < nb_tx; i++) | |||
| { | |||
| if (!fread((void*) &val_uint256, sizeof(uint256), 1, block_fd)) break; | |||
| if (!fread((void*) &val_uint32_t, sizeof(uint32_t), 1, block_fd)) break; | |||
| if (!fread((void*) &val_uint64_t, sizeof(uint64_t), 1, block_fd)) break; | |||
| utils_bin_to_hex((unsigned char*)val_uint256, sizeof(uint256), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| printf("\n\t%u %s %u %ju", i, hash_hex_str, val_uint32_t, val_uint64_t); | |||
| amount += val_uint64_t; | |||
| } | |||
| printf("\nTotal Block Outputs Amount : %ju", amount); | |||
| printf("\n"); | |||
| return true; | |||
| } | |||
| btc_bool write_(FILE* block_fd) | |||
| { | |||
| return true; | |||
| } | |||
| int main(int ac, char** av) | |||
| { | |||
| // Un fichier pour lire le block | |||
| FILE* block_fd; | |||
| block_fd = fopen(av[1],"rb"); | |||
| if (block_fd == NULL) return 1; | |||
| read_(block_fd); | |||
| fclose(block_fd); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,202 @@ | |||
| #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 <btc/block.h> | |||
| #include <btc/net.h> | |||
| #include <btc/utils.h> | |||
| #include <btc/serialize.h> | |||
| #include <btc/tx.h> | |||
| btc_bool file_exist(const char *filename) | |||
| { | |||
| struct stat buffer; | |||
| return (stat (filename, &buffer) == 0); | |||
| } | |||
| btc_bool read_(FILE* block_fd) | |||
| { | |||
| uint32_t val_uint32_t; | |||
| uint64_t val_uint64_t; | |||
| uint256 val_uint256; | |||
| uint32_t nb_tx; | |||
| char hash_hex_str[65]; | |||
| fread((void*) &val_uint256, sizeof(uint256), 1, block_fd); | |||
| utils_bin_to_hex(val_uint256, sizeof(uint256), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| printf("\nHash : %s", hash_hex_str); | |||
| fread((void*) &val_uint32_t, sizeof(int32_t), 1, block_fd); | |||
| printf("\nVersion : %u", val_uint32_t); | |||
| fread((void*) &val_uint32_t, sizeof(uint32_t), 1, block_fd); | |||
| printf("\nTimestamp : %u", val_uint32_t); | |||
| fread((void*) &val_uint32_t, sizeof(uint32_t), 1, block_fd); | |||
| printf("\nNonce : %u", val_uint32_t); | |||
| fread((void*) &val_uint256, sizeof(uint256), 1, block_fd); | |||
| utils_bin_to_hex(val_uint256, sizeof(uint256), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| printf("\nPrevious : %s", hash_hex_str); | |||
| fread((void*) &val_uint256, sizeof(uint256), 1, block_fd); | |||
| utils_bin_to_hex(val_uint256, sizeof(uint256), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| printf("\nMerkle : %s", hash_hex_str); | |||
| fread((void*) &nb_tx, sizeof(uint32_t), 1, block_fd); | |||
| printf("\nNonce : %u", val_uint32_t); | |||
| while(nb_tx--) | |||
| { | |||
| fread((void*) &val_uint256, sizeof(uint256), 1, block_fd); | |||
| fread((void*) &val_uint32_t, sizeof(int32_t), 1, block_fd); | |||
| fread((void*) &val_uint64_t, sizeof(uint64_t), 1, block_fd); | |||
| utils_bin_to_hex(val_uint256, sizeof(uint256), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| printf("\n\t%s %u %lu", hash_hex_str, val_uint32_t, val_uint64_t); | |||
| } | |||
| printf("\n"); | |||
| return true; | |||
| } | |||
| btc_bool write_(FILE* block_fd) | |||
| { | |||
| return true; | |||
| } | |||
| int main(int ac, char** av) | |||
| { | |||
| // Un buffer pour stocker le block en mémoire | |||
| void* ptr = NULL; | |||
| struct const_buffer buf; | |||
| // Un fichier pour lire le block | |||
| FILE* block_fd; | |||
| char block_filename[85]; | |||
| const char* coinbase_hash = "0000000000000000000000000000000000000000000000000000000000000000"; | |||
| btc_block_header header; | |||
| uint256 hash_bin_val; | |||
| char hash_hex_str[65]; | |||
| uint32_t nb_tx = 0; | |||
| unsigned long total_outputs = 0; | |||
| btc_tx *tx; | |||
| block_fd = fopen(av[1],"rb"); | |||
| if (block_fd == NULL) return 1; | |||
| fread((void*)&(buf.len), sizeof(size_t), 1, block_fd); | |||
| ptr = calloc(sizeof(char), buf.len); | |||
| buf.p = ptr; | |||
| fread((void*)buf.p, sizeof(char), buf.len, block_fd); | |||
| fclose(block_fd); | |||
| if (!btc_block_header_deserialize(&header, &buf)) return 1; | |||
| if (!deser_varlen(&nb_tx, &buf)) return 1; | |||
| 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); | |||
| sprintf(block_filename,"data/simpledb/block_%s",hash_hex_str); | |||
| if (file_exist(block_filename)) unlink(block_filename); | |||
| // Save the block in a file | |||
| block_fd = fopen(block_filename,"wb"); | |||
| if (block_fd == NULL) return 1; | |||
| 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); | |||
| printf("\nHash : %s", hash_hex_str); | |||
| printf("\nVersion : %d", header.version); | |||
| printf("\nTimestamp : %u", header.timestamp); | |||
| printf("\nNonce : %u", header.nonce); | |||
| memset(hash_hex_str, 0, sizeof(hash_hex_str)); | |||
| utils_bin_to_hex(header.prev_block, sizeof(header.prev_block), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| printf("\nPrev : %s", hash_hex_str); | |||
| printf("\nNb TX : %d", nb_tx); | |||
| memset(hash_hex_str, 0, sizeof(hash_hex_str)); | |||
| utils_bin_to_hex(header.merkle_root, sizeof(header.merkle_root), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| printf("\nMerkle : %s", hash_hex_str); | |||
| for (unsigned int i = 0; i < nb_tx; i++) | |||
| { | |||
| int64_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.p, buf.len, tx, &consummed, true); | |||
| buf.p += consummed; | |||
| btc_tx_hash(tx, hash_bin_val); | |||
| fwrite((const void*) &hash_bin_val, sizeof(uint256), 1, block_fd); | |||
| fwrite((const void*) &tx->locktime, sizeof(uint32_t), 1, block_fd); | |||
| //printf("\nTx : %d", i); | |||
| //printf("\n\tVersion : %d", tx->version); | |||
| //printf("\n\tLocktime : %d", tx->locktime); | |||
| utils_bin_to_hex(hash_bin_val, sizeof(hash_bin_val), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| printf("\n\t%s", hash_hex_str); | |||
| 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"); | |||
| // printf("\n\t\tInput %lu :\n\t\t\tHash : %s\n\t\t\tOutput : %d", victx, hex_txin, tx_in->prevout.n); | |||
| } | |||
| for (size_t voctx = 0; voctx < tx->vout->len; voctx++) | |||
| { | |||
| btc_tx_out* tx_out = vector_idx(tx->vout, voctx); | |||
| // printf("\n\t\tOutput %lu :\n\t\t\tAmount : %ld", voctx, tx_out->value); | |||
| tx_total_outputs += tx_out->value; | |||
| } | |||
| fwrite((const void*) &tx_total_outputs, sizeof(uint64_t), 1, block_fd); | |||
| //printf("\n\tTotal (%ld) outputs : %ld", tx->vout->len, tx_total_outputs); | |||
| //printf("\n"); | |||
| printf(" %ld %d", tx_total_outputs, tx->locktime); | |||
| fflush(stdout); | |||
| total_outputs += tx_total_outputs; | |||
| btc_tx_free(tx); | |||
| } | |||
| printf("\nTotal Block Outputs Amount : %ld", total_outputs); | |||
| printf("\n"); | |||
| free(ptr); | |||
| fflush(stdout); | |||
| fclose(block_fd); | |||
| // Read the block from a file | |||
| block_fd = fopen(block_filename,"rb"); | |||
| if (block_fd == NULL) return 1; | |||
| read_(block_fd); | |||
| fclose(block_fd); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,21 @@ | |||
| #include<stdlib.h> | |||
| #include<stdio.h> | |||
| #include<signal.h> | |||
| #include<unistd.h> | |||
| void sig_handler(int signo) | |||
| { | |||
| if (signo == SIGINT) | |||
| printf("\nSIGINT catched\n"); | |||
| exit(0); | |||
| } | |||
| int main(void) | |||
| { | |||
| if (signal(SIGINT, sig_handler) == SIG_ERR) | |||
| printf("\ncan't catch SIGINT\n"); | |||
| // A long long wait so that we can easily issue a signal to this process | |||
| while(1) | |||
| sleep(1); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,187 @@ | |||
| #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 <btc/block.h> | |||
| #include <btc/net.h> | |||
| #include <btc/netspv.h> | |||
| #include <btc/utils.h> | |||
| btc_spv_client* spv_global_client; | |||
| /* | |||
| * Fermer correctement le réseau en cas d'interuption | |||
| */ | |||
| void sig_handler(int signo) | |||
| { | |||
| if (signo == SIGINT) | |||
| { | |||
| printf("\nSIGINT catched\n"); | |||
| fflush(stdout); | |||
| btc_node_group_shutdown(spv_global_client->nodegroup); | |||
| sleep(10); | |||
| } | |||
| } | |||
| /* | |||
| * Informer de la consommation mémoire du processus | |||
| */ | |||
| void print_mem_usage() | |||
| { | |||
| struct rusage r_usage; | |||
| getrusage(RUSAGE_SELF,&r_usage); | |||
| printf("Memory usage: %ld kilobytes\n",r_usage.ru_maxrss); | |||
| fflush(stdout); | |||
| } | |||
| btc_bool file_exist(const char *filename) | |||
| { | |||
| struct stat buffer; | |||
| return (stat (filename, &buffer) == 0); | |||
| } | |||
| btc_bool write_blockindex(btc_blockindex* blockindex) | |||
| { | |||
| FILE* block_fd = NULL; | |||
| char block_filename[2000]; | |||
| char hash_hex_str[65]; | |||
| utils_bin_to_hex(blockindex->hash, sizeof(uint256), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| sprintf(block_filename,"data/simpledb/block_%u_%s",blockindex->height, hash_hex_str); | |||
| if (file_exist(block_filename)) return false; | |||
| // Save the block in a file | |||
| block_fd = fopen(block_filename,"wb"); | |||
| if (block_fd == NULL) return false; | |||
| fwrite((const void*) &blockindex->height, sizeof(uint32_t), 1, block_fd); | |||
| fwrite((const void*) &blockindex->hash, sizeof(uint256), 1, block_fd); | |||
| fwrite((const void*) &blockindex->header.version, sizeof(int32_t), 1, block_fd); | |||
| fwrite((const void*) &blockindex->header.timestamp, sizeof(uint32_t), 1, block_fd); | |||
| fwrite((const void*) &blockindex->header.nonce, sizeof(uint32_t), 1, block_fd); | |||
| fwrite((const void*) &blockindex->header.prev_block, sizeof(uint256), 1, block_fd); | |||
| fwrite((const void*) &blockindex->header.merkle_root, sizeof(uint256), 1, block_fd); | |||
| fclose(block_fd); | |||
| return true; | |||
| } | |||
| btc_bool add_blockindex_tx(btc_blockindex* blockindex, btc_tx* tx) | |||
| { | |||
| FILE* block_fd = NULL; | |||
| char block_filename[2000]; | |||
| char hash_hex_str[65]; | |||
| uint256 hash_bin_val; | |||
| uint64_t tx_amount = 0; | |||
| utils_bin_to_hex(blockindex->hash, sizeof(uint256), hash_hex_str); | |||
| utils_reverse_hex(hash_hex_str, 64); | |||
| btc_tx_hash(tx, hash_bin_val); | |||
| for (size_t voctx = 0; voctx < tx->vout->len; voctx++) | |||
| { | |||
| btc_tx_out* tx_out = vector_idx(tx->vout, voctx); | |||
| tx_amount += tx_out->value; | |||
| } | |||
| sprintf(block_filename,"data/simpledb/block_%u_%s",blockindex->height, hash_hex_str); | |||
| if (!file_exist(block_filename)) write_blockindex(blockindex); | |||
| // Save the block in a file | |||
| block_fd = fopen(block_filename,"ab"); | |||
| if (block_fd == NULL) return false; | |||
| 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_amount, sizeof(uint64_t), 1, block_fd); | |||
| fclose(block_fd); | |||
| return true; | |||
| } | |||
| /* | |||
| * Callback de fin de synchro | |||
| */ | |||
| void test_spv_sync_completed(btc_spv_client* client) | |||
| { | |||
| UNUSED(client); | |||
| printf("\nSync completed, at height %d\n", client->headers_db->getchaintip(client->headers_db_ctx)->height); | |||
| fflush(stdout); | |||
| } | |||
| /* | |||
| * Callback de message "header" | |||
| */ | |||
| btc_bool test_spv_header_message_processed(struct btc_spv_client_ *client, btc_node *node, btc_blockindex *newtip) | |||
| { | |||
| UNUSED(client); | |||
| UNUSED(node); | |||
| printf("\nGet SPV Header message\n"); | |||
| if (newtip) { | |||
| printf("\nNew headers tip height %d\n", newtip->height); | |||
| } | |||
| fflush(stdout); | |||
| return true; | |||
| } | |||
| /* | |||
| * Callback de nouveau dernier block connu | |||
| */ | |||
| void test_spv_header_connected(struct btc_spv_client_ *client) | |||
| { | |||
| btc_blockindex* blockindex = client->headers_db->getchaintip(client->headers_db_ctx); | |||
| write_blockindex(blockindex); | |||
| printf("\nSPV Header Connected : %d\n", blockindex->height); | |||
| fflush(stdout); | |||
| } | |||
| /* | |||
| * Callback de nouvelle transaction dans le dernier block connu | |||
| */ | |||
| void test_spv_sync_transaction(void *ctx, btc_tx *tx, unsigned int pos, btc_blockindex *blockindex) | |||
| { | |||
| UNUSED(ctx); | |||
| UNUSED(pos); | |||
| add_blockindex_tx(blockindex, tx); | |||
| } | |||
| int main(int ac, char** av) | |||
| { | |||
| /* | |||
| if (signal(SIGINT, sig_handler) == SIG_ERR) | |||
| { | |||
| printf("\ncan't catch SIGINT\n"); | |||
| return 1; | |||
| } | |||
| */ | |||
| spv_global_client = btc_spv_client_new(&btc_chainparams_main, false, false); | |||
| spv_global_client->header_message_processed = test_spv_header_message_processed; | |||
| spv_global_client->sync_completed = test_spv_sync_completed; | |||
| spv_global_client->header_connected = test_spv_header_connected; | |||
| spv_global_client->sync_transaction = test_spv_sync_transaction; | |||
| btc_spv_client_load(spv_global_client, "data/index.db"); | |||
| printf("Discover peers..."); | |||
| btc_spv_client_discover_peers(spv_global_client, NULL); | |||
| printf("done\n"); | |||
| printf("Start interacting with the p2p network...\n"); | |||
| btc_spv_client_runloop(spv_global_client); | |||
| btc_spv_client_free(spv_global_client); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,56 @@ | |||
| #include <btc/block.h> | |||
| #include <btc/net.h> | |||
| #include <btc/netspv.h> | |||
| #include <sys/resource.h> | |||
| #include <stdio.h> | |||
| #include <unistd.h> | |||
| void print_mem_usage() | |||
| { | |||
| struct rusage r_usage; | |||
| getrusage(RUSAGE_SELF,&r_usage); | |||
| printf("Memory usage: %ld kilobytes\n",r_usage.ru_maxrss); | |||
| } | |||
| void test_spv_sync_completed(btc_spv_client* client) { | |||
| print_mem_usage(); | |||
| printf("Sync completed, at height %d\n", client->headers_db->getchaintip(client->headers_db_ctx)->height); | |||
| // btc_node_group_shutdown(client->nodegroup); | |||
| } | |||
| btc_bool test_spv_header_message_processed(struct btc_spv_client_ *client, btc_node *node, btc_blockindex *newtip) { | |||
| UNUSED(client); | |||
| UNUSED(node); | |||
| printf("Get SPV Header message\n"); | |||
| if (newtip) { | |||
| printf("New headers tip height %d\n", newtip->height); | |||
| } | |||
| return true; | |||
| } | |||
| void test_netspv() | |||
| { | |||
| // unlink("headers.db"); | |||
| btc_spv_client* client = btc_spv_client_new(&btc_chainparams_main, false, false); | |||
| client->header_message_processed = test_spv_header_message_processed; | |||
| client->sync_completed = test_spv_sync_completed; | |||
| client->use_checkpoints = false; // Start from genesis | |||
| btc_spv_client_load(client, "data/full.db"); | |||
| printf("Discover peers..."); | |||
| btc_spv_client_discover_peers(client, NULL); | |||
| printf("done\n"); | |||
| printf("Start interacting with the p2p network...\n"); | |||
| btc_spv_client_runloop(client); | |||
| btc_spv_client_free(client); | |||
| } | |||
| int main(int ac, char** av) | |||
| { | |||
| print_mem_usage(); | |||
| test_netspv(); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,809 @@ | |||
| #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){ | |||
| 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; | |||
| fclose(blockchain_fd); | |||
| blockchain_fd = NULL; | |||
| } | |||
| } | |||
| 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; | |||
| } | |||
| int main(int ac, char** av) | |||
| { | |||
| btc_node_group* group = NULL; | |||
| cb_file_write_log_flag = false; | |||
| cb_file_write_log_filter = "STATUS"; | |||
| /* | |||
| * Daemonize | |||
| */ | |||
| daemonize(); | |||
| /* | |||
| * Trapper les signaux pour fermer correctement le programme | |||
| */ | |||
| if (signal(SIGCHLD,SIG_IGN) == SIG_ERR) | |||
| { | |||
| printf("\ncan't catch SIGCHLD\n"); | |||
| return 1; | |||
| } | |||
| if (signal(SIGTSTP,SIG_IGN) == SIG_ERR) | |||
| { | |||
| printf("\ncan't catch SIGTSTP\n"); | |||
| return 1; | |||
| } | |||
| if (signal(SIGTTOU,SIG_IGN) == SIG_ERR) | |||
| { | |||
| printf("\ncan't catch SIGTTOU\n"); | |||
| return 1; | |||
| } | |||
| if (signal(SIGTTIN,SIG_IGN) == SIG_ERR) | |||
| { | |||
| printf("\ncan't catch SIGTTIN\n"); | |||
| return 1; | |||
| } | |||
| if (signal(SIGHUP,signal_handler) == SIG_ERR) | |||
| { | |||
| printf("\ncan't catch SIGHUP\n"); | |||
| return 1; | |||
| } | |||
| if (signal(SIGTERM,signal_handler) == SIG_ERR) | |||
| { | |||
| printf("\ncan't catch SIGTERM\n"); | |||
| return 1; | |||
| } | |||
| if (signal(SIGINT, signal_handler) == SIG_ERR) | |||
| { | |||
| printf("\ncan't catch SIGINT\n"); | |||
| return 1; | |||
| } | |||
| /* | |||
| * Gestion du fichier de chaine de blocks | |||
| */ | |||
| if (ac > 1) simple_db_path = av[1]; | |||
| 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; | |||
| } | |||
| @ -0,0 +1,27 @@ | |||
| CC=gcc | |||
| IFLAGS=-I /usr/local/include/btc | |||
| CFLAGS=-g | |||
| LDFLAGS=-lbtc | |||
| OBJDIR=obj | |||
| BINDIR=bin | |||
| SRCDIR=src | |||
| all: $(OBJDIR) $(BINDIR) $(EXE) | |||
| $(OBJDIR): | |||
| mkdir -p $(OBJDIR) | |||
| $(BINDIR): | |||
| mkdir -p $(BINDIR) | |||
| $(OBJDIR)/%.o : $(SRCDIR)/%.c | $(OBJDIR) $(BINDIR) | |||
| $(CC) $(IFLAGS) -o $@ -c $< $(CFLAGS) | |||
| .PHONY: clean mrproper $(EXE) | |||
| clean: | |||
| rm -f $(OBJDIR)/*.o | |||
| mrproper : clean | |||
| rm -rf $(OBJDIR) | |||
| rm -rf $(BINDIR) | |||