From 263362ad7e90f6f1329cf3a6a1124e5b9778e586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Al=D0=B5x=D0=B0nder=20Lysik=D0=BEv?= <alexander.lysikov@demlabs.net> Date: Tue, 18 Aug 2020 22:32:38 +0500 Subject: [PATCH] added command 'ledger' and 'token' --- modules/chain/dap_chain.c | 31 +- modules/chain/include/dap_chain.h | 3 + modules/global-db/dap_chain_global_db_hist.c | 6 +- .../global-db/include/dap_chain_global_db.h | 2 +- modules/net/dap_chain_node_cli.c | 13 + modules/net/dap_chain_node_cli_cmd_tx.c | 816 +++++++++++++++++- .../net/include/dap_chain_node_cli_cmd_tx.h | 13 + 7 files changed, 871 insertions(+), 13 deletions(-) diff --git a/modules/chain/dap_chain.c b/modules/chain/dap_chain.c index dd0910e8f6..6f476a3056 100644 --- a/modules/chain/dap_chain.c +++ b/modules/chain/dap_chain.c @@ -93,8 +93,35 @@ void dap_chain_deinit(void) dap_chain_item_t * l_item = NULL, *l_tmp = NULL; pthread_rwlock_wrlock(&s_chain_items_rwlock); HASH_ITER(hh, s_chain_items, l_item, l_tmp) { - dap_chain_delete(s_chain_items->chain); - } + dap_chain_delete(l_item->chain); + } + pthread_rwlock_unlock(&s_chain_items_rwlock); +} + + +/** + * @brief dap_chain_deinit + * note: require dap_chain_enum_unlock() after + */ +dap_chain_t* dap_chain_enum(void** a_item) +{ + // if a_item == 0x1 then first item + dap_chain_item_t *l_item_start = (*a_item == 0x1) ? s_chain_items : (dap_chain_item_t*) *a_item; + dap_chain_item_t *l_item = NULL; + dap_chain_item_t *l_item_tmp = NULL; + pthread_rwlock_wrlock(&s_chain_items_rwlock); + HASH_ITER(hh, l_item_start, l_item, l_item_tmp) { + *a_item = l_item_tmp; + return l_item->chain; + } + return NULL ; +} + +/** + * @brief dap_chain_enum_unlock + */ +void dap_chain_enum_unlock(void) +{ pthread_rwlock_unlock(&s_chain_items_rwlock); } diff --git a/modules/chain/include/dap_chain.h b/modules/chain/include/dap_chain.h index 0888d5eaa2..ee2419d5e7 100644 --- a/modules/chain/include/dap_chain.h +++ b/modules/chain/include/dap_chain.h @@ -158,6 +158,9 @@ typedef struct dap_chain{ int dap_chain_init(void); void dap_chain_deinit(void); +dap_chain_t* dap_chain_enum(void** a_item); +void dap_chain_enum_unlock(void); + dap_chain_t * dap_chain_create(dap_ledger_t* a_ledger,const char * a_chain_net_name, const char * a_chain_name, dap_chain_net_id_t a_chain_net_id, dap_chain_id_t a_chain_id ); diff --git a/modules/global-db/dap_chain_global_db_hist.c b/modules/global-db/dap_chain_global_db_hist.c index c2683b1560..e552cd7e71 100644 --- a/modules/global-db/dap_chain_global_db_hist.c +++ b/modules/global-db/dap_chain_global_db_hist.c @@ -12,7 +12,7 @@ #include "dap_chain_global_db_hist.h" #include "uthash.h" -// for dap_db_history_filter() +// for dap_db_history() typedef struct dap_tx_data{ dap_chain_hash_fast_t tx_hash; char tx_hash_str[70]; @@ -135,7 +135,7 @@ uint8_t* dap_db_log_pack(dap_global_db_obj_t *a_obj, size_t *a_data_size_out) } -// for dap_db_history_filter() +// for dap_db_history() 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) @@ -724,7 +724,7 @@ char* dap_db_history_addr(dap_chain_addr_t * a_addr, const char *a_group_mempool * * return history string */ -char* dap_db_history_filter(dap_chain_addr_t * a_addr, const char *a_group_mempool) +char* dap_db_history(dap_chain_addr_t * a_addr, const char *a_group_mempool) { dap_string_t *l_str_out = dap_string_new(NULL); // load history diff --git a/modules/global-db/include/dap_chain_global_db.h b/modules/global-db/include/dap_chain_global_db.h index 4ac3c5a9a3..fdbc872c92 100644 --- a/modules/global-db/include/dap_chain_global_db.h +++ b/modules/global-db/include/dap_chain_global_db.h @@ -124,7 +124,7 @@ 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); +//char* dap_db_history(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); diff --git a/modules/net/dap_chain_node_cli.c b/modules/net/dap_chain_node_cli.c index 8ce67e110d..a14707aede 100644 --- a/modules/net/dap_chain_node_cli.c +++ b/modules/net/dap_chain_node_cli.c @@ -62,6 +62,7 @@ #include "dap_list.h" #include "dap_chain_node_cli_cmd.h" #include "dap_chain_node_client.h" +#include "dap_chain_node_cli_cmd_tx.h" #include "dap_chain_node_cli.h" //#include "dap_chain_node_cli.h" @@ -957,6 +958,18 @@ int dap_chain_node_cli_init(dap_config_t * g_config) dap_chain_node_cli_cmd_item_create("tx_history", com_tx_history, NULL, "Transaction history (for address or by hash)", "tx_history [-addr <addr> | -w <wallet name> | -tx <tx_hash>] -net <net name> -chain <chain name>\n"); + // Ledger info + dap_chain_node_cli_cmd_item_create("ledger", com_ledger, NULL, "Ledger info", + "ledger list coins -net <network name>\n" + "ledger list coins_cond -net <network name>\n" + "ledger list addrs -net <network name>\n" + "ledger tx [all | -addr <addr> | -w <wallet name> | -tx <tx_hash>] [-chain <chain name>] -net <network name>\n"); + + // Token info + dap_chain_node_cli_cmd_item_create("token", com_token, NULL, "Token info", + "token list -net <network name>\n" + "token tx all name <token name> -net <network name> [-page_start <page>] [-page <page>]\n"); + // Log dap_chain_node_cli_cmd_item_create ("print_log", com_print_log, NULL, "Print log info", "print_log [ts_after <timestamp >] [limit <line numbers>]\n" ); diff --git a/modules/net/dap_chain_node_cli_cmd_tx.c b/modules/net/dap_chain_node_cli_cmd_tx.c index c34c4e7e81..d70e24efb8 100644 --- a/modules/net/dap_chain_node_cli_cmd_tx.c +++ b/modules/net/dap_chain_node_cli_cmd_tx.c @@ -26,15 +26,18 @@ #include <stddef.h> #include <pthread.h> -#include <dap_common.h> -#include <dap_enc_base58.h> -#include <dap_strfuncs.h> -#include <dap_string.h> -#include <dap_list.h> -#include <dap_hash.h> +#include "dap_common.h" +#include "dap_enc_base58.h" +#include "dap_strfuncs.h" +#include "dap_string.h" +#include "dap_list.h" +#include "dap_hash.h" +#include "dap_chain_wallet.h" +#include "dap_chain_datum.h" +#include "dap_chain_datum_token.h" #include "dap_chain_datum_tx_items.h" - +#include "dap_chain_node_cli.h" #include "dap_chain_node_cli_cmd_tx.h" #define LOG_TAG "chain_node_cli_cmd_tx" @@ -676,3 +679,802 @@ char* dap_db_history_addr(dap_chain_addr_t * a_addr, dap_chain_t * a_chain, cons char *l_ret_str = l_str_out ? dap_string_free(l_str_out, false) : NULL; return l_ret_str; } + +static char* dap_db_history_token_list(dap_chain_t * a_chain, const char *a_token_name, const char *a_hash_out_type, size_t *a_token_num) +{ + dap_string_t *l_str_out = dap_string_new(NULL); + *a_token_num = 0; + bool l_tx_hash_found = false; + // list all transactions + dap_tx_data_t *l_tx_data_hash = NULL; + // load transactions + dap_chain_atom_iter_t *l_atom_iter = a_chain->callback_atom_iter_create(a_chain); + dap_chain_atom_ptr_t *l_atom = a_chain->callback_atom_iter_get_first(l_atom_iter); + size_t l_atom_size = a_chain->callback_atom_get_size(l_atom); + while(l_atom && l_atom_size) { + dap_chain_datum_t *l_datum = + a_chain->callback_atom_get_datum ? + a_chain->callback_atom_get_datum(l_atom) : (dap_chain_datum_t*) l_atom; + if(!l_datum) { + // go to next transaction + l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter); + l_atom_size = a_chain->callback_atom_get_size(l_atom); + log_it(L_ERROR, "datum=NULL for atom=0x%x", l_atom); + continue; + } + char l_time_str[70]; + // get time of create datum + if(dap_time_to_str_rfc822(l_time_str, 71, l_datum->header.ts_create) < 1) + l_time_str[0] = '\0'; + if(l_datum->header.type_id==DAP_CHAIN_DATUM_TOKEN_DECL) { + dap_chain_datum_token_t *l_token = (dap_chain_datum_token_t*) l_datum->data; + if(!a_token_name || !dap_strcmp(l_token->ticker, a_token_name)) { + dap_string_append_printf(l_str_out, "token %s, created: %s\n", l_token->ticker, l_time_str); + switch (l_token->type) { + // Simple private token decl + case DAP_CHAIN_DATUM_TOKEN_TYPE_SIMPLE: + dap_string_append_printf(l_str_out, " total_supply: %.0llf(%llu), signs: valid/total %02d/%02d \n", + l_token->header_private.total_supply / DATOSHI_LD, + l_token->header_private.total_supply, + l_token->header_private.signs_valid, l_token->header_private.signs_total); + break; + case DAP_CHAIN_DATUM_TOKEN_TYPE_PRIVATE_DECL: + dap_string_append_printf(l_str_out, " tsd_total_size: %llu, flags: 0x%x \n", + l_token->header_private_decl.tsd_total_size, + l_token->header_private_decl.flags); + break; + case DAP_CHAIN_DATUM_TOKEN_TYPE_PRIVATE_UPDATE: + dap_string_append_printf(l_str_out, " tsd_total_size: %llu, padding: 0x%x \n", + l_token->header_private_update.tsd_total_size, + l_token->header_private_update.padding); + break; + case DAP_CHAIN_DATUM_TOKEN_TYPE_PUBLIC: { + char *l_addr = dap_chain_addr_to_str(&l_token->header_public.premine_address); + dap_string_append_printf(l_str_out, + " total_supply: %.0llf(%llu), flags: 0x%x\n, premine_supply: %llu, premine_address '%s'\n", + l_token->header_public.total_supply / DATOSHI_LD, + l_token->header_public.total_supply, + l_token->header_public.flags, + l_token->header_public.premine_supply, + l_addr ? l_addr : "-"); + DAP_DELETE(l_addr); + } + break; + default: + dap_string_append_printf(l_str_out, "unknown token type: 0x%x\n", l_token->type); + break; + + } + dap_string_append_printf(l_str_out, "\n"); + (*a_token_num)++; + } + } + + // go to next transaction + l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter); + l_atom_size = a_chain->callback_atom_get_size(l_atom); + } + + a_chain->callback_atom_iter_delete(l_atom_iter); + char *l_ret_str = l_str_out ? dap_string_free(l_str_out, false) : NULL; + return l_ret_str; +} + +/** + * Get data according the history log + * + * return history string + */ +static char* dap_db_history_filter(dap_chain_t * a_chain, const char *a_token_name, const char *a_hash_out_type) +{ + dap_string_t *l_str_out = dap_string_new(NULL); + + bool l_tx_hash_found = false; + // list all transactions + dap_tx_data_t *l_tx_data_hash = NULL; + // load transactions + dap_chain_atom_iter_t *l_atom_iter = a_chain->callback_atom_iter_create(a_chain); + dap_chain_atom_ptr_t *l_atom = a_chain->callback_atom_iter_get_first(l_atom_iter); + size_t l_atom_size = a_chain->callback_atom_get_size(l_atom); + size_t l_datum_num = 0, l_token_num = 0, l_emission_num = 0, l_tx_num = 0; + while(l_atom && l_atom_size) { + dap_chain_datum_t *l_datum = + a_chain->callback_atom_get_datum ? + a_chain->callback_atom_get_datum(l_atom) : (dap_chain_datum_t*) l_atom; + if(!l_datum) { + // go to next transaction + l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter); + l_atom_size = a_chain->callback_atom_get_size(l_atom); + log_it(L_ERROR, "datum=NULL for atom=0x%x", l_atom); + continue; + } + char l_time_str[70]; + // get time of create datum + if(dap_time_to_str_rfc822(l_time_str, 71, l_datum->header.ts_create) < 1) + l_time_str[0] = '\0'; + switch (l_datum->header.type_id) { + + // token + case DAP_CHAIN_DATUM_TOKEN_DECL: { + dap_chain_datum_token_t *l_token = (dap_chain_datum_token_t*) l_datum->data; + if(!a_token_name || !dap_strcmp(l_token->ticker, a_token_name)) { + dap_string_append_printf(l_str_out, "token %s, created: %s\n", l_token->ticker, l_time_str); + switch (l_token->type) { + // Simple private token decl + case DAP_CHAIN_DATUM_TOKEN_TYPE_SIMPLE: + dap_string_append_printf(l_str_out, " total_supply: %.0llf(%llu), signs: valid/total %02d/%02d \n", + l_token->header_private.total_supply / DATOSHI_LD, + l_token->header_private.total_supply, + l_token->header_private.signs_valid, l_token->header_private.signs_total); + break; + case DAP_CHAIN_DATUM_TOKEN_TYPE_PRIVATE_DECL: + dap_string_append_printf(l_str_out, " tsd_total_size: %llu, flags: 0x%x \n", + l_token->header_private_decl.tsd_total_size, + l_token->header_private_decl.flags); + break; + case DAP_CHAIN_DATUM_TOKEN_TYPE_PRIVATE_UPDATE: + dap_string_append_printf(l_str_out, " tsd_total_size: %llu, padding: 0x%x \n", + l_token->header_private_update.tsd_total_size, + l_token->header_private_update.padding); + break; + case DAP_CHAIN_DATUM_TOKEN_TYPE_PUBLIC: { + char *l_addr = dap_chain_addr_to_str(&l_token->header_public.premine_address); + dap_string_append_printf(l_str_out, + " total_supply: %.0llf(%llu), flags: 0x%x\n, premine_supply: %llu, premine_address '%s'\n", + l_token->header_public.total_supply / DATOSHI_LD, + l_token->header_public.total_supply, + l_token->header_public.flags, + l_token->header_public.premine_supply, + l_addr ? l_addr : "-"); + DAP_DELETE(l_addr); + } + break; + default: + dap_string_append_printf(l_str_out, "unknown token type: 0x%x\n", l_token->type); + break; + + } + dap_string_append_printf(l_str_out, "\n"); + l_token_num++; + } + } + break; + + // emission + case DAP_CHAIN_DATUM_TOKEN_EMISSION: { + dap_chain_datum_token_emission_t *l_token_em = (dap_chain_datum_token_emission_t*) l_datum->data; + if(!a_token_name || !dap_strcmp(l_token_em->hdr.ticker, a_token_name)) { + dap_string_append_printf(l_str_out, "emission: %.0llf(%llu) %s, type: %s, version: %d\n", + l_token_em->hdr.value / DATOSHI_LD, l_token_em->hdr.value, l_token_em->hdr.ticker, + c_dap_chain_datum_token_emission_type_str[l_token_em->hdr.type], + l_token_em->hdr.version); + char * l_token_emission_address_str = dap_chain_addr_to_str(&(l_token_em->hdr.address)); + dap_string_append_printf(l_str_out, " to addr: %s\n", l_token_emission_address_str); + DAP_DELETE(l_token_emission_address_str); + switch (l_token_em->hdr.type) { + case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_UNDEFINED: + break; + case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_AUTH: + dap_string_append_printf(l_str_out, " signs_count: %d\n", l_token_em->data.type_auth.signs_count); + break; + case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_ALGO: + dap_string_append_printf(l_str_out, " codename: %s\n", l_token_em->data.type_algo.codename); + break; + case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_ATOM_OWNER: + dap_string_append_printf(l_str_out, " value_start: %.0llf(%llu), codename: %s\n", + l_token_em->data.type_atom_owner.value_start / DATOSHI_LD, + l_token_em->data.type_atom_owner.value_start, + l_token_em->data.type_atom_owner.value_change_algo_codename); + break; + case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_SMART_CONTRACT: { + char *l_addr = dap_chain_addr_to_str(&l_token_em->data.type_presale.addr); + // get time of create datum + if(dap_time_to_str_rfc822(l_time_str, 71, l_token_em->data.type_presale.lock_time) < 1) + l_time_str[0] = '\0'; + dap_string_append_printf(l_str_out, " flags: 0x%x, lock_time: %s\n", + l_token_em->data.type_presale.flags, l_time_str); + dap_string_append_printf(l_str_out, " addr: %s\n", l_addr); + DAP_DELETE(l_addr); + } + break; + } + dap_string_append_printf(l_str_out, "\n"); + l_emission_num++; + } + } + break; + + // transaction + case DAP_CHAIN_DATUM_TX:{ + 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_tx_data_t *l_tx_data = NULL; + + + // calc tx 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); + char *tx_hash_str; + char l_tx_hash_str[70]; + dap_chain_hash_fast_to_str(&l_tx_hash, l_tx_hash_str, 70); + if(!dap_strcmp(a_hash_out_type, "hex")) + tx_hash_str = dap_strdup(l_tx_hash_str); + else + tx_hash_str = dap_enc_base58_from_hex_str_to_str(l_tx_hash_str); + + dap_string_append_printf(l_str_out, "transaction: %s hash: %s\n", l_list_tx_token ? "(emit)" : "", tx_hash_str); + DAP_DELETE(tx_hash_str); + + 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 - only for first OUT item + 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->datum = DAP_NEW_SIZE(dap_chain_datum_t, l_atom_size); + memcpy(l_tx_data->datum, l_datum, l_atom_size); + // save token name + if(l_list_tx_token) { + dap_chain_tx_token_t *tk = l_list_tx_token->data; + 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); + + // found a_tx_hash now + // transaction time + if(l_tx->header.ts_created > 0) { + time_t rawtime = (time_t) l_tx->header.ts_created; + struct tm l_timeinfo = { 0 }; + localtime_r(&rawtime, &l_timeinfo); + dap_string_append_printf(l_str_out, " %s", asctime(&l_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]; + char *tx_hash_base58_str = NULL; + if(!dap_hash_fast_is_blank(&tx_prev_hash)) { + tx_hash_base58_str = dap_enc_base58_from_hex_str_to_str(l_tx_data->tx_hash_str); + 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"); + tx_hash_base58_str = dap_strdup("Null"); + } + if(!dap_strcmp(a_hash_out_type, "hex")) + dap_string_append_printf(l_str_out, " IN item \n prev tx_hash %s\n", l_tx_hash_str); + else + dap_string_append_printf(l_str_out, " IN item \n prev tx_hash %s\n", tx_hash_base58_str); + DAP_DELETE(tx_hash_base58_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_chain_datum_t *l_datum_prev = get_prev_tx(l_tx_data_prev); + 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 ? l_tx_prev_out->header.value : 0); + } + 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; + l_tx_num++; + } + break; + default: + dap_string_append_printf(l_str_out, "unknown datum type=%d %lld %s to %s\n", l_datum->header.type_id); + break; + } + // go to next transaction + l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter); + l_atom_size = a_chain->callback_atom_get_size(l_atom); + l_datum_num++; +/* continue; + + + + //////// 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)) { + // go to next transaction + l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter); + l_atom_size = a_chain->callback_atom_get_size(l_atom); + continue; + } + + + + + + + + //break; + + // go to next transaction + l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter); + l_atom_size = a_chain->callback_atom_get_size(l_atom); + */ + + } + a_chain->callback_atom_iter_delete(l_atom_iter); + //total + dap_string_append_printf(l_str_out, + "---------------\ntokens: %u\nemissions: %u\ntransactions: %u\ntotal datums: %u", l_token_num, + l_emission_num, l_tx_num, l_datum_num); + + // 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) + { + HASH_DEL(l_tx_data_hash, l_iter_current); + // delete datum + DAP_DELETE(l_iter_current->datum); + // delete struct + DAP_DELETE(l_iter_current); + } + + // 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; +} + + + + +/** + * ledger command + * + */ +int com_ledger(int a_argc, char ** a_argv, void *a_arg_func, char **a_str_reply) +{ + enum { CMD_NONE, CMD_LIST, CMD_TX_HISTORY }; + int arg_index = 1; + const char *l_addr_base58 = NULL; + const char *l_wallet_name = NULL; + const char *l_net_str = NULL; + const char *l_chain_str = NULL; + const char *l_tx_hash_str = NULL; + + dap_chain_t * l_chain = NULL; + dap_chain_net_t * l_net = NULL; + + const char * l_hash_out_type = NULL; + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-H", &l_hash_out_type); + if(!l_hash_out_type) + l_hash_out_type = "base58"; + if(dap_strcmp(l_hash_out_type,"hex") && dap_strcmp(l_hash_out_type,"base58")) { + dap_chain_node_cli_set_reply_text(a_str_reply, "invalid parameter -H, valid values: -H <hex | base58>"); + return -1; + } + + int l_cmd = CMD_NONE; + if (dap_chain_node_cli_find_option_val(a_argv, 1, 2, "list", NULL)) + l_cmd = CMD_LIST; + else if (dap_chain_node_cli_find_option_val(a_argv, 1, 2, "tx", NULL)) + l_cmd = CMD_TX_HISTORY; + // command tx_history + if(l_cmd == CMD_TX_HISTORY) { + bool l_is_all = dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-all", NULL); + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-addr", &l_addr_base58); + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-w", &l_wallet_name); + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-net", &l_net_str); + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-chain", &l_chain_str); + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-tx", &l_tx_hash_str); + + if(!l_is_all && !l_addr_base58 && !l_wallet_name && !l_tx_hash_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "command requires parameter '-all' or '-addr' or '-w'"); + return -1; + } + + // Select chain network + if(!l_net_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "command requires parameter '-net'"); + return -2; + } else { + if((l_net = dap_chain_net_by_name(l_net_str)) == NULL) { // Can't find such network + dap_chain_node_cli_set_reply_text(a_str_reply, + "command requires parameter '-net' to be valid chain network name"); + return -3; + } + } + //Select chain emission + if(!l_chain_str) { // chain may be null -> then all chain use + //dap_chain_node_cli_set_reply_text(a_str_reply, "command requires parameter '-chain'"); + //return -4; + } else { + if((l_chain = dap_chain_net_get_chain_by_name(l_net, l_chain_str)) == NULL) { // Can't find such chain + dap_chain_node_cli_set_reply_text(a_str_reply, + "command requires parameter '-chain' to be valid chain name in chain net %s", + l_net_str); + return -5; + } + } + //char *l_group_mempool = dap_chain_net_get_gdb_group_mempool(l_chain); + //const char *l_chain_group = dap_chain_gdb_get_group(l_chain); + + dap_chain_hash_fast_t l_tx_hash; + if(l_tx_hash_str) { + if(dap_chain_str_to_hash_fast(l_tx_hash_str, &l_tx_hash) < 0) { + l_tx_hash_str = NULL; + dap_chain_node_cli_set_reply_text(a_str_reply, "tx hash not recognized"); + return -1; + } +// char hash_str[99]; +// dap_chain_hash_fast_to_str(&l_tx_hash, hash_str,99); +// int gsdgsd=523; + } + dap_chain_addr_t *l_addr = NULL; + // if need addr + if(l_wallet_name || l_addr_base58) { + if(l_wallet_name) { + const char *c_wallets_path = dap_chain_wallet_get_path(g_config); + dap_chain_wallet_t * l_wallet = dap_chain_wallet_open(l_wallet_name, c_wallets_path); + if(l_wallet) { + dap_chain_addr_t *l_addr_tmp = (dap_chain_addr_t *) dap_chain_wallet_get_addr(l_wallet, + l_net->pub.id); + l_addr = DAP_NEW_SIZE(dap_chain_addr_t, sizeof(dap_chain_addr_t)); + memcpy(l_addr, l_addr_tmp, sizeof(dap_chain_addr_t)); + dap_chain_wallet_close(l_wallet); + } + } + if(!l_addr && l_addr_base58) { + l_addr = dap_chain_addr_from_str(l_addr_base58); + } + if(!l_addr && !l_tx_hash_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "wallet address not recognized"); + return -1; + } + } + + dap_string_t *l_str_ret = dap_string_new(NULL); //char *l_str_ret = NULL; + dap_chain_t *l_chain_cur; + void *l_chain_tmp = (void*)0x1; + int l_num = 0; + // only one chain + if(l_chain) + l_chain_cur = l_chain; + // all chain + else + l_chain_cur = dap_chain_enum(&l_chain_tmp); + while(l_chain_cur) { + // only selected net + if(l_net->pub.id.uint64 == l_chain_cur->net_id.uint64) { + // separator between chains + if(l_num>0 && !l_chain) + dap_string_append(l_str_ret, "---------------\n"); + + char *l_str_out = NULL; + dap_string_append_printf(l_str_ret, "chain: %s\n", l_chain_cur->name); + if(l_is_all) { + // without filters + l_str_out = dap_db_history_filter(l_chain_cur, NULL, l_hash_out_type); + dap_string_append_printf(l_str_ret, "all history:\n%s\n", l_str_out ? l_str_out : " empty"); + } + else { + l_str_out = l_tx_hash_str ? + dap_db_history_tx(&l_tx_hash, l_chain_cur, l_hash_out_type) : + dap_db_history_addr(l_addr, l_chain_cur, l_hash_out_type); + + if(l_tx_hash_str) { + dap_string_append_printf(l_str_ret, "history for tx hash %s:\n%s\n", l_tx_hash_str, + l_str_out ? l_str_out : " empty"); + } + else if(l_addr) { + char *l_addr_str = dap_chain_addr_to_str(l_addr); + dap_string_append_printf(l_str_ret, "history for addr %s:\n%s\n", l_addr_str, + l_str_out ? l_str_out : " empty"); + DAP_DELETE(l_addr_str); + } + } + DAP_DELETE(l_str_out); + l_num++; + } + // only one chain use + if(l_chain) + break; + dap_chain_enum_unlock(); + l_chain_cur = dap_chain_enum(&l_chain_tmp); + } + // all chain + if(!l_chain) + dap_chain_enum_unlock(); + dap_chain_node_cli_set_reply_text(a_str_reply, l_str_ret->str); + dap_string_free(l_str_ret, true); + return 0; + } + else{ + dap_chain_node_cli_set_reply_text(a_str_reply, "command requires parameter 'list' or 'tx' or 'info'"); + return -1; + } +} + +/** + * token command + * + */ +int com_token(int a_argc, char ** a_argv, void *a_arg_func, char **a_str_reply) +{ + enum { CMD_NONE, CMD_LIST, CMD_INFO, CMD_TX }; + int arg_index = 1; + const char *l_addr_base58 = NULL; + const char *l_wallet_name = NULL; + const char *l_net_str = NULL; + const char *l_chain_str = NULL; + + dap_chain_t * l_chain = NULL; + dap_chain_net_t * l_net = NULL; + + const char * l_hash_out_type = NULL; + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-H", &l_hash_out_type); + if(!l_hash_out_type) + l_hash_out_type = "base58"; + if(dap_strcmp(l_hash_out_type,"hex") && dap_strcmp(l_hash_out_type,"base58")) { + dap_chain_node_cli_set_reply_text(a_str_reply, "invalid parameter -H, valid values: -H <hex | base58>"); + return -1; + } + + //bool l_is_all = dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-all", NULL); + //dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-addr", &l_addr_base58); + //dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-w", &l_wallet_name); + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-net", &l_net_str); + //dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-chain", &l_chain_str); + //dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-tx", &l_tx_hash_str); + + // Select chain network + if(!l_net_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "command requires parameter '-net'"); + return -2; + } else { + if((l_net = dap_chain_net_by_name(l_net_str)) == NULL) { // Can't find such network + dap_chain_node_cli_set_reply_text(a_str_reply, + "command requires parameter '-net' to be valid chain network name"); + return -3; + } + } + + int l_cmd = CMD_NONE; + if (dap_chain_node_cli_find_option_val(a_argv, 1, 2, "list", NULL)) + l_cmd = CMD_LIST; + else if (dap_chain_node_cli_find_option_val(a_argv, 1, 2, "info", NULL)) + l_cmd = CMD_INFO; + else if (dap_chain_node_cli_find_option_val(a_argv, 1, 2, "tx", NULL)) + l_cmd = CMD_TX; + // token list + if(l_cmd == CMD_LIST) { + dap_string_t *l_str_out = dap_string_new(NULL); + size_t l_token_num_total = 0; + // get first chain + void *l_chain_tmp = (void*)0x1; + dap_chain_t *l_chain_cur = dap_chain_enum(&l_chain_tmp); + while(l_chain_cur) { + // only selected net + if(l_net->pub.id.uint64 == l_chain_cur->net_id.uint64) { + size_t l_token_num = 0; + char *token_list_str = dap_db_history_token_list(l_chain_cur, NULL, l_hash_out_type, &l_token_num); + if(token_list_str) + dap_string_append(l_str_out, token_list_str); + l_token_num_total += l_token_num; + } + // next chain + dap_chain_enum_unlock(); + l_chain_cur = dap_chain_enum(&l_chain_tmp); + } + dap_chain_enum_unlock(); + //total + dap_string_append_printf(l_str_out, "---------------\ntokens: %u\n", l_token_num_total); + dap_chain_node_cli_set_reply_text(a_str_reply, l_str_out->str); + dap_string_free(l_str_out, true); + return 0; + + } + // token info + if(l_cmd == CMD_INFO) { + const char *l_token_name_str = NULL; + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-name", &l_token_name_str); + if(!l_token_name_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "command requires parameter '-name' <token name>"); + return -4; + } + + dap_string_t *l_str_out = dap_string_new(NULL); + size_t l_token_num_total = 0; + // get first chain + void *l_chain_tmp = (void*)0x1; + dap_chain_t *l_chain_cur = dap_chain_enum(&l_chain_tmp); + while(l_chain_cur) { + // only selected net + if(l_net->pub.id.uint64 == l_chain_cur->net_id.uint64) { + size_t l_token_num = 0; + // filter - token name + char *token_list_str = dap_db_history_token_list(l_chain_cur, l_token_name_str, l_hash_out_type, &l_token_num); + if(token_list_str) + dap_string_append(l_str_out, token_list_str); + l_token_num_total += l_token_num; + } + // next chain + dap_chain_enum_unlock(); + l_chain_cur = dap_chain_enum(&l_chain_tmp); + } + dap_chain_enum_unlock(); + if(!l_token_num_total) + dap_string_append_printf(l_str_out, "token '%s' not found\n", l_token_name_str); + dap_chain_node_cli_set_reply_text(a_str_reply, l_str_out->str); + dap_string_free(l_str_out, true); + return 0; + + } + // command tx history + if(l_cmd == CMD_TX) { + + const char *l_token_name_str = NULL; + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-name", &l_token_name_str); + if(!l_token_name_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "command requires parameter '-name' <token name>"); + return -4; + } + + dap_string_t *l_str_out = dap_string_new(NULL); + // get first chain + void *l_chain_tmp = (void*)0x1; + dap_chain_t *l_chain_cur = dap_chain_enum(&l_chain_tmp); + while(l_chain_cur) { + // only selected net + if(l_net->pub.id.uint64 == l_chain_cur->net_id.uint64) { + char *token_list_str = dap_db_history_filter(l_chain_cur, l_token_name_str, l_hash_out_type); + if(token_list_str){ + dap_string_append(l_str_out, "%s\n", token_list_str); + dap_string_append(l_str_out, token_list_str); + } + // next chain + dap_chain_enum_unlock(); + l_chain_cur = dap_chain_enum(&l_chain_tmp); + } + dap_chain_enum_unlock(); + dap_chain_node_cli_set_reply_text(a_str_reply, l_str_out->str); + dap_string_free(l_str_out, true); + return 0; + + +/* bool l_is_all = dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-all", NULL); + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-addr", &l_addr_base58); + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-w", &l_wallet_name); + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-net", &l_net_str); + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-chain", &l_chain_str); + dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-tx", &l_tx_hash_str); + + if(!l_is_all && !l_addr_base58 && !l_wallet_name && !l_tx_hash_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "command requires parameter '-all' or '-addr' or '-w'"); + return -1; + } + + // Select chain network + if(!l_net_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "command requires parameter '-net'"); + return -2; + } else { + if((l_net = dap_chain_net_by_name(l_net_str)) == NULL) { // Can't find such network + dap_chain_node_cli_set_reply_text(a_str_reply, + "tx_history requires parameter '-net' to be valid chain network name"); + return -3; + } + } + //Select chain emission + if(!l_chain_str) { // chain may be null -> then all chain use + //dap_chain_node_cli_set_reply_text(a_str_reply, "command requires parameter '-chain'"); + //return -4; + } else { + if((l_chain = dap_chain_net_get_chain_by_name(l_net, l_chain_str)) == NULL) { // Can't find such chain + dap_chain_node_cli_set_reply_text(a_str_reply, + "command requires parameter '-chain' to be valid chain name in chain net %s", + l_net_str); + return -5; + } + } + */ + } + return 0; +} + + diff --git a/modules/net/include/dap_chain_node_cli_cmd_tx.h b/modules/net/include/dap_chain_node_cli_cmd_tx.h index 64fb1fbc0c..a7a1a4a317 100644 --- a/modules/net/include/dap_chain_node_cli_cmd_tx.h +++ b/modules/net/include/dap_chain_node_cli_cmd_tx.h @@ -31,3 +31,16 @@ */ char* dap_db_history_tx(dap_chain_hash_fast_t* a_tx_hash, dap_chain_t * a_chain, const char *a_hash_out_type); char* dap_db_history_addr(dap_chain_addr_t * a_addr, dap_chain_t * a_chain, const char *a_hash_out_type); + +/** + * ledger command + * + */ +int com_ledger(int a_argc, char ** a_argv, void *a_arg_func, char **a_str_reply); + +/** + * token command + * + */ +int com_token(int a_argc, char ** a_argv, void *a_arg_func, char **a_str_reply); + -- GitLab