diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e8563d..813e26a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,8 @@ set(INCLUDE_TX "" CACHE INTERNAL "used internally") set(INCLUDE_BLOCK "" CACHE INTERNAL "used internally") set(INCLUDE_GLOBAL "" CACHE INTERNAL "used internally") set(INCLUDE_TEST "" CACHE INTERNAL "used internally") +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) set(UTHASH_IN_PROJ "src/includes/uthash/") set(INCLUDE_ALL diff --git a/src/core/blocks/base_block.c b/src/core/blocks/base_block.c index 3a093b5..98d50cf 100644 --- a/src/core/blocks/base_block.c +++ b/src/core/blocks/base_block.c @@ -3,6 +3,7 @@ #include #include "base_block.h" +#include "base_tx.h" #include "ser_block.h" #include "crypto.h" @@ -56,3 +57,15 @@ void pretty_print_block(Block *block, char *prefix){ printf(LINE_BREAK); } } + +void free_block(Block *block){ + if(block != NULL){ + if(block->txs != NULL){ + for(unsigned int i = 0; i < block->num_txs; i++ ){ + free_tx(block->txs[i]); + } + free(block->txs); + } + free(block); + } +} \ No newline at end of file diff --git a/src/core/globals/init_globals.c b/src/core/globals/init_globals.c index 204ba04..f30f7f9 100644 --- a/src/core/globals/init_globals.c +++ b/src/core/globals/init_globals.c @@ -4,11 +4,14 @@ #include "utxo_pool.h" #include "mempool.h" #include "init_db.h" +#include "wallet_pool.h" void node_init(char *db_env) { // Read from file for chain height and top block hash blockchain_init_leveldb(db_env); utxo_pool_init_leveldb(db_env); + wallet_init_leveldb(db_env); + mempool_init(); } void miner_init() { diff --git a/src/core/txs/handle_tx.c b/src/core/txs/handle_tx.c index c8d2009..2af37ca 100644 --- a/src/core/txs/handle_tx.c +++ b/src/core/txs/handle_tx.c @@ -4,7 +4,8 @@ #include "utxo_to_tx.h" int handle_tx(Transaction *tx){ - if(mempool_add(tx) == NULL){ + Transaction *coppied_tx = copy_tx(tx); + if(mempool_add(coppied_tx) == NULL){ return 1; } return 0; diff --git a/src/core/utils/CMakeLists.txt b/src/core/utils/CMakeLists.txt index e179bc8..e3ad9b4 100644 --- a/src/core/utils/CMakeLists.txt +++ b/src/core/utils/CMakeLists.txt @@ -8,4 +8,10 @@ target_link_libraries(crypto add_library(init_db STATIC init_db.c) target_include_directories(init_db PUBLIC ${INCLUDE_ALL}) target_link_libraries(init_db - leveldb) \ No newline at end of file + leveldb) + +add_library(queue STATIC queue.c) +target_include_directories(queue PUBLIC ${INCLUDE_ALL}) +target_link_libraries(queue + core_tx + core_block) \ No newline at end of file diff --git a/src/core/utils/queue.c b/src/core/utils/queue.c new file mode 100644 index 0000000..022cfa7 --- /dev/null +++ b/src/core/utils/queue.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include "pthread.h" +#include "queue.h" +#include "base_tx.h" +#include "base_block.h" +#include + +Queue *queue_init(){ + Queue *new_q = malloc(sizeof(Queue)); + new_q->head = NULL; + new_q->tail = NULL; + //INitialize sepmaphores and lock + sem_init(&new_q->sem_len, 0, 0); + if (pthread_mutex_init(&new_q->lock, NULL) != 0) { + printf("\n mutex init has failed\n"); + return NULL; + } + //pthread_mutex_unlock(&new_q->lock); + return new_q; +} + +void queue_destroy(Queue *existing_q){ + pthread_mutex_lock(&existing_q->lock); // Maybe we don't need to lock if we are destroying? + pthread_mutex_destroy(&existing_q->lock); + QueueItem *cur_item = existing_q->head; + QueueItem *next_item = NULL; + while(cur_item != NULL){ + next_item = cur_item->next; + free(cur_item); + cur_item = next_item; + } + free(existing_q); +} + +int queue_add_void(Queue *existing_q, void *new_item){ + pthread_mutex_lock(&existing_q->lock); + QueueItem *new_entry = malloc(sizeof(QueueItem)); + new_entry->item = new_item; + new_entry->next = NULL; + if(!existing_q->tail && !existing_q->head){ + existing_q->tail = new_entry; + existing_q->head = new_entry; + } + else{ + existing_q->tail->next = new_entry; + existing_q->tail = new_entry; + } + pthread_mutex_unlock(&existing_q->lock); + int ret = sem_post(&existing_q->sem_len); + return 0; +} + +void *queue_pop_void(Queue *existing_q){ + sem_wait(&existing_q->sem_len); + pthread_mutex_lock(&existing_q->lock); + if(!existing_q->head){ + fprintf(stderr, "Popping off empty Queue"); + exit(1); + } + + QueueItem *popped_item = existing_q->head; + existing_q->head = existing_q->head->next; + // Check to see if tail should be set to Null + if(!existing_q->head){ + existing_q->tail = NULL; + } + void *ret = popped_item->item; + free(popped_item); + pthread_mutex_unlock(&existing_q->lock); + return ret; +} diff --git a/src/includes/blocks/base_block.h b/src/includes/blocks/base_block.h index 2233563..2325b3f 100644 --- a/src/includes/blocks/base_block.h +++ b/src/includes/blocks/base_block.h @@ -22,6 +22,13 @@ typedef struct Block{ */ void hash_blockheader(unsigned char *dest, BlockHeader *header); +/** + * @brief Frees a block and it's containing transactions safely + * + * @param block to free + */ +void free_block(Block *block); + /* Prints a Block Header to stdout so data can be visualized diff --git a/src/includes/globals/constants.h b/src/includes/globals/constants.h index 85a8f90..9277c5f 100644 --- a/src/includes/globals/constants.h +++ b/src/includes/globals/constants.h @@ -10,7 +10,7 @@ #define ERR_BUF 1024 #define BLOCK_REWARD 100 #define DESIRED_NUM_TX 10 -#define HASH_DIFFICULTY 2 +#define HASH_DIFFICULTY 3 #define MINING_NODE 1 #define WALLET_NODE 1 diff --git a/src/includes/utils/queue.h b/src/includes/utils/queue.h new file mode 100644 index 0000000..b6f3c58 --- /dev/null +++ b/src/includes/utils/queue.h @@ -0,0 +1,26 @@ +#pragma once +#include +#include +#include +#include +#include "base_tx.h" +#include "base_block.h" + +typedef struct QueueItem{ + void *item; + struct QueueItem *next; +} QueueItem; + +typedef struct Queue { + QueueItem *head; + QueueItem *tail; + pthread_mutex_t lock; + sem_t sem_len; +} Queue; + +Queue *queue_init(); + +void queue_destroy(Queue *exisiting_q); + +int queue_add_void(Queue *existing_q, void *new_item); +void *queue_pop_void(Queue *existing_q); \ No newline at end of file diff --git a/src/runtime/CMakeLists.txt b/src/runtime/CMakeLists.txt index 1849eb5..f4ed096 100644 --- a/src/runtime/CMakeLists.txt +++ b/src/runtime/CMakeLists.txt @@ -4,7 +4,7 @@ message(STATUS "Building Runtime") # Add inlcudes for the runtime headers add_subdirectory(includes) -add_executable(shell shell.c) +add_library(shell shell.c) target_compile_options(shell PRIVATE ${COMPILE_OPTIONS_STD}) target_include_directories(shell PUBLIC ${INCLUDE_ALL} @@ -14,3 +14,15 @@ target_link_libraries(shell wallet handle_block init_globals) + +add_executable(runtime runtime.c) +target_compile_options(runtime PRIVATE ${COMPILE_OPTIONS_STD}) +target_include_directories(runtime PUBLIC ${INCLUDE_ALL}) +target_link_libraries(runtime + queue + handle_block + Threads::Threads + init_globals + validate_tx + handle_tx + shell) \ No newline at end of file diff --git a/src/runtime/includes/runtime.h b/src/runtime/includes/runtime.h new file mode 100644 index 0000000..d3a9cfc --- /dev/null +++ b/src/runtime/includes/runtime.h @@ -0,0 +1,17 @@ +#pragma once +#include "queue.h" +#include "pthread.h" +#include "semaphore.h" + +typedef struct Globals { + Queue *queue_block; + Queue *queue_tx; + pthread_mutex_t utxo_pool_lock; + pthread_mutex_t utxo_to_tx_lock; + pthread_mutex_t blockchain_lock; + pthread_mutex_t wallet_pool_lock; + pthread_mutex_t key_pool_lock; + pthread_mutex_t mempool_lock; + int *miner_update; + pthread_mutex_t miner_update_lock; +} Globals; \ No newline at end of file diff --git a/src/runtime/includes/shell.h b/src/runtime/includes/shell.h index 35663cd..c46b60b 100644 --- a/src/runtime/includes/shell.h +++ b/src/runtime/includes/shell.h @@ -16,12 +16,13 @@ #pragma once #include +#include "runtime.h" #define COMMAND_NAME_LEN 128 typedef struct { char *name; - int (*func)(char **); + int (*func)(Globals *, char **); size_t num_args; char *help; } Command; @@ -37,17 +38,17 @@ void str_to_buf( size_t dest_len, size_t src_len ); -int shell_mine(char **args); -int shell_build_tx(char **args); -int shell_print_chain(char **args); -int shell_print_block(char **args); -int shell_exit(char **args); -int shell_help(char **args); +int shell_mine(Globals *globals, char **args); +int shell_build_tx(Globals *globals, char **args); +int shell_print_chain(Globals *globals, char **args); +int shell_print_block(Globals *globals, char **args); +int shell_exit(Globals *globals, char **args); +int shell_help(Globals *globals, char **args); char *shell_read_line(); char **shell_tokenize(char *line); -int shell_execute(size_t num_args, char **args); -void shell_loop(); +int shell_execute(Globals *globals, size_t num_args, char **args); +void shell_loop(Globals *globals); void shell_init_globals(); void shell_init_commands(); diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c new file mode 100644 index 0000000..8d86527 --- /dev/null +++ b/src/runtime/runtime.c @@ -0,0 +1,204 @@ +#include "queue.h" +#include +#include "pthread.h" +#include "validate_block.h" +#include "handle_block.h" +#include "validate_tx.h" +#include "handle_tx.h" +#include "create_block.h" +#include "shell.h" +#include "runtime.h" +#include "init_globals.h" +#include "init_db.h" + +Globals *init_globals(){ + Globals *new_globals = malloc(sizeof(Globals)); + new_globals->queue_block = queue_init(); + new_globals->queue_tx = queue_init(); + int mutex_fail = 0; + if (pthread_mutex_init(&new_globals->utxo_pool_lock, NULL) != 0) { + mutex_fail = 1; + } + if (pthread_mutex_init(&new_globals->utxo_to_tx_lock, NULL) != 0) { + mutex_fail = 1; + } + if (pthread_mutex_init(&new_globals->blockchain_lock, NULL) != 0) { + mutex_fail = 1; + } + if (pthread_mutex_init(&new_globals->wallet_pool_lock, NULL) != 0) { + mutex_fail = 1; + } + if (pthread_mutex_init(&new_globals->key_pool_lock, NULL) != 0) { + mutex_fail = 1; + } + if (pthread_mutex_init(&new_globals->mempool_lock, NULL) != 0) { + mutex_fail = 1; + } + if (pthread_mutex_init(&new_globals->miner_update_lock, NULL) != 0) { + mutex_fail = 1; + } + if (mutex_fail == 1){ + fprintf(stderr,"mutex init has failed\n"); + return NULL; + } + new_globals->miner_update = malloc(sizeof(int)); + *new_globals->miner_update = 1; + + return new_globals; +} + +void *node_block_thread(void *arg){ + Globals *globals = arg; + while(1){ + printf("Node Thread Waiting to pop Block\n"); + Block *popped_block = queue_pop_void(globals->queue_block); + printf("Node Thread Block Popped\n"); + //Now aquire locks for validation (Utxo pool and blockcahin) + printf("Node Thread Waiting on lock for validation\n"); + pthread_mutex_lock(&globals->utxo_pool_lock); + pthread_mutex_lock(&globals->blockchain_lock); + int block_valid = validate_block(popped_block); + + + if(block_valid == 0){ + //Now aquire additional locks for handling + printf("Node Thread Waiting on lock for Handling\n"); + pthread_mutex_lock(&globals->utxo_to_tx_lock); + pthread_mutex_lock(&globals->wallet_pool_lock); + pthread_mutex_lock(&globals->key_pool_lock); + pthread_mutex_lock(&globals->mempool_lock); + accept_block(popped_block); + free_block(popped_block); + pthread_mutex_lock(&globals->miner_update_lock); + *globals->miner_update = 0; + pthread_mutex_unlock(&globals->miner_update_lock); + pthread_mutex_unlock(&globals->utxo_to_tx_lock); + pthread_mutex_unlock(&globals->wallet_pool_lock); + pthread_mutex_unlock(&globals->key_pool_lock); + pthread_mutex_unlock(&globals->mempool_lock); + printf("Node Thread unlockedr Handling\n"); + } + pthread_mutex_unlock(&globals->utxo_pool_lock); + pthread_mutex_unlock(&globals->blockchain_lock); + } + return NULL; +} + +void *node_tx_thread(void *arg){ + Globals *globals = arg; + while(1){ + printf("NodeTX Thread Waiting to pop TX\n"); + Transaction *popped_tx = queue_pop_void(globals->queue_tx); + printf("NodeTX Thread TX Popped\n"); + //Now aquire locks for validation (Utxo pool and blockcahin) + printf("NodeTX Thread Waiting on lock for validation\n"); + + + pthread_mutex_lock(&globals->utxo_pool_lock); + pthread_mutex_lock(&globals->utxo_to_tx_lock); + pthread_mutex_lock(&globals->mempool_lock); + + int tx_valid = validate_tx_incoming(popped_tx); + + + if(tx_valid == 0){ + //Now aquire additional locks for handling + handle_tx(popped_tx); + free_tx(popped_tx); + } + pthread_mutex_unlock(&globals->mempool_lock); + pthread_mutex_unlock(&globals->utxo_to_tx_lock); + pthread_mutex_unlock(&globals->utxo_pool_lock); + printf("NodeTX Thread unlocked from validation\n"); + } + return NULL; +} + +void *miner_thread(void *arg){ + Globals *globals = arg; + unsigned long hash_check_flag = 10000; + int new_block_in_chain = 1; //1 is false 0 is true + while(1){ + printf("Miner Thread waiting on lock to create Block\n"); + //Now aquire locks for creating a block! + pthread_mutex_lock(&globals->utxo_pool_lock); + pthread_mutex_lock(&globals->blockchain_lock); + pthread_mutex_lock(&globals->key_pool_lock) ; + pthread_mutex_lock(&globals->mempool_lock); + printf("Miner Thread got lock to create Block\n"); + Block *new_block = create_block_alloc(); + + pthread_mutex_unlock(&globals->utxo_pool_lock); + pthread_mutex_unlock(&globals->blockchain_lock); + pthread_mutex_unlock(&globals->key_pool_lock) ; + pthread_mutex_unlock(&globals->mempool_lock); + printf("Miner Thread done creating Block\n"); + + while(try_header_hash(&(new_block->header)) != 0){ + change_nonce(new_block); + if(new_block->header.nonce % hash_check_flag == 0){ + pthread_mutex_lock(&globals->miner_update_lock); + if(globals->miner_update == 0){ + new_block_in_chain = 0; + *globals->miner_update = 1; + } + pthread_mutex_unlock(&globals->miner_update_lock); + if(new_block_in_chain == 0){ + break; + } + } + } + if(new_block_in_chain == 0){ + free_block(new_block); + continue; + new_block_in_chain = 1; + } + print_block(new_block,""); + printf("Miner mined a block Block\n"); + queue_add_void(globals->queue_block, new_block); + } + return NULL; +} + +void *shell_thread(void *arg){ + Globals *globals = arg; + shell_init(); + shell_loop(globals); + return NULL; +} + +int main() { + + Globals *globals = init_globals(); + + // Intitialize the globabls! + node_init(PROD_DB_LOC); + + pthread_t node_block, node_tx, shell, miner; + int node_block_ret, node_tx_ret, shell_ret, miner_ret; + /* Create independent threads each of which will execute function */ + + // node_block_ret = pthread_create( &node_block, NULL, pop, (void*) globals->queue_block); + // sleep(1); + // node_tx_ret = pthread_create( &node_tx, NULL, add_and_delay, (void*) globals->queue_block); + + node_block_ret = pthread_create( &node_block, NULL, node_block_thread, (void*) globals); + node_tx_ret = pthread_create( &node_tx, NULL, node_tx_thread, (void*) globals); + shell_ret = pthread_create( &shell, NULL, shell_thread, (void*) globals); + miner_ret = pthread_create( &miner, NULL, miner_thread, (void*) globals); + + /* Wait till threads are complete before main continues. Unless we */ + /* wait we run the risk of executing an exit which will terminate */ + /* the process and all threads before the threads have completed. */ + + pthread_join(shell, NULL); + pthread_join(node_block, NULL); + pthread_join(node_tx, NULL); + pthread_join(miner, NULL); + + printf("Node Block returns: %d\n",node_block_ret); + printf("Node TX returns: %d\n", node_tx_ret); + printf("Shell returns: %d\n", shell_ret); + printf("Miner returns: %d\n", miner_ret); + return 0; +} diff --git a/src/runtime/shell.c b/src/runtime/shell.c index 6ed200f..bef76f8 100644 --- a/src/runtime/shell.c +++ b/src/runtime/shell.c @@ -8,7 +8,7 @@ #include "init_globals.h" #include "mempool.h" #include "blockchain.h" - +#include "runtime.h" #include "wallet.h" #include "create_block.h" #include "handle_block.h" @@ -19,10 +19,10 @@ #define ARG_SIZE 32 Command shell_commands[] = { - { - .name = "mine", .func = &shell_mine, .num_args = 1, - .help = "Usage: mine\nMine a new block" - }, + // { + // .name = "mine", .func = &shell_mine, .num_args = 1, + // .help = "Usage: mine\nMine a new block" + // }, { .name = "create_tx", .func = &shell_build_tx, .num_args = 4, .help = "Usage: create_tx ADDRESS AMT FEE\nCreate a new transaction" @@ -83,27 +83,27 @@ void str_to_buf( } } -int shell_mine(char **args) { - (void)args; - Block *block; - unsigned char header_hash[BLOCK_HASH_LEN]; +// int shell_mine(Globals *globals, char **args) { +// (void)args; +// Block *block; +// unsigned char header_hash[BLOCK_HASH_LEN]; - printf("Mining...\n"); - block = mine_block(); - handle_new_block(block); +// printf("Mining...\n"); +// block = mine_block(); +// handle_new_block(block); - // This might not be needed anymore... - hash_blockheader(header_hash, &block->header); - printf("Block mined!\n\n"); - dump_buf("", "Hash: ", header_hash, BLOCK_HASH_LEN); - pretty_print_block(block, ""); +// // This might not be needed anymore... +// hash_blockheader(header_hash, &block->header); +// printf("Block mined!\n\n"); +// dump_buf("", "Hash: ", header_hash, BLOCK_HASH_LEN); +// pretty_print_block(block, ""); - unsigned char tx_hash[TX_HASH_LEN]; - hash_tx(tx_hash, block->txs[0]); - return 0; -} +// unsigned char tx_hash[TX_HASH_LEN]; +// hash_tx(tx_hash, block->txs[0]); +// return 0; +// } -int shell_build_tx(char **args) { +int shell_build_tx(Globals *globals, char **args) { Transaction *tx; TxOptions *options; Output *output; @@ -121,10 +121,14 @@ int shell_build_tx(char **args) { options->dests = output; options->tx_fee = strtoul(args[3], &end, 10); - tx = build_tx(options); - mempool_add(tx); + pthread_mutex_lock(&globals->wallet_pool_lock); + pthread_mutex_lock(&globals->key_pool_lock); - printf("Transaction created!\n\n"); + tx = build_tx(options); + queue_add_void(globals->queue_tx, tx); + pthread_mutex_unlock(&globals->wallet_pool_lock); + pthread_mutex_unlock(&globals->key_pool_lock); + printf("Transaction added to buffer!\n\n"); pretty_print_tx(tx, ""); free(output); @@ -133,13 +137,15 @@ int shell_build_tx(char **args) { return 0; } -int shell_print_chain(char **args) { +int shell_print_chain(Globals *globals, char **args) { (void)args; + pthread_mutex_lock(&globals->blockchain_lock); pretty_print_blockchain_hashmap(); + pthread_mutex_unlock(&globals->blockchain_lock); return 0; } -int shell_print_block(char **args) { +int shell_print_block(Globals *globals, char **args) { unsigned char buf[BLOCK_HASH_LEN]; Block *block; @@ -147,7 +153,9 @@ int shell_print_block(char **args) { buf, args[1], BLOCK_HASH_LEN, strlen(args[1]) ); + pthread_mutex_lock(&globals->blockchain_lock); int ret_find = blockchain_find_leveldb(&block, buf); + pthread_mutex_unlock(&globals->blockchain_lock); if (!block || ret_find != 0) { printf("%s: block not found\n", args[0]); return 0; @@ -159,12 +167,18 @@ int shell_print_block(char **args) { return 0; } -int shell_exit(char **args) { +int shell_exit(Globals *globals, char **args) { + pthread_mutex_lock(&globals->utxo_pool_lock); + pthread_mutex_lock(&globals->blockchain_lock); + pthread_mutex_lock(&globals->utxo_to_tx_lock); + pthread_mutex_lock(&globals->wallet_pool_lock); + pthread_mutex_lock(&globals->key_pool_lock); + pthread_mutex_lock(&globals->mempool_lock); (void)args; return 1; } -int shell_help(char **args) { +int shell_help(Globals *globals, char **args) { int len; (void)args; @@ -227,7 +241,7 @@ char **shell_tokenize(char *line) { return args; } -int shell_execute(size_t num_args, char **args) { +int shell_execute(Globals *globals, size_t num_args, char **args) { CommandPool *entry; Command *cmd; int len, i; @@ -254,10 +268,10 @@ int shell_execute(size_t num_args, char **args) { return 0; } - return cmd->func(args); + return cmd->func(globals, args); } -void shell_loop() { +void shell_loop(Globals *globals) { char *line; char **args; int status; @@ -272,7 +286,7 @@ void shell_loop() { continue; args = shell_tokenize(line); num_args = arg_len(args); - status = shell_execute(num_args, args); + status = shell_execute(globals, num_args, args); free(line); free(args); @@ -303,12 +317,12 @@ void shell_init_commands() { } void shell_init() { - shell_init_globals(); + //shell_init_globals(); shell_init_commands(); } -int main() { - shell_init(); - shell_loop(); - return 1; -} +// int main() { +// // shell_init(); +// // shell_loop(); +// return 1; +// } diff --git a/src/tests/core/utils/CMakeLists.txt b/src/tests/core/utils/CMakeLists.txt index b21d85e..c4cf7d6 100644 --- a/src/tests/core/utils/CMakeLists.txt +++ b/src/tests/core/utils/CMakeLists.txt @@ -12,3 +12,10 @@ target_link_libraries(test_ser_key add_test(test_crypto ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_crypto) add_test(test_ser_key ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_ser_key) + +add_executable(test_queue test_queue.c) +target_compile_options(test_queue PRIVATE ${COMPILE_OPTIONS_STD}) +target_include_directories(test_queue PUBLIC ${INCLUDE_ALL}) +target_link_libraries(test_queue + queue + Threads::Threads) \ No newline at end of file diff --git a/src/tests/core/utils/test_queue.c b/src/tests/core/utils/test_queue.c new file mode 100644 index 0000000..d1d56c3 --- /dev/null +++ b/src/tests/core/utils/test_queue.c @@ -0,0 +1,16 @@ +#include "queue.h" + +int main() { + + Queue *test_q = queue_init(); + + int *test1 = malloc(sizeof(int)); + *test1 = 4; + queue_add_void(test_q, test1); + int *pop_int = queue_pop_void(test_q); + printf("Popped val: %i\n", *pop_int); + + + + return 0; +}