| @ -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) | |||||