diff --git a/CMakeLists.txt b/CMakeLists.txt index ecaf5efeb98c0a8350b6ef22458a607f63885b8d..bc89b6933001a05f67334cf4e1b16e3784a9b55d 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,9 +4,27 @@ project (dap_chain_global_db) file(GLOB DAP_CHAIN_GLOBAL_DB_SRC *.c) file(GLOB DAP_CHAIN_GLOBAL_DB_HDR *.h) +if(WIN32) + include_directories(../libdap/src/win32/) + include_directories(../3rdparty/libmemcached/) + include_directories(../3rdparty/libmemcached/win32/) + include_directories(../3rdparty/wepoll/include/) + include_directories(../3rdparty/uthash/src/) + include_directories(../3rdparty/libjson-c/) + include_directories(../3rdparty/libmagic/src/) + include_directories(../3rdparty/curl/include/) + include_directories(../3rdparty/libsqlite3/) +endif() + add_library(${PROJECT_NAME} STATIC ${DAP_CHAIN_GLOBAL_DB_SRC} ${DAP_CHAIN_GLOBAL_DB_HDR}) -target_link_libraries(dap_chain_global_db dap_core dap_crypto dap_chain dap_chain_crypto ldb talloc tevent sqlite3 ${CMAKE_CURRENT_SOURCE_DIR}/libcuttdb.a) +if(WIN32) + target_link_libraries(dap_chain_global_db dap_core dap_crypto dap_chain dap_chain_crypto) +endif() +if(UNIX) + target_link_libraries(dap_chain_global_db dap_core dap_crypto dap_chain dap_chain_crypto ldb talloc tevent sqlite3 ${CMAKE_CURRENT_SOURCE_DIR}/libcuttdb.a) +endif() + target_include_directories(dap_chain_global_db INTERFACE .) set(${PROJECT_NAME}_DEFINITIONS CACHE INTERNAL "${PROJECT_NAME}: Definitions" FORCE) diff --git a/dap_chain_global_db.c b/dap_chain_global_db.c index 412c07bc138936fe1ca6a5eeed9a15dde27f839a..f28b49dc147ae6b8d821a5ef5c52e5248cf002d0 100755 --- a/dap_chain_global_db.c +++ b/dap_chain_global_db.c @@ -77,7 +77,10 @@ char * extract_group_prefix(const char * a_group) { char * l_group_prefix = NULL, *l_delimeter; size_t l_group_prefix_size; - l_delimeter = index(a_group, '.'); + +// l_delimeter = index(a_group, '.'); + l_delimeter = strchr(a_group, '.'); + if(l_delimeter == NULL) { l_group_prefix = dap_strdup(a_group); l_group_prefix_size = dap_strlen(l_group_prefix) + 1; diff --git a/dap_chain_global_db.h b/dap_chain_global_db.h index 23ff07d57334ff888b0f8e654ab70c921f81cb29..0bc7ae0e7705e777578e6e27cb454427ae561848 100755 --- a/dap_chain_global_db.h +++ b/dap_chain_global_db.h @@ -7,6 +7,7 @@ #include "dap_common.h" #include "dap_config.h" #include "dap_list.h" +#include "dap_chain_common.h" #include "dap_chain_global_db_driver.h" @@ -100,6 +101,12 @@ char* dap_chain_global_db_hash_fast(const uint8_t *data, size_t data_size); // Get data according the history log uint8_t* dap_db_log_pack(dap_global_db_obj_t *a_obj, size_t *a_data_size_out); + +// Get data according the history log +//char* dap_db_history_tx(dap_chain_hash_fast_t * a_tx_hash, const char *a_group_mempool); +//char* dap_db_history_addr(dap_chain_addr_t * a_addr, const char *a_group_mempool); +//char* dap_db_history_filter(dap_chain_addr_t * a_addr, const char *a_group_mempool); + // Parse data from dap_db_log_pack() void* dap_db_log_unpack(const void *a_data, size_t a_data_size, size_t *a_store_obj_count); // Get timestamp from dap_db_log_pack() diff --git a/dap_chain_global_db_driver_cdb.c b/dap_chain_global_db_driver_cdb.c index 67742a34236b52392e5d7065214e52f2df3d0b87..85ac361b8e06f0f5999b116cf8eba0b74fa40d92 100644 --- a/dap_chain_global_db_driver_cdb.c +++ b/dap_chain_global_db_driver_cdb.c @@ -187,7 +187,13 @@ int dap_db_driver_cdb_init(const char *a_cdb_path, dap_db_driver_callbacks_t *a_ if(s_cdb_path[strlen(s_cdb_path)] == '/') { s_cdb_path[strlen(s_cdb_path)] = '\0'; } + +#ifdef _WIN32 + mkdir(s_cdb_path); +#else mkdir(s_cdb_path, 0755); +#endif + struct dirent *d; DIR *dir = opendir(s_cdb_path); if (!dir) { @@ -229,7 +235,13 @@ int dap_cdb_add_group(const char *a_group) { strcat(l_cdb_path, s_cdb_path); strcat(l_cdb_path, "/"); strcat(l_cdb_path, a_group); + +#ifdef _WIN32 + mkdir(l_cdb_path); +#else mkdir(l_cdb_path, 0755); +#endif + return 0; } @@ -312,7 +324,7 @@ dap_store_obj_t *dap_db_driver_cdb_read_store_obj(const char *a_group, const cha if(a_count_out) { *a_count_out = l_count_out; } - for (ulong i = 0; i < l_count_out; ++i) { + for (uint64_t i = 0; i < l_count_out; ++i) { l_arg.o[i].group = dap_strdup(a_group); } l_obj = l_arg.o; @@ -360,7 +372,7 @@ dap_store_obj_t* dap_db_driver_cdb_read_cond_store_obj(const char *a_group, uint if(a_count_out) { *a_count_out = l_count_out; } - for (ulong i = 0; i < l_count_out; ++i) { + for (uint64_t i = 0; i < l_count_out; ++i) { l_arg.o[i].group = dap_strdup(a_group); } return l_arg.o; diff --git a/dap_chain_global_db_hist.c b/dap_chain_global_db_hist.c index 6f642922dbe3420cb758686f7bf2c80a64507005..b1ca5b4a9dfa643d265bd1ad30a71c065bf08fca 100755 --- a/dap_chain_global_db_hist.c +++ b/dap_chain_global_db_hist.c @@ -5,9 +5,26 @@ #include <dap_common.h> #include <dap_strfuncs.h> +#include <dap_list.h> +#include <dap_string.h> +#include <dap_hash.h> +#include "dap_chain_datum_tx_items.h" + #include "dap_chain_global_db.h" #include "dap_chain_global_db_hist.h" +#include "uthash.h" +// for dap_db_history_filter() +typedef struct dap_tx_data{ + dap_chain_hash_fast_t tx_hash; + char tx_hash_str[70]; + char token_ticker[10]; + size_t obj_num; + size_t pos_num; + dap_chain_addr_t addr; + UT_hash_handle hh; +} dap_tx_data_t; + #define LOG_TAG "dap_chain_global_db_hist" static char* dap_db_history_pack_hist(dap_global_db_hist_t *a_rec) @@ -111,6 +128,938 @@ uint8_t* dap_db_log_pack(dap_global_db_obj_t *a_obj, size_t *a_data_size_out) } + +// for dap_db_history_filter() +static dap_store_obj_t* get_prev_tx(dap_global_db_obj_t *a_objs, dap_tx_data_t *a_tx_data) +{ + if(!a_objs || !a_tx_data) + return NULL; + dap_global_db_obj_t *l_obj_cur = a_objs + a_tx_data->obj_num; + dap_global_db_hist_t l_rec; + if(dap_db_history_unpack_hist((char*) l_obj_cur->value, &l_rec) == -1) + return NULL; + char **l_keys = dap_strsplit(l_rec.keys, GLOBAL_DB_HIST_KEY_SEPARATOR, -1); + size_t l_count = dap_str_countv(l_keys); + if(a_tx_data->pos_num >= l_count) { + dap_strfreev(l_keys); + return NULL; + } + dap_store_obj_t *l_obj = + (dap_store_obj_t*) l_keys ? dap_chain_global_db_obj_get(l_keys[a_tx_data->pos_num], l_rec.group) : NULL; + dap_strfreev(l_keys); + return l_obj; +} + +/** + * Get data according the history log + * + * return history string + */ +#if 0 +char* dap_db_history_tx(dap_chain_hash_fast_t* a_tx_hash, const char *a_group_mempool) +{ + dap_string_t *l_str_out = dap_string_new(NULL); + // load history + size_t l_data_size_out = 0; + dap_global_db_obj_t *l_objs = dap_chain_global_db_gr_load(GROUP_LOCAL_HISTORY, &l_data_size_out); + size_t i, j; + bool l_tx_hash_found = false; + dap_tx_data_t *l_tx_data_hash = NULL; + for(i = 0; i < l_data_size_out; i++) { + dap_global_db_obj_t *l_obj_cur = l_objs + i; + + // parse global_db records in a history record + dap_global_db_hist_t l_rec; + if(dap_db_history_unpack_hist((char*) l_obj_cur->value, &l_rec) == -1) + continue; + // use only groups with datums + if(dap_strcmp(a_group_mempool, l_rec.group)) + continue; + + char **l_keys = dap_strsplit(l_rec.keys, GLOBAL_DB_HIST_KEY_SEPARATOR, -1); + size_t l_count = dap_str_countv(l_keys); + dap_store_obj_t *l_obj = NULL; + // all objs in one history records + for(j = 0; j < l_count; j++) { + + if(l_rec.type != 'a') + continue; + l_obj = (dap_store_obj_t*) dap_chain_global_db_obj_get(l_keys[j], l_rec.group); + if(!l_obj) + continue; + // datum + dap_chain_datum_t *l_datum = (dap_chain_datum_t*) l_obj->value; + if(!l_datum && l_datum->header.type_id != DAP_CHAIN_DATUM_TX) + continue; + + dap_tx_data_t *l_tx_data = NULL; + + // transaction + dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*) l_datum->data; + + // find Token items - present in emit transaction + dap_list_t *l_list_tx_token = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_TOKEN, NULL); + + // find OUT items + dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT, NULL); + dap_list_t *l_list_tmp = l_list_out_items; + while(l_list_tmp) { + const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_list_tmp->data; + // save OUT item l_tx_out + if(!l_tx_data) + { + // save tx hash + l_tx_data = DAP_NEW_Z(dap_tx_data_t); + dap_chain_hash_fast_t l_tx_hash; + dap_hash_fast(l_tx, dap_chain_datum_tx_get_size(l_tx), &l_tx_hash); + memcpy(&l_tx_data->tx_hash, &l_tx_hash, sizeof(dap_chain_hash_fast_t)); + memcpy(&l_tx_data->addr, &l_tx_out->addr, sizeof(dap_chain_addr_t)); + dap_chain_hash_fast_to_str(&l_tx_data->tx_hash, l_tx_data->tx_hash_str, + sizeof(l_tx_data->tx_hash_str)); + l_tx_data->obj_num = i; + l_tx_data->pos_num = j; + // save token name + if(l_list_tx_token) { + dap_chain_tx_token_t *tk = l_list_tx_token->data; + int d = sizeof(l_tx_data->token_ticker); + memcpy(l_tx_data->token_ticker, tk->header.ticker, sizeof(l_tx_data->token_ticker)); + } + // take token from prev out item + else { + + // find IN items + dap_list_t *l_list_in_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN, NULL); + dap_list_t *l_list_tmp_in = l_list_in_items; + // find token_ticker in prev OUT items + while(l_list_tmp_in) { + const dap_chain_tx_in_t *l_tx_in = + (const dap_chain_tx_in_t*) l_list_tmp_in->data; + dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash; + + //find prev OUT item + dap_tx_data_t *l_tx_data_prev = NULL; + HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), + l_tx_data_prev); + if(l_tx_data_prev != NULL) { + // fill token in l_tx_data from prev transaction + if(l_tx_data) { + // get token from prev tx + memcpy(l_tx_data->token_ticker, l_tx_data_prev->token_ticker, + sizeof(l_tx_data->token_ticker)); + break; + } + l_list_tmp_in = dap_list_next(l_list_tmp_in); + } + } + if(l_list_in_items) + dap_list_free(l_list_in_items); + } + HASH_ADD(hh, l_tx_data_hash, tx_hash, sizeof(dap_chain_hash_fast_t), l_tx_data); + } + l_list_tmp = dap_list_next(l_list_tmp); + } + if(l_list_out_items) + dap_list_free(l_list_out_items); + + // calc hash + dap_chain_hash_fast_t l_tx_hash; + dap_hash_fast(l_tx, dap_chain_datum_tx_get_size(l_tx), &l_tx_hash); + // search tx with a_tx_hash + if(!dap_hash_fast_compare(a_tx_hash, &l_tx_hash)) + continue; + // found a_tx_hash now + + // transaction time + char *l_time_str = NULL; + if(l_tx->header.ts_created > 0) { + time_t rawtime = (time_t) l_tx->header.ts_created; + struct tm * timeinfo; + timeinfo = localtime(&rawtime); + if(timeinfo) { + dap_string_append_printf(l_str_out, " %s", asctime(timeinfo)); + } + } + + // find all OUT items in transaction + l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT, NULL); + l_list_tmp = l_list_out_items; + while(l_list_tmp) { + const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_list_tmp->data; + dap_tx_data_t *l_tx_data_prev = NULL; + + const char *l_token_str = NULL; + if(l_tx_data) + l_token_str = l_tx_data->token_ticker; + char *l_dst_to_str = + (l_tx_out) ? dap_chain_addr_to_str(&l_tx_out->addr) : + NULL; + dap_string_append_printf(l_str_out, " OUT item %lld %s to %s\n", + l_tx_out->header.value, + dap_strlen(l_token_str) > 0 ? l_token_str : "?", + l_dst_to_str ? l_dst_to_str : "?" + ); + DAP_DELETE(l_dst_to_str); + l_list_tmp = dap_list_next(l_list_tmp); + } + // find all IN items in transaction + dap_list_t *l_list_in_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN, NULL); + l_list_tmp = l_list_in_items; + // find cur addr in prev OUT items + while(l_list_tmp) { + const dap_chain_tx_in_t *l_tx_in = (const dap_chain_tx_in_t*) l_list_tmp->data; + dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash; + char l_tx_hash_str[70]; + if(!dap_hash_fast_is_blank(&tx_prev_hash)) + dap_chain_hash_fast_to_str(&tx_prev_hash, l_tx_hash_str, sizeof(l_tx_hash_str)); + else + strcpy(l_tx_hash_str,"Null"); + dap_string_append_printf(l_str_out, " IN item \n prev tx_hash %s\n", l_tx_hash_str); + + //find prev OUT item + dap_tx_data_t *l_tx_data_prev = NULL; + HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), l_tx_data_prev); + if(l_tx_data_prev != NULL) { + + dap_store_obj_t *l_obj_prev = get_prev_tx(l_objs, l_tx_data_prev); + dap_chain_datum_t *l_datum_prev = + l_obj_prev ? (dap_chain_datum_t*) l_obj_prev->value : NULL; + dap_chain_datum_tx_t *l_tx_prev = + l_datum_prev ? (dap_chain_datum_tx_t*) l_datum_prev->data : NULL; + + // find OUT items in prev datum + dap_list_t *l_list_out_prev_items = dap_chain_datum_tx_items_get(l_tx_prev, + TX_ITEM_TYPE_OUT, NULL); + // find OUT item for IN item; + dap_list_t *l_list_out_prev_item = dap_list_nth(l_list_out_prev_items, + l_tx_in->header.tx_out_prev_idx); + dap_chain_tx_out_t *l_tx_prev_out = + l_list_out_prev_item ? + (dap_chain_tx_out_t*) l_list_out_prev_item->data : + NULL; + // print value from prev out item + dap_string_append_printf(l_str_out, " prev OUT item value=%lld", + l_tx_prev_out->header.value + ); + } + dap_string_append_printf(l_str_out, "\n"); + l_list_tmp = dap_list_next(l_list_tmp); + } + + if(l_list_tx_token) + dap_list_free(l_list_tx_token); + if(l_list_out_items) + dap_list_free(l_list_out_items); + if(l_list_in_items) + dap_list_free(l_list_in_items); + l_tx_hash_found = true; + break; + } + dap_list_t *l_records_out = NULL; + + DAP_DELETE(l_obj); + dap_strfreev(l_keys); + // transaction was found -> exit + if(l_tx_hash_found) + break; + } + dap_chain_global_db_objs_delete(l_objs, l_data_size_out); + // if no history + if(!l_str_out->len) + dap_string_append(l_str_out, "empty"); + char *l_ret_str = l_str_out ? dap_string_free(l_str_out, false) : NULL; + return l_ret_str; +} +#endif + +/** + * Get data according the history log + * + * return history string + */ +#if 0 +char* dap_db_history_addr(dap_chain_addr_t * a_addr, const char *a_group_mempool) +{ + dap_string_t *l_str_out = dap_string_new(NULL); + // load history + size_t l_data_size_out = 0; + dap_global_db_obj_t *l_objs = dap_chain_global_db_gr_load(GROUP_LOCAL_HISTORY, &l_data_size_out); + size_t i, j; + dap_tx_data_t *l_tx_data_hash = NULL; + for(i = 0; i < l_data_size_out; i++) { + dap_global_db_obj_t *l_obj_cur = l_objs + i; + // parse global_db records in a history record + dap_global_db_hist_t l_rec; + if(dap_db_history_unpack_hist((char*) l_obj_cur->value, &l_rec) == -1) + continue; + // use only groups with datums + if(dap_strcmp(a_group_mempool, l_rec.group)) + continue; + + char **l_keys = dap_strsplit(l_rec.keys, GLOBAL_DB_HIST_KEY_SEPARATOR, -1); + size_t l_count = dap_str_countv(l_keys); + dap_store_obj_t *l_obj = NULL; + // all objs in one history records + for(j = 0; j < l_count; j++) { + if(l_rec.type != 'a') + continue; + l_obj = (dap_store_obj_t*) dap_chain_global_db_obj_get(l_keys[j], l_rec.group); + if(!l_obj) + continue; + // datum + dap_chain_datum_t *l_datum = (dap_chain_datum_t*) l_obj->value; + if(!l_datum && l_datum->header.type_id != DAP_CHAIN_DATUM_TX) + continue; + + // transaction + dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*) l_datum->data; + dap_list_t *l_records_out = NULL; + // transaction time + char *l_time_str = NULL; + { + if(l_tx->header.ts_created > 0) { + time_t rawtime = (time_t) l_tx->header.ts_created; + struct tm * timeinfo; + timeinfo = localtime(&rawtime); + if(timeinfo) + l_time_str = dap_strdup(asctime(timeinfo)); + } + else + l_time_str = dap_strdup(" "); + } + + // transaction + dap_tx_data_t *l_tx_data = NULL; + + // find Token items - present in emit transaction + dap_list_t *l_list_tx_token = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_TOKEN, NULL); + + // find OUT items + dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT, NULL); + dap_list_t *l_list_tmp = l_list_out_items; + while(l_list_tmp) { + const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_list_tmp->data; + // save OUT item l_tx_out + { + // save tx hash + l_tx_data = DAP_NEW_Z(dap_tx_data_t); + dap_chain_hash_fast_t l_tx_hash; + dap_hash_fast(l_tx, dap_chain_datum_tx_get_size(l_tx), &l_tx_hash); + memcpy(&l_tx_data->tx_hash, &l_tx_hash, sizeof(dap_chain_hash_fast_t)); + memcpy(&l_tx_data->addr, &l_tx_out->addr, sizeof(dap_chain_addr_t)); + dap_chain_hash_fast_to_str(&l_tx_data->tx_hash, l_tx_data->tx_hash_str, + sizeof(l_tx_data->tx_hash_str)); + l_tx_data->obj_num = i; + l_tx_data->pos_num = j; + // save token name + if(l_tx_data && l_list_tx_token) { + dap_chain_tx_token_t *tk = l_list_tx_token->data; + int d = sizeof(l_tx_data->token_ticker); + memcpy(l_tx_data->token_ticker, tk->header.ticker, sizeof(l_tx_data->token_ticker)); + } + HASH_ADD(hh, l_tx_data_hash, tx_hash, sizeof(dap_chain_hash_fast_t), l_tx_data); + + // save OUT items to list + { + l_records_out = dap_list_append(l_records_out, (void*) l_tx_out); + } + } + l_list_tmp = dap_list_next(l_list_tmp); + } + + // find IN items + l_count = 0; + dap_list_t *l_list_in_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN, NULL); + l_list_tmp = l_list_in_items; + // find cur addr in prev OUT items + bool l_is_use_all_cur_out = false; + { + while(l_list_tmp) { + const dap_chain_tx_in_t *l_tx_in = (const dap_chain_tx_in_t*) l_list_tmp->data; + dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash; + + //find prev OUT item + dap_tx_data_t *l_tx_data_prev = NULL; + HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), l_tx_data_prev); + if(l_tx_data_prev != NULL) { + // fill token in l_tx_data from prev transaction + if(l_tx_data) { + // get token from prev tx + memcpy(l_tx_data->token_ticker, l_tx_data_prev->token_ticker, + sizeof(l_tx_data->token_ticker)); + dap_store_obj_t *l_obj_prev = get_prev_tx(l_objs, l_tx_data_prev); + dap_chain_datum_t *l_datum_prev = + l_obj_prev ? (dap_chain_datum_t*) l_obj_prev->value : NULL; + dap_chain_datum_tx_t *l_tx_prev = + l_datum_prev ? (dap_chain_datum_tx_t*) l_datum_prev->data : NULL; + + // find OUT items in prev datum + dap_list_t *l_list_out_prev_items = dap_chain_datum_tx_items_get(l_tx_prev, + TX_ITEM_TYPE_OUT, NULL); + // find OUT item for IN item; + dap_list_t *l_list_out_prev_item = dap_list_nth(l_list_out_prev_items, + l_tx_in->header.tx_out_prev_idx); + dap_chain_tx_out_t *l_tx_prev_out = + l_list_out_prev_item ? + (dap_chain_tx_out_t*) l_list_out_prev_item->data : + NULL; + if(l_tx_prev_out && !memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t))) + l_is_use_all_cur_out = true; + + } + } + + // find prev OUT items for IN items + l_list_tmp = l_list_in_items; + while(l_list_tmp) { + const dap_chain_tx_in_t *l_tx_in = (const dap_chain_tx_in_t*) l_list_tmp->data; + dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash; + // if first transaction - empty prev OUT item + if(dap_hash_fast_is_blank(&tx_prev_hash)) { + // add emit info to ret string + if(!memcmp(&l_tx_data->addr, a_addr, sizeof(dap_chain_addr_t))) + { + dap_list_t *l_records_tmp = l_records_out; + while(l_records_tmp) { + + const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_records_tmp->data; + dap_string_append_printf(l_str_out, "tx hash %s \n emit %lld %s\n", + l_tx_data->tx_hash_str, + l_tx_out->header.value, + l_tx_data->token_ticker); + l_records_tmp = dap_list_next(l_records_tmp); + } + } + dap_list_free(l_records_out); + } + // in other transactions except first one + else { + //find prev OUT item + dap_tx_data_t *l_tx_data_prev = NULL; + HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), l_tx_data_prev); + if(l_tx_data_prev != NULL) { + char *l_src_str = NULL; + bool l_src_str_is_cur = false; + if(l_tx_data) { + // get token from prev tx + memcpy(l_tx_data->token_ticker, l_tx_data_prev->token_ticker, + sizeof(l_tx_data->token_ticker)); + + dap_store_obj_t *l_obj_prev = get_prev_tx(l_objs, l_tx_data_prev); + dap_chain_datum_t *l_datum_prev = + l_obj_prev ? (dap_chain_datum_t*) l_obj_prev->value : NULL; + dap_chain_datum_tx_t *l_tx_prev = + l_datum_prev ? (dap_chain_datum_tx_t*) l_datum_prev->data : NULL; + + // find OUT items in prev datum + dap_list_t *l_list_out_prev_items = dap_chain_datum_tx_items_get(l_tx_prev, + TX_ITEM_TYPE_OUT, NULL); + // find OUT item for IN item; + dap_list_t *l_list_out_prev_item = dap_list_nth(l_list_out_prev_items, + l_tx_in->header.tx_out_prev_idx); + dap_chain_tx_out_t *l_tx_prev_out = + l_list_out_prev_item ? + (dap_chain_tx_out_t*) l_list_out_prev_item->data : + NULL; + // if use src addr + bool l_is_use_src_addr = false; + // find source addrs + dap_string_t *l_src_addr = dap_string_new(NULL); + { + // find IN items in prev datum - for get destination addr + dap_list_t *l_list_in_prev_items = dap_chain_datum_tx_items_get(l_tx_prev, + TX_ITEM_TYPE_IN, NULL); + dap_list_t *l_list_tmp = l_list_in_prev_items; + while(l_list_tmp) { + dap_chain_tx_in_t *l_tx_prev_in = l_list_tmp->data; + dap_chain_hash_fast_t l_tx_prev_prev_hash = + l_tx_prev_in->header.tx_prev_hash; + //find prev OUT item + dap_tx_data_t *l_tx_data_prev_prev = NULL; + HASH_FIND(hh, l_tx_data_hash, &l_tx_prev_prev_hash, + sizeof(dap_chain_hash_fast_t), l_tx_data_prev_prev); + if(l_tx_data_prev_prev) { + // if use src addr + if(!memcmp(&l_tx_data_prev_prev->addr, a_addr, + sizeof(dap_chain_addr_t))) + l_is_use_src_addr = true; + char *l_str = dap_chain_addr_to_str(&l_tx_data_prev_prev->addr); + if(l_src_addr->len > 0) + dap_string_append_printf(l_src_addr, "\n %s", l_str); + else + dap_string_append_printf(l_src_addr, "%s", l_str); // first record + DAP_DELETE(l_str); + } + l_list_tmp = dap_list_next(l_list_tmp); + } + } + + char *l_dst_to_str = + (l_tx_prev_out) ? dap_chain_addr_to_str(&l_tx_prev_out->addr) : + NULL; + // if use dst addr + bool l_is_use_dst_addr = false; + if(!memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t))) + l_is_use_dst_addr = true; + + l_src_str_is_cur = l_is_use_src_addr; + if(l_src_addr->len <= 1) { + l_src_str = + (l_tx_data) ? dap_chain_addr_to_str(&l_tx_data->addr) : + NULL; + if(!memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t))) + l_src_str_is_cur = true; + dap_string_free(l_src_addr, true); + } + else + l_src_str = dap_string_free(l_src_addr, false); + if(l_is_use_src_addr && !l_is_use_dst_addr) { + dap_string_append_printf(l_str_out, + "tx hash %s \n %s in send %lld %s from %s\n to %s\n", + l_tx_data->tx_hash_str, + l_time_str ? l_time_str : "", + l_tx_prev_out->header.value, + l_tx_data->token_ticker, + l_src_str ? l_src_str : "", + l_dst_to_str); + } else if(l_is_use_dst_addr && !l_is_use_src_addr) { + if(!l_src_str_is_cur) + dap_string_append_printf(l_str_out, + "tx hash %s \n %s in recv %lld %s from %s\n", + l_tx_data->tx_hash_str, + l_time_str ? l_time_str : "", + l_tx_prev_out->header.value, + l_tx_data->token_ticker, + l_src_str ? l_src_str : ""); + } + + DAP_DELETE(l_dst_to_str); + dap_list_free(l_list_out_prev_items); + DAP_DELETE(l_obj_prev); + } + + // OUT items + dap_list_t *l_records_tmp = l_records_out; + while(l_records_tmp) { + + const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_records_tmp->data; + + if(l_is_use_all_cur_out + || !memcmp(&l_tx_out->addr, a_addr, sizeof(dap_chain_addr_t))) { + + char *l_addr_str = (l_tx_out) ? dap_chain_addr_to_str(&l_tx_out->addr) : NULL; + + if(!memcmp(&l_tx_out->addr, a_addr, sizeof(dap_chain_addr_t))) { + if(!l_src_str_is_cur) + dap_string_append_printf(l_str_out, "tx hash %s \n %s recv %lld %s from %s\n", + l_tx_data->tx_hash_str, + l_time_str ? l_time_str : "", + l_tx_out->header.value, + l_tx_data_prev->token_ticker, + l_src_str ? l_src_str : "?"); + } + else { + dap_string_append_printf(l_str_out, "tx hash %s \n %s send %lld %s to %sd\n", + l_tx_data->tx_hash_str, + l_time_str ? l_time_str : "", + l_tx_out->header.value, + l_tx_data_prev->token_ticker, + l_addr_str ? l_addr_str : ""); + } + DAP_DELETE(l_addr_str); + } + l_records_tmp = dap_list_next(l_records_tmp); + } + dap_list_free(l_records_out); + DAP_DELETE(l_src_str); + + } + } + l_list_tmp = dap_list_next(l_list_tmp); + } + l_list_tmp = dap_list_next(l_list_tmp); + } + } + + + + if(l_list_tx_token) + dap_list_free(l_list_tx_token); + if(l_list_out_items) + dap_list_free(l_list_out_items); + if(l_list_in_items) + dap_list_free(l_list_in_items); + + DAP_DELETE(l_time_str); + } + DAP_DELETE(l_obj); + dap_strfreev(l_keys); + + } + // delete hashes + dap_tx_data_t *l_iter_current, *l_item_tmp; + HASH_ITER(hh, l_tx_data_hash , l_iter_current, l_item_tmp) + { + // delete struct + DAP_DELETE(l_iter_current); + HASH_DEL(l_tx_data_hash, l_iter_current); + } + dap_chain_global_db_objs_delete(l_objs, l_data_size_out); + // if no history + if(!l_str_out->len) + dap_string_append(l_str_out, " empty"); + char *l_ret_str = l_str_out ? dap_string_free(l_str_out, false) : NULL; + return l_ret_str; +} +#endif + + +/** + * Get data according the history log + * + * return history string + */ +char* dap_db_history_filter(dap_chain_addr_t * a_addr, const char *a_group_mempool) +{ + dap_string_t *l_str_out = dap_string_new(NULL); + // load history + size_t l_data_size_out = 0; + dap_global_db_obj_t *l_objs = dap_chain_global_db_gr_load(GROUP_LOCAL_HISTORY, &l_data_size_out); + size_t i, j; + dap_tx_data_t *l_tx_data_hash = NULL; + for(i = 0; i < l_data_size_out; i++) { + dap_global_db_obj_t *l_obj_cur = l_objs + i; + + // parse global_db records in a history record + dap_global_db_hist_t l_rec; + if(dap_db_history_unpack_hist((char*) l_obj_cur->value, &l_rec) == -1) + continue; + // use only groups with datums + if(dap_strcmp(a_group_mempool, l_rec.group)) + continue; + + char **l_keys = dap_strsplit(l_rec.keys, GLOBAL_DB_HIST_KEY_SEPARATOR, -1); + size_t l_count = dap_str_countv(l_keys); + dap_store_obj_t *l_obj = NULL; + // all objs in one history records + for(j = 0; j < l_count; j++) { + // add record + if(l_rec.type == 'a') { + l_obj = (dap_store_obj_t*) dap_chain_global_db_obj_get(l_keys[j], l_rec.group); + if(!l_obj) + continue; + dap_chain_datum_t *l_datum = (dap_chain_datum_t*) l_obj->value; + if(!l_datum) + continue; + switch (l_datum->header.type_id) { + /* case DAP_CHAIN_DATUM_TOKEN_DECL: { + dap_chain_datum_token_t *l_token = (dap_chain_datum_token_t*) l_datum->data; + } + break; + case DAP_CHAIN_DATUM_TOKEN_EMISSION: { + dap_chain_datum_token_emission_t *l_token_emission = + (dap_chain_datum_token_emission_t*) l_datum->data; + } + break;*/ + // find transaction + case DAP_CHAIN_DATUM_TX: { + dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*) l_datum->data; + dap_list_t *l_records_out = NULL; + + // transaction time + char *l_time_str = NULL; + if(l_tx->header.ts_created > 0) { + time_t rawtime = (time_t) l_tx->header.ts_created; + struct tm * timeinfo; + timeinfo = localtime(&rawtime); + if(timeinfo) + l_time_str = dap_strdup(asctime(timeinfo)); + } + else + l_time_str = dap_strdup(" "); + + int l_count = 0; + dap_tx_data_t *l_tx_data = NULL; + // find Token items - present in emit transaction + l_count = 0; + dap_list_t *l_list_tx_token = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_TOKEN, &l_count); + + // find OUT items + dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT, &l_count); + dap_list_t *l_list_tmp = l_list_out_items; + while(l_list_tmp) { + const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_list_tmp->data; + // save OUT item l_tx_out + { + // save tx hash + l_tx_data = DAP_NEW_Z(dap_tx_data_t); + dap_chain_hash_fast_t l_tx_hash; + dap_hash_fast(l_tx, dap_chain_datum_tx_get_size(l_tx), &l_tx_hash); + memcpy(&l_tx_data->tx_hash, &l_tx_hash, sizeof(dap_chain_hash_fast_t)); + memcpy(&l_tx_data->addr, &l_tx_out->addr, sizeof(dap_chain_addr_t)); + l_tx_data->obj_num = i; + l_tx_data->pos_num = j; + // save token name + if(l_tx_data && l_list_tx_token) { + dap_chain_tx_token_t *tk = l_list_tx_token->data; + int d = sizeof(l_tx_data->token_ticker); + memcpy(l_tx_data->token_ticker, tk->header.ticker, sizeof(l_tx_data->token_ticker)); + } + HASH_ADD(hh, l_tx_data_hash, tx_hash, sizeof(dap_chain_hash_fast_t), l_tx_data); + + // save OUT items to list + { + l_records_out = dap_list_append(l_records_out, (void*) l_tx_out); + } + } + l_list_tmp = dap_list_next(l_list_tmp); + } + + // find IN items + l_count = 0; + dap_list_t *l_list_in_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN, &l_count); + l_list_tmp = l_list_in_items; + + // find cur addr in prev OUT items + bool l_is_use_all_cur_out = false; + { + while(l_list_tmp) { + const dap_chain_tx_in_t *l_tx_in = (const dap_chain_tx_in_t*) l_list_tmp->data; + dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash; + + //find prev OUT item + dap_tx_data_t *l_tx_data_prev = NULL; + HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), l_tx_data_prev); + if(l_tx_data_prev != NULL) { + // fill token in l_tx_data from prev transaction + if(l_tx_data) { + // get token from prev tx + memcpy(l_tx_data->token_ticker, l_tx_data_prev->token_ticker, + sizeof(l_tx_data->token_ticker)); + dap_store_obj_t *l_obj_prev = get_prev_tx(l_objs, l_tx_data_prev); + dap_chain_datum_t *l_datum_prev = + l_obj_prev ? (dap_chain_datum_t*) l_obj_prev->value : NULL; + dap_chain_datum_tx_t *l_tx_prev = + l_datum_prev ? (dap_chain_datum_tx_t*) l_datum_prev->data : NULL; + + // find OUT items in prev datum + dap_list_t *l_list_out_prev_items = dap_chain_datum_tx_items_get(l_tx_prev, + TX_ITEM_TYPE_OUT, &l_count); + // find OUT item for IN item; + dap_list_t *l_list_out_prev_item = dap_list_nth(l_list_out_prev_items, + l_tx_in->header.tx_out_prev_idx); + dap_chain_tx_out_t *l_tx_prev_out = + l_list_out_prev_item ? + (dap_chain_tx_out_t*) l_list_out_prev_item->data : + NULL; + if(l_tx_prev_out && !memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t))) + l_is_use_all_cur_out = true; + + } + } + l_list_tmp = dap_list_next(l_list_tmp); + } + } + + // find prev OUT items for IN items + l_list_tmp = l_list_in_items; + while(l_list_tmp) { + const dap_chain_tx_in_t *l_tx_in = (const dap_chain_tx_in_t*) l_list_tmp->data; + dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash; + // if first transaction - empty prev OUT item + if(dap_hash_fast_is_blank(&tx_prev_hash)) { + // add emit info to ret string + if(!memcmp(&l_tx_data->addr, a_addr, sizeof(dap_chain_addr_t))) + { + dap_list_t *l_records_tmp = l_records_out; + while(l_records_tmp) { + const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_records_tmp->data; + dap_string_append_printf(l_str_out, "emit %lld %s\n", + l_tx_out->header.value, + l_tx_data->token_ticker); + l_records_tmp = dap_list_next(l_records_tmp); + } + } + dap_list_free(l_records_out); + } + // in other transactions except first one + else { + //find prev OUT item + dap_tx_data_t *l_tx_data_prev = NULL; + HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), l_tx_data_prev); + if(l_tx_data_prev != NULL) { + char *l_src_str = NULL; + bool l_src_str_is_cur = false; + if(l_tx_data) { + // get token from prev tx + memcpy(l_tx_data->token_ticker, l_tx_data_prev->token_ticker, + sizeof(l_tx_data->token_ticker)); + + dap_store_obj_t *l_obj_prev = get_prev_tx(l_objs, l_tx_data_prev); + dap_chain_datum_t *l_datum_prev = + l_obj_prev ? (dap_chain_datum_t*) l_obj_prev->value : NULL; + dap_chain_datum_tx_t *l_tx_prev = + l_datum_prev ? (dap_chain_datum_tx_t*) l_datum_prev->data : NULL; + + // find OUT items in prev datum + dap_list_t *l_list_out_prev_items = dap_chain_datum_tx_items_get(l_tx_prev, + TX_ITEM_TYPE_OUT, &l_count); + // find OUT item for IN item; + dap_list_t *l_list_out_prev_item = dap_list_nth(l_list_out_prev_items, + l_tx_in->header.tx_out_prev_idx); + dap_chain_tx_out_t *l_tx_prev_out = + l_list_out_prev_item ? + (dap_chain_tx_out_t*) l_list_out_prev_item->data : + NULL; + // if use src addr + bool l_is_use_src_addr = false; + // find source addrs + dap_string_t *l_src_addr = dap_string_new(NULL); + { + // find IN items in prev datum - for get destination addr + dap_list_t *l_list_in_prev_items = dap_chain_datum_tx_items_get(l_tx_prev, + TX_ITEM_TYPE_IN, &l_count); + dap_list_t *l_list_tmp = l_list_in_prev_items; + while(l_list_tmp) { + dap_chain_tx_in_t *l_tx_prev_in = l_list_tmp->data; + dap_chain_hash_fast_t l_tx_prev_prev_hash = + l_tx_prev_in->header.tx_prev_hash; + //find prev OUT item + dap_tx_data_t *l_tx_data_prev_prev = NULL; + HASH_FIND(hh, l_tx_data_hash, &l_tx_prev_prev_hash, + sizeof(dap_chain_hash_fast_t), l_tx_data_prev_prev); + if(l_tx_data_prev_prev) { + // if use src addr + if(!memcmp(&l_tx_data_prev_prev->addr, a_addr, + sizeof(dap_chain_addr_t))) + l_is_use_src_addr = true; + char *l_str = dap_chain_addr_to_str(&l_tx_data_prev_prev->addr); + if(l_src_addr->len > 0) + dap_string_append_printf(l_src_addr, "\n %s", l_str); + else + dap_string_append_printf(l_src_addr, "%s", l_str); // first record + DAP_DELETE(l_str); + } + l_list_tmp = dap_list_next(l_list_tmp); + } + } + + char *l_dst_to_str = + (l_tx_prev_out) ? dap_chain_addr_to_str(&l_tx_prev_out->addr) : + NULL; + // if use dst addr + bool l_is_use_dst_addr = false; + if(!memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t))) + l_is_use_dst_addr = true; + + l_src_str_is_cur = l_is_use_src_addr; + if(l_src_addr->len <= 1) { + l_src_str = + (l_tx_data) ? dap_chain_addr_to_str(&l_tx_data->addr) : + NULL; + if(!memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t))) + l_src_str_is_cur = true; + dap_string_free(l_src_addr, true); + } + else + l_src_str = dap_string_free(l_src_addr, false); + if(l_is_use_src_addr && !l_is_use_dst_addr) { + dap_string_append_printf(l_str_out, + "%s in send %lld %s from %s\n to %s\n", + l_time_str ? l_time_str : "", + l_tx_prev_out->header.value, + l_tx_data->token_ticker, + l_src_str ? l_src_str : "", + l_dst_to_str); + } else if(l_is_use_dst_addr && !l_is_use_src_addr) { + if(!l_src_str_is_cur) + dap_string_append_printf(l_str_out, + "%s in recv %lld %s from %s\n", + l_time_str ? l_time_str : "", + l_tx_prev_out->header.value, + l_tx_data->token_ticker, + l_src_str ? l_src_str : ""); + } + + DAP_DELETE(l_dst_to_str); + dap_list_free(l_list_out_prev_items); + DAP_DELETE(l_obj_prev); + } + + // OUT items + dap_list_t *l_records_tmp = l_records_out; + while(l_records_tmp) { + + const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_records_tmp->data; + + if(l_is_use_all_cur_out + || !memcmp(&l_tx_out->addr, a_addr, sizeof(dap_chain_addr_t))) { + + char *l_addr_str = (l_tx_out) ? dap_chain_addr_to_str(&l_tx_out->addr) : NULL; + + if(!memcmp(&l_tx_out->addr, a_addr, sizeof(dap_chain_addr_t))) { + if(!l_src_str_is_cur) + dap_string_append_printf(l_str_out, "%s recv %lld %s from %s\n", + l_time_str ? l_time_str : "", + l_tx_out->header.value, + l_tx_data_prev->token_ticker, + l_src_str ? l_src_str : "?"); + } + else { + dap_string_append_printf(l_str_out, "%s send %lld %s to %sd\n", + l_time_str ? l_time_str : "", + l_tx_out->header.value, + l_tx_data_prev->token_ticker, + l_addr_str ? l_addr_str : ""); + } + DAP_DELETE(l_addr_str); + } + l_records_tmp = dap_list_next(l_records_tmp); + } + dap_list_free(l_records_out); + DAP_DELETE(l_src_str); + + } + } + l_list_tmp = dap_list_next(l_list_tmp); + } + if(l_list_tx_token) + dap_list_free(l_list_tx_token); + if(l_list_out_items) + dap_list_free(l_list_out_items); + if(l_list_in_items) + dap_list_free(l_list_in_items); + + DAP_DELETE(l_time_str); + } + break; + default: + continue; + } + } + // delete record + else if(l_rec.type == 'd') { + //printf("del_gr%d_%d=%s\n", i, j, l_rec.group); + } + } + DAP_DELETE(l_obj); + dap_strfreev(l_keys); + } + // delete hashes + dap_tx_data_t *l_iter_current, *l_item_tmp; + HASH_ITER(hh, l_tx_data_hash , l_iter_current, l_item_tmp) + { + // delete struct + DAP_DELETE(l_iter_current); + HASH_DEL(l_tx_data_hash, l_iter_current); + } + dap_chain_global_db_objs_delete(l_objs, l_data_size_out); + // if no history + if(!l_str_out->len) + dap_string_append(l_str_out, "empty"); + char *l_ret_str = l_str_out ? dap_string_free(l_str_out, false) : NULL; + return l_ret_str; +} + /** * Add data to the history log */