diff --git a/CMakeLists.txt b/CMakeLists.txt index ec2db312c5a007120fefa8ed4b235c90037289d7..630925c807c9cf73aae30d3f80b7c5c6d34d7a99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ set(DAP_CHAIN_HEADERS dap_chain_ops.h dap_chain.h dap_chain_mine.h + dap_chain_mine_task.h dap_chain_block.h dap_chain_block_cache.h dap_chain_internal.h diff --git a/dap_chain.c b/dap_chain.c index 377c127d9d22f57798df24a93baf5101c5274131..d27eefa61e10bbb5dad123deb9d06d7fa1e4b205 100644 --- a/dap_chain.c +++ b/dap_chain.c @@ -57,7 +57,7 @@ dap_chain_t * dap_chain_open(const char * a_file_storage,const char * a_file_cac { dap_chain_t * l_chain = DAP_NEW_Z(dap_chain_t); - l_chain->difficulty = 4; + l_chain->difficulty = 2; DAP_CHAIN_INTERNAL_LOCAL_NEW(l_chain); diff --git a/dap_chain_block.c b/dap_chain_block.c index 8d7e38d5a4bffa8dbc69e75834a6eebb845f7182..87d6562de41c06a7c0a6b6ac96caab968919f7cf 100644 --- a/dap_chain_block.c +++ b/dap_chain_block.c @@ -44,6 +44,7 @@ dap_chain_block_t * dap_chain_block_new(dap_chain_hash_t * a_prev_block ) }else{ l_block->header.signature = DAP_CHAIN_BLOCK_SIGNATURE; l_block->header.version = 1; + l_block->header.timestamp = time(NULL); if( a_prev_block ){ memcpy(&l_block->header.prev_block,a_prev_block,sizeof(l_block->header.prev_block)); }else{ diff --git a/dap_chain_block.h b/dap_chain_block.h index b639db986016b903acd20ab58339e0149dd99a13..9676d15ba283b06f402674dc01c6f18b2872e6d8 100644 --- a/dap_chain_block.h +++ b/dap_chain_block.h @@ -67,3 +67,4 @@ static inline void dap_chain_block_hash_calc(dap_chain_block_t * a_block, dap_ch dap_hash(a_block,a_block->header.size,a_hash->data, sizeof(a_hash->data),DAP_HASH_TYPE_SLOW_0); } + diff --git a/dap_chain_block_cache.c b/dap_chain_block_cache.c index f3857d5d11b1660c5ea1721ef929409a200bf77e..0ffb6a62aa146ed2c1dee8f3736ee234ec852166 100644 --- a/dap_chain_block_cache.c +++ b/dap_chain_block_cache.c @@ -36,9 +36,9 @@ void dap_chain_block_cache_dump(dap_chain_block_cache_t * a_block_cache) if ( a_block_cache ) { dap_chain_block_t * l_block = a_block_cache->block; if( l_block ){ - char * l_hash_str = dap_chain_hash_to_str(&a_block_cache->block_hash); - char * l_prev_hash_str = dap_chain_hash_to_str(&l_block->header.prev_block); - char * l_root_sections_str = dap_chain_hash_to_str(&l_block->header.root_sections); + char * l_hash_str = dap_chain_hash_to_str_new(&a_block_cache->block_hash); + char * l_prev_hash_str = dap_chain_hash_to_str_new(&l_block->header.prev_block); + char * l_root_sections_str = dap_chain_hash_to_str_new(&l_block->header.root_sections); log_it(L_INFO, " ** block_hash %s",l_hash_str); log_it(L_INFO, " ** version %d",l_block->header.version); log_it(L_INFO, " ** timestamp %s", ctime( (time_t*) &l_block->header.timestamp) ); diff --git a/dap_chain_block_cache.h b/dap_chain_block_cache.h index f258da4634c3994524033ff7082bf940f2839493..1947bab6ec5c28814ec12aae988db78345c6d39e 100644 --- a/dap_chain_block_cache.h +++ b/dap_chain_block_cache.h @@ -29,7 +29,7 @@ typedef struct dap_chain_block_cache{ dap_chain_hash_t block_hash; uint32_t sections_size; - uint64_t block_mine_time; + double block_mine_time; dap_chain_block_t * block; } dap_chain_block_cache_t; diff --git a/dap_chain_common.c b/dap_chain_common.c index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..28f6c9fb0b751aabe544501a2f1b0e0d84f6213d 100644 --- a/dap_chain_common.c +++ b/dap_chain_common.c @@ -0,0 +1,26 @@ +#include <string.h> +#include "dap_common.h" +#include "dap_chain_common.h" + +#define LOG_TAG "dap_chain_common" + +/** + * @brief dap_chain_hash_to_str + * @param a_hash + * @param a_str + * @param a_str_max + * @return + */ +size_t dap_chain_hash_to_str(dap_chain_hash_t * a_hash, char * a_str, size_t a_str_max) +{ + const size_t c_hash_str_size = sizeof(*a_hash)*2 +1 /*trailing zero*/ +2 /* heading 0x */ ; + if (a_str_max < c_hash_str_size ){ + log_it(L_ERROR,"String for hash too small, need %u but have only %u",c_hash_str_size,a_str_max); + } + size_t i; + snprintf(a_str,3,"0x"); + for (i = 0; i< sizeof(a_hash->data); ++i) + snprintf(a_str+i*2+2,3,"%02x",a_hash->data[i]); + a_str[c_hash_str_size]='\0'; + return strlen(a_str); +} diff --git a/dap_chain_common.h b/dap_chain_common.h index 0e3df1b81972eb8bd34e71147aae2fe0b511c3f8..6cc1da25c34c10903d1e9f488987e8144ff4819b 100644 --- a/dap_chain_common.h +++ b/dap_chain_common.h @@ -29,7 +29,7 @@ #include "dap_common.h" #include "dap_math_ops.h" -#define DAP_CHAIN_HASH_SIZE 64 +#define DAP_CHAIN_HASH_SIZE 32 #define DAP_CHAIN_ADDR_HASH_SIZE 32 typedef union dap_chain_hash{ @@ -56,19 +56,18 @@ typedef struct dap_chain_addr{ uint64_t checksum; } dap_chain_addr_t; +size_t dap_chain_hash_to_str(dap_chain_hash_t * a_hash, char * a_str, size_t a_str_max); + /** * @brief dap_chain_hash_to_str * @param a_hash * @return */ -static inline char * dap_chain_hash_to_str(dap_chain_hash_t * a_hash) +static inline char * dap_chain_hash_to_str_new(dap_chain_hash_t * a_hash) { const size_t c_hash_str_size = sizeof(*a_hash)*2 +1 /*trailing zero*/ +2 /* heading 0x */ ; char * ret = DAP_NEW_Z_SIZE(char, c_hash_str_size); - size_t i; - snprintf(ret,2,"0x"); - for (i = 0; i< sizeof(a_hash->data); ++i) - snprintf(ret+i+2,2,"%02x",a_hash->data[i]); + dap_chain_hash_to_str(a_hash,ret,c_hash_str_size); return ret; } diff --git a/dap_chain_mine.c b/dap_chain_mine.c index a5ab4339cb7e571191bce236e6d3f5bd1b675c57..9c992241b18cccd020564a36eb3604234a994dc6 100644 --- a/dap_chain_mine.c +++ b/dap_chain_mine.c @@ -1,44 +1,292 @@ -#include <time.h> +/* + * Authors: + * Dmitriy A. Gearasimov <kahovski@gmail.com> + * DeM Labs Inc. https://demlabs.net + * DeM Labs Open source community https://github.com/demlabsinc + * Copyright (c) 2017-2018 + * All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#else +#define _GNU_SOURCE +#endif + +#include <sys/time.h> +#include <unistd.h> +#include <sched.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#include <inttypes.h> +#include <errno.h> + #include "dap_common.h" #include "dap_chain_block.h" +#include <pthread.h> #include "dap_chain_mine.h" +#include "dap_chain_mine_task.h" #define LOG_TAG "dap_chain_mine" +int get_cpu_count() +{ + long nprocs = -1; + long nprocs_max = -1; +#ifdef _WIN32 + #ifndef _SC_NPROCESSORS_ONLN + SYSTEM_INFO info; + GetSystemInfo(&info); + #define sysconf(a) info.dwNumberOfProcessors + #define _SC_NPROCESSORS_ONLN + #endif +#endif +#ifdef _SC_NPROCESSORS_ONLN + nprocs = sysconf(_SC_NPROCESSORS_ONLN); + if (nprocs < 1) { + log_it(L_ERROR, "Could not determine number of CPUs online: %s ", strerror (errno)); + return -1; + } + nprocs_max = sysconf(_SC_NPROCESSORS_CONF); + if (nprocs_max < 1){ + log_it(L_ERROR, "Could not determine number of CPUs configured: %s",strerror (errno)); + return -2; + } + log_it(L_INFO, "%ld of %ld processors online",nprocs, nprocs_max); + return nprocs; + #else + log_it(L_ERROR, "Could not determine number of CPUs"); + return -3; + #endif +} + /** - * @brief dap_chain_mine_block - * @param a_block_cache + * @brief s_mine_thread + * @param a_arg * @return */ -int dap_chain_mine_block(dap_chain_block_cache_t * a_block_cache, bool a_mine_gold_only) +static void * s_mine_thread(void * a_arg) { + dap_chain_mine_task_t * l_task = (dap_chain_mine_task_t *) a_arg; dap_chain_hash_t l_hash; - dap_chain_block_t * l_block = a_block_cache->block; - dap_chain_hash_kind_t l_hash_kind; - uint64_t l_difficulty = l_block->header.difficulty; - time_t l_tm_start = time(NULL); - uint64_t l_hash_count = 0; - do{ - l_block->header.nonce++; - log_it(L_DEBUG,"nonce %llu",l_block->header.nonce); - dap_chain_block_hash_calc(l_block,&l_hash); + dap_chain_hash_kind_t l_hash_kind = HASH_USELESS; + uint64_t l_difficulty = l_task->block->header.difficulty; + uint_fast64_t l_nonce; + uint_fast64_t l_hash_count = 0; + log_it(L_INFO, "Th#%u: started",l_task->id); + + // Set CPU affininty and nice level +#ifndef NO_POSIX_SHED + uint32_t l_cpu_count = get_cpu_count(); + cpu_set_t mask; + CPU_ZERO(&mask); + CPU_SET( l_task->id % l_cpu_count , &mask); + + if ( pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &mask) != 0 ){ + log_it(L_CRITICAL, "Error pthread_setaffinity_np() You really have %d or more core in CPU?", l_task->id % l_cpu_count ); + abort(); + }else + log_it(L_DEBUG, "set affinity to CPU %u", l_task->id % l_cpu_count ); + +#else +#warning "No SHED affinity, mining could be uneffective" +#endif + // Set thread priority + struct sched_param l_prio_param; + int l_prio_policy=SCHED_RR; + l_prio_param.__sched_priority= 99; + pthread_t self_id= pthread_self(); + if( pthread_setschedparam(self_id,l_prio_policy,&l_prio_param)== 0 ){ + log_it(L_DEBUG, "Set priority Round-Robin 99 lvl"); + }else + log_it(L_WARNING, "Can't set priority to Round-Robin 99 lvl"); + + for( l_nonce = l_task->nonce_from ; l_nonce < l_task->nonce_to; ++l_nonce ){ + //log_it(L_DEBUG, "Th#%u: nonce = %llu hash_count = %llu", l_task->id, l_task->block->header.nonce, + // l_hash_count); + l_task->block->header.nonce = l_nonce; + dap_chain_block_hash_calc(l_task->block,&l_hash); l_hash_count++; + + char l_hash_str[140]; + dap_chain_hash_to_str(&l_hash,l_hash_str,sizeof (l_hash_str) ); + //log_it(L_DEBUG, "Th#%u: block hash %s ",l_task->id, l_hash_str); + + // Update task structure every 10 hashes to prevent often context switch + if (l_hash_count % 10 == 0){ // TODO Make automatic growing value, depending from hash rate + atomic_uint_fast64_t l_hash_count_atomic = ATOMIC_VAR_INIT(l_hash_count); + atomic_exchange(&l_task->hash_count, l_hash_count_atomic); + + if( atomic_load(& l_task->tasks->is_mined) ){ + log_it(L_INFO, "Th#%u: Stop the process", l_task->id); + break; + } + } + l_hash_kind = dap_chain_hash_kind_check(&l_hash,l_difficulty ); - if(l_block->header.nonce = 0x0fffffffffffffff ) - break; - if (a_mine_gold_only){ - if ( l_hash_kind != HASH_GOLD ){ - continue; + if (l_task->gold_only){ + if ( l_hash_kind == HASH_GOLD ){ + char l_hash_str[140]; + dap_chain_hash_to_str(&l_hash,l_hash_str,sizeof (l_hash_str) ); + log_it(L_INFO, "Th#%u: !!! Mined GOLD token !!! block hash %s ",l_task->id, l_hash_str); + break; } + }else if ( l_hash_kind != HASH_USELESS ){ + char l_hash_str[140]; + dap_chain_hash_to_str(&l_hash,l_hash_str,sizeof (l_hash_str) ); + log_it(L_INFO, "Th#%u: !!! Mined SILVER token !!! block hash %s", l_task->id, l_hash_str); + break; } - }while ( l_hash_kind == HASH_USELESS ); - time_t l_tm_end = time(NULL); - if ( l_hash_kind == HASH_GOLD ){ - log_it(L_INFO, " !!! Mined GOLD token !!!"); - }else if ( l_hash_kind == HASH_SILVER ) { - log_it(L_INFO, " !!! Mined SILVER token !!!"); - } - log_it(L_DEBUG, "Mining time: %lu seconds, %llu hashes, %llu H/s ", l_tm_end - l_tm_start,l_hash_count, - l_hash_count / (l_tm_end - l_tm_start)); - return l_hash_kind != HASH_USELESS; + } + + if ( l_hash_kind != HASH_USELESS ){ + log_it(L_INFO, "Th#%u: !!! Mined nonce = %" PRIuFAST64 " on try %" PRIuFAST64,l_nonce, l_hash_count ); + atomic_bool l_is_mined_atomic = ATOMIC_VAR_INIT(true); + atomic_uint_fast64_t l_mined_nonce_atomic = ATOMIC_VAR_INIT(l_nonce); + + memcpy(&l_task->tasks->mined_hash,&l_hash, sizeof(l_task->tasks->mined_hash) ); + + atomic_exchange(&l_task->tasks->is_mined, l_is_mined_atomic ); + atomic_exchange(&l_task->tasks->mined_nonce ,l_mined_nonce_atomic); + }else + log_it(L_DEBUG, "Th#%u: Mined nothing"); + + DAP_DELETE(l_task->block); + return NULL; } + +static void * s_stats_thread(void * a_arg) +{ + dap_chain_mine_tasks_t * l_tasks = (dap_chain_mine_tasks_t *) a_arg; + + struct dap_chain_mine_task_result * l_result = DAP_NEW_Z(struct dap_chain_mine_task_result); + struct timespec l_tm_start; + struct timespec l_tm_end ; + clock_gettime(CLOCK_MONOTONIC_RAW,&l_tm_start); + uint64_t l_tm_diff; + double l_tm_diff_secs; + + while(true){ + uint_fast64_t l_hash_count_total = 0; + uint64_t i; + log_it(L_DEBUG, "Statistic:"); + + for( i = 0; i<l_tasks->tasks_count ; ++i){ + //log_it(L_DEBUG, "Thread #%u: hash_count = %llu", i, atomic_load(&l_tasks->task[i].hash_count)); + l_hash_count_total += atomic_load(& l_tasks->task[i].hash_count ); + } + + clock_gettime(CLOCK_MONOTONIC_RAW,&l_tm_end); + l_tm_diff = (l_tm_end.tv_sec - l_tm_start.tv_sec) * 1000000 + (l_tm_end.tv_nsec - l_tm_start.tv_nsec)/ 1000; + l_tm_diff_secs = ( (double) l_tm_diff)/ 1000000.0; + + log_it(L_INFO, "Mining time: %04.03lf seconds, %llu hashes, %.03lf H/s ", l_tm_diff_secs + , l_hash_count_total , + ( (double) l_hash_count_total) / ((double) l_tm_diff_secs ) ); + if(atomic_load(&l_tasks->is_mined) ){ + l_result->success = true; + l_result->nonce = atomic_load(&l_tasks->mined_nonce); + memcpy(&l_result->mined_hash, &l_tasks->mined_hash, sizeof(l_tasks->mined_hash) ); + l_result->mined_time = l_tm_diff; + l_result->hashrate_middle = ( (double) l_hash_count_total) / ((double) l_tm_diff_secs ) ; + break; + } + sleep(2); + } + return l_result; +} + + +/** + * @brief dap_chain_mine_block + * @param a_block_cache + * @return + */ +int dap_chain_mine_block(dap_chain_block_cache_t * a_block_cache, bool a_mine_gold_only, uint32_t a_threads) +{ + pthread_t stats_pid; + struct dap_chain_mine_task_result * l_result = NULL; + dap_chain_mine_tasks_t * l_tasks = DAP_NEW_Z ( dap_chain_mine_tasks_t); + + uint32_t i; + + if( a_threads == 0 ){ + int rval= get_cpu_count(); + if(rval<0 ) + return -4; + else + a_threads = rval; + } + + l_tasks->task = DAP_NEW_Z_SIZE( struct dap_chain_mine_task, (sizeof(struct dap_chain_mine_task)*a_threads+16) ); + l_tasks->tasks_count = a_threads; + l_tasks->is_mined = ATOMIC_VAR_INIT(false); + l_tasks->mined_nonce = ATOMIC_VAR_INIT(0); + l_tasks->block_cache = a_block_cache; + uint64_t l_nonce_task_length = UINT64_MAX / a_threads; + for(i = 0; i< a_threads; i++){ // Creates mining threads + dap_chain_mine_task_t *l_task = &l_tasks->task[i]; + l_task->tasks = l_tasks; + l_task->id = i; + l_task->hash_count = ATOMIC_VAR_INIT(0); + // Each thread has its own copy of the block for mining + l_task->block = DAP_NEW_Z_SIZE(dap_chain_block_t,a_block_cache->block->header.size); + memcpy(l_task->block, a_block_cache->block,a_block_cache->block->header.size ); + // spread nonce between threads + l_task->nonce_from = i *l_nonce_task_length; + l_task->nonce_to = (i==a_threads-1)? UINT64_MAX: (i+1)*l_nonce_task_length; + pthread_create(& l_task->task_pid ,NULL,s_mine_thread, l_task); + } + // Create statistic collector thread + pthread_create(&stats_pid,NULL,s_stats_thread, l_tasks); + + // Join to it, waiting for results + pthread_join(stats_pid,(void**) &l_result); + log_it(L_DEBUG,"Finishing mining threads, free memory..."); + for(i = 0; i< a_threads; i++){ // Creates mining threads + pthread_join(l_tasks->task[i].task_pid,NULL); + } + + DAP_DELETE(l_tasks->task); + //} + DAP_DELETE (l_tasks); + if(l_result){ + if(l_result->success){ + log_it(L_INFO,"Mined nonce = 0x%016x Hashrate %.03lf H/s",l_result->nonce, l_result->hashrate_middle); + a_block_cache->block->header.nonce = l_result->nonce; + a_block_cache->block_mine_time = l_result->mined_time; + memcpy(&a_block_cache->block_hash, &l_result->mined_hash,sizeof(l_result->mined_hash)); + DAP_DELETE(l_result); + return 0; + }else{ + log_it(L_INFO,"Minded nothing"); + DAP_DELETE(l_result); + return 1; + } + }else{ + log_it(L_ERROR,"No result! Its NULL!"); + return -2; + } + + +} + + diff --git a/dap_chain_mine.h b/dap_chain_mine.h index cb5edfcc707155363f315c34590a92e4b1243754..71d1d590b4a4454fd0e0266d2124f228ff8a9d3a 100644 --- a/dap_chain_mine.h +++ b/dap_chain_mine.h @@ -27,4 +27,4 @@ #include "dap_chain_block_cache.h" -int dap_chain_mine_block(dap_chain_block_cache_t * a_block_cache, bool a_mine_gold_only); +int dap_chain_mine_block(dap_chain_block_cache_t * a_block_cache, bool a_mine_gold_only, uint32_t a_threads); diff --git a/dap_chain_mine_task.h b/dap_chain_mine_task.h new file mode 100644 index 0000000000000000000000000000000000000000..2fbe03fabad40216783e457d81123a9e3db272e7 --- /dev/null +++ b/dap_chain_mine_task.h @@ -0,0 +1,62 @@ +/* + * Authors: + * Dmitriy A. Gearasimov <kahovski@gmail.com> + * DeM Labs Inc. https://demlabs.net + * DeM Labs Open source community https://github.com/demlabsinc + * Copyright (c) 2017-2018 + * All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once +#include <stdint.h> +#include <stddef.h> +#include <stdatomic.h> +#include "dap_chain_common.h" +#include "dap_chain_block.h" +#include "dap_chain_block_cache.h" + +typedef struct dap_chain_mine_task +{ + uint32_t id; + pthread_t task_pid; + uint64_t nonce_from; + uint64_t nonce_to; + atomic_uint_fast64_t hash_count; + bool gold_only; + dap_chain_block_t * block; + struct dap_chain_mine_tasks * tasks; +} dap_chain_mine_task_t; + +typedef struct dap_chain_mine_tasks{ + atomic_bool is_mined; + uint32_t tasks_count; + atomic_uint_fast64_t mined_nonce; + struct dap_chain_mine_task * task; + dap_chain_hash_t mined_hash; + double hashrate_prev[10]; + double hashrate_avg; + dap_chain_block_cache_t * block_cache; +} dap_chain_mine_tasks_t; + +struct dap_chain_mine_task_result +{ + bool success; + uint64_t nonce; + double mined_time; + double hashrate_middle; + dap_chain_hash_t mined_hash; +};