diff --git a/dap-sdk/core/include/dap_time.h b/dap-sdk/core/include/dap_time.h index 9554b3118ed12b48d1678e9a9eb0e6ba8d4d08ec..76a739898d160fc5c04c26e3458756ed158c4c33 100644 --- a/dap-sdk/core/include/dap_time.h +++ b/dap-sdk/core/include/dap_time.h @@ -42,7 +42,7 @@ void dap_usleep(dap_time_t a_microseconds); * @return */ char* dap_ctime_r(dap_time_t *a_time, char* a_buf); -char* dap_gdb_ctime_r(dap_nanotime_t *a_time, char* a_buf); +char* dap_nanotime_to_str(dap_nanotime_t *a_time, char* a_buf); int dap_time_to_str_rfc822(char * out, size_t out_size_max, dap_time_t t); diff --git a/dap-sdk/core/src/dap_time.c b/dap-sdk/core/src/dap_time.c index 075e1961da4d760402a4b54c0af824ab201376b0..1b45fc2360c356d6081adac909bfb7d6a899f0c5 100644 --- a/dap-sdk/core/src/dap_time.c +++ b/dap-sdk/core/src/dap_time.c @@ -233,7 +233,7 @@ char* dap_ctime_r(dap_time_t *a_time, char* a_buf) * @param a_buf The minimum buffer size is 26 elements. * @return */ -char* dap_gdb_ctime_r(dap_nanotime_t *a_chain_time, char* a_buf){ +char* dap_nanotime_to_str(dap_nanotime_t *a_chain_time, char* a_buf){ dap_time_t l_time = dap_gdb_time_to_sec(*a_chain_time); return dap_ctime_r(&l_time, a_buf); } diff --git a/modules/common/include/dap_chain_datum_tx.h b/modules/common/include/dap_chain_datum_tx.h index 226fe77eb32b2b09e66e4724b1ed9a4f62969ff6..c4b29bba1aa08363882771b207aaf0499c06f1ac 100644 --- a/modules/common/include/dap_chain_datum_tx.h +++ b/modules/common/include/dap_chain_datum_tx.h @@ -26,6 +26,7 @@ #include "dap_list.h" #include "dap_enc_key.h" #include "dap_chain_common.h" +#include "dap_time.h" typedef enum dap_chain_tx_cond_type { COND_SERVICE_PROVIDE = 0x20, // @@ -39,7 +40,7 @@ typedef enum dap_chain_tx_cond_type { */ typedef struct dap_chain_datum_tx{ struct { - uint64_t ts_created; + dap_time_t ts_created; uint32_t tx_items_size; // size of next sequencly lying tx_item sections would be decided to belong this transaction } DAP_ALIGN_PACKED header; uint8_t tx_items[]; diff --git a/modules/net/dap_chain_net.c b/modules/net/dap_chain_net.c index a387eb04d7c37a2d6c0805427ac730eee6e08f5d..563f88e0df658b3d989c83525c453a3bd362ebbb 100644 --- a/modules/net/dap_chain_net.c +++ b/modules/net/dap_chain_net.c @@ -26,6 +26,10 @@ #ifndef _GNU_SOURCE #define _GNU_SOURCE +#include "dap_chain.h" +#include "dap_chain_datum_tx_out_cond.h" +#include "dap_list.h" +#include "dap_time.h" #endif #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE /* See feature_test_macros(7) */ @@ -3288,6 +3292,131 @@ void dap_chain_net_proc_mempool (dap_chain_net_t * a_net) } } +/** + * @brief dap_chain_net_get_tx_cond_all_by_srv_uid + * @param a_net + * @param a_srv_uid + * @param a_search_type + * @return + */ +dap_list_t * dap_chain_net_get_tx_cond_all_by_srv_uid(dap_chain_net_t * a_net, const dap_chain_net_srv_uid_t a_srv_uid, + const dap_time_t a_time_from, const dap_time_t a_time_to, + const dap_chain_net_tx_search_type_t a_search_type) +{ + dap_ledger_t * l_ledger = a_net->pub.ledger; + dap_list_t * l_ret = NULL; + + switch (a_search_type) { + case TX_SEARCH_TYPE_NET: + case TX_SEARCH_TYPE_CELL: + case TX_SEARCH_TYPE_LOCAL: + case TX_SEARCH_TYPE_CELL_SPENT: + case TX_SEARCH_TYPE_NET_SPENT: { + // pass all chains + for ( dap_chain_t * l_chain = a_net->pub.chains; l_chain; l_chain = l_chain->next){ + dap_chain_cell_t * l_cell, *l_cell_tmp; + // Go through all cells + HASH_ITER(hh,l_chain->cells,l_cell, l_cell_tmp){ + dap_chain_atom_iter_t * l_atom_iter = l_chain->callback_atom_iter_create(l_chain,l_cell->id, false ); + // try to find transaction in chain ( inside shard ) + size_t l_atom_size = 0; + dap_chain_atom_ptr_t l_atom = l_chain->callback_atom_iter_get_first(l_atom_iter, &l_atom_size); + + // Check atoms in chain + while(l_atom && l_atom_size) { + dap_chain_datum_t *l_datum = (dap_chain_datum_t*) l_atom; + // transaction + dap_chain_datum_tx_t *l_tx = NULL; + + // Check if its transaction + if ( l_datum && (l_datum->header.type_id == DAP_CHAIN_DATUM_TX)) { + l_tx = (dap_chain_datum_tx_t*) l_datum->data; + } + + // If found TX + if (l_tx){ + // Check for time from + if(a_time_from && l_tx->header.ts_created < a_time_from) + continue; + + // Check for time to + if(a_time_to && l_tx->header.ts_created > a_time_to) + continue; + + if(a_search_type == TX_SEARCH_TYPE_CELL_SPENT || a_search_type == TX_SEARCH_TYPE_NET_SPENT ){ + dap_hash_fast_t * l_tx_hash = dap_chain_node_datum_tx_calc_hash(l_tx); + bool l_is_spent = dap_chain_ledger_tx_spent_find_by_hash(l_ledger,l_tx_hash); + DAP_DELETE(l_tx_hash); + if(!l_is_spent) + continue; + } + // Check for OUT_COND items + dap_list_t *l_list_out_cond_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT_COND , NULL); + if(l_list_out_cond_items){ + dap_list_t *l_list_cur = l_list_out_cond_items; + while(l_list_cur){ // Go through all cond items + l_list_cur = dap_list_next(l_list_cur); + dap_chain_tx_out_cond_t * l_tx_out_cond = (dap_chain_tx_out_cond_t *)l_list_cur->data; + if(l_tx_out_cond) // If we found cond out with target srv_uid + if(l_tx_out_cond->header.srv_uid.uint64 == a_srv_uid.uint64) + l_ret = dap_list_append(l_ret,l_tx); + } + dap_list_free(l_list_out_cond_items); + } + } + + // go to next atom + l_atom = l_chain->callback_atom_iter_get_next(l_atom_iter, &l_atom_size); + + } + } + } + } break; + + case TX_SEARCH_TYPE_NET_UNSPENT: + case TX_SEARCH_TYPE_CELL_UNSPENT: + l_ret = dap_chain_ledger_tx_cache_find_out_cond_all(l_ledger, a_srv_uid); + break; + } + return l_ret; + +} + +/** + * @brief Summarize all tx inputs + * @param a_net + * @param a_tx + * @return + */ +uint256_t dap_chain_net_get_tx_total_value(dap_chain_net_t * a_net, dap_chain_datum_tx_t * a_tx) +{ + uint256_t l_ret = {0}; + int l_item_idx = 0; + dap_chain_tx_in_t *l_in_item = NULL; + do { + l_in_item = (dap_chain_tx_in_t*) dap_chain_datum_tx_item_get(a_tx, &l_item_idx, TX_ITEM_TYPE_IN , NULL); + l_item_idx++; + if(l_in_item ) { + //const char *token = l_out_cond_item->subtype.srv_xchange.token; + dap_chain_datum_tx_t * l_tx_prev = dap_chain_net_get_tx_by_hash(a_net,&l_in_item->header.tx_prev_hash, TX_SEARCH_TYPE_NET_SPENT); + if(l_tx_prev){ + int l_tx_prev_out_index = l_in_item->header.tx_out_prev_idx; + dap_chain_tx_out_t * l_tx_prev_out =(dap_chain_tx_out_t *) + dap_chain_datum_tx_item_get(l_tx_prev,&l_tx_prev_out_index, TX_ITEM_TYPE_OUT,NULL); + if( l_tx_prev_out_index == l_in_item->header.tx_out_prev_idx && l_tx_prev_out){ + uint256_t l_in_value = l_tx_prev_out->header.value; + if(SUM_256_256(l_in_value,l_ret, &l_ret )!= 0) + log_it(L_ERROR, "Overflow on inputs values calculation (summing)"); + }else{ + log_it(L_WARNING, "Can't find item with index %d in prev tx hash", l_tx_prev_out_index); + } + }else + log_it(L_WARNING, "Can't find prev tx hash"); + } + } while(l_in_item); + return l_ret; +} + /** * @brief dap_chain_net_tx_get_by_hash * @param a_net diff --git a/modules/net/include/dap_chain_net.h b/modules/net/include/dap_chain_net.h index 96a05c6e9ec051a01ab40ee5eef8619778942638..dd277fab5cdbabf2037f4fb17c69db0ac8d76337 100644 --- a/modules/net/include/dap_chain_net.h +++ b/modules/net/include/dap_chain_net.h @@ -27,6 +27,8 @@ along with any CellFrame SDK based project. If not, see <http://www.gnu.org/lic #include <stdint.h> #include <string.h> +#include "dap_chain_datum_tx.h" +#include "dap_math_ops.h" #include "dap_net.h" #include "dap_stream_ch.h" #include "dap_strfuncs.h" @@ -157,6 +159,13 @@ typedef enum dap_chain_net_tx_search_type { dap_chain_datum_tx_t * dap_chain_net_get_tx_by_hash(dap_chain_net_t * a_net, dap_chain_hash_fast_t * a_tx_hash, dap_chain_net_tx_search_type_t a_search_type); +uint256_t dap_chain_net_get_tx_total_value(dap_chain_net_t * a_net, dap_chain_datum_tx_t * a_tx); + +dap_list_t * dap_chain_net_get_tx_cond_all_by_srv_uid(dap_chain_net_t * a_net, const dap_chain_net_srv_uid_t a_srv_uid, + const dap_time_t a_time_from, const dap_time_t a_time_to, + const dap_chain_net_tx_search_type_t a_search_type); + + dap_chain_node_role_t dap_chain_net_get_role(dap_chain_net_t * a_net); /** diff --git a/modules/service/xchange/dap_chain_net_srv_xchange.c b/modules/service/xchange/dap_chain_net_srv_xchange.c index 45bc1a6653a46d37aa835a5929ea1f79fb9ef79e..673d9a0ce6b83e0fda462731baa3c13dccc91e80 100644 --- a/modules/service/xchange/dap_chain_net_srv_xchange.c +++ b/modules/service/xchange/dap_chain_net_srv_xchange.c @@ -23,17 +23,29 @@ */ #include <math.h> +#include <stdint.h> #include "dap_chain_datum_token.h" +#include "dap_chain_datum_tx.h" +#include "dap_chain_datum_tx_in.h" #include "dap_chain_ledger.h" +#include "dap_chain_net.h" #include "dap_chain_node_cli.h" +#include "dap_hash.h" +#include "dap_list.h" +#include "dap_math_ops.h" +#include "dap_strfuncs.h" #include "dap_string.h" #include "dap_chain_common.h" #include "dap_chain_mempool.h" #include "dap_chain_net_srv.h" #include "dap_chain_net_srv_xchange.h" +#include "dap_time.h" #define LOG_TAG "dap_chain_net_srv_xchange" +const dap_chain_net_srv_uid_t c_dap_chain_net_srv_xchange_uid = {.uint64= DAP_CHAIN_NET_SRV_XCHANGE_ID}; + + static int s_cli_srv_xchange(int a_argc, char **a_argv, char **a_str_reply); static int s_callback_requested(dap_chain_net_srv_t *a_srv, uint32_t a_usage_id, dap_chain_net_srv_client_remote_t *a_srv_client, const void *a_data, size_t a_data_size); static int s_callback_response_success(dap_chain_net_srv_t *a_srv, uint32_t a_usage_id, dap_chain_net_srv_client_remote_t *a_srv_client, const void *a_data, size_t a_data_size); @@ -68,10 +80,12 @@ int dap_chain_net_srv_xchange_init() "\tList of exchange transactions\n" "srv_xchange token_pair -net <net name> list all\n" "\tList of all token pairs\n" - "srv_xchange token_pair -net <net name> price average -token1 <token 1> -token2 <token 2> [-interval <interval length>] [-time <Moment to calc>] \n" - "\tGet average price for token pair <token 1>:<token 2> from time -time with interval length -interval \n" + "srv_xchange token_pair -net <net name> price average -token1 <token 1> -token2 <token 2> [-time_from <From time>] [-time_to <To time>] \n" + "\tGet average price for token pair <token 1>:<token 2> from <From time> to <To time> \n" + "\tAll times are in RFC822" "srv_xchange token_pair -net <net name> price history -token1 <token 1> -token2 <token 2> [-time_from <From time>] [-time_to <To time>] \n" - "\tPrint price history for token pair <token 1>:<token 2> from time -time with interval length -interval \n" + "\tPrint price history for token pair <token 1>:<token 2> from <From time> to <To time>\n" + "\tAll times are in RFC822" "srv_xchange enable\n" "\tEnable eXchange service\n" "srv_xchange disable\n" @@ -862,6 +876,7 @@ static bool s_filter_tx_list(dap_chain_datum_t *a_datum, dap_chain_t *a_chain, v return false; } + static int s_cli_srv_xchange(int a_argc, char **a_argv, char **a_str_reply) { enum { @@ -1044,17 +1059,134 @@ static int s_cli_srv_xchange(int a_argc, char **a_argv, char **a_str_reply) return -6; } + // Read time_from + dap_time_t l_time_from = 0; + const char * l_time_from_str = NULL; + dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-time_from", &l_time_from_str); + l_time_from = dap_time_from_str_rfc822(l_time_from_str); + + // Read time_to + dap_time_t l_time_to = 0; + const char * l_time_to_str = NULL; + dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-time_to", &l_time_to_str); + l_time_to = dap_time_from_str_rfc822(l_time_to_str); + + // Check for price subcommand if (strcmp(l_price_subcommand,"average") == 0){ + dap_string_t *l_reply_str = dap_string_new(""); + + dap_list_t *l_tx_cond_list = dap_chain_net_get_tx_cond_all_by_srv_uid(l_net, c_dap_chain_net_srv_xchange_uid, + l_time_from,l_time_to,TX_SEARCH_TYPE_NET ); + dap_list_t * l_cur = l_tx_cond_list; + uint256_t l_total_rates = {0}; + uint256_t l_total_rates_count = {0}; + while(l_cur){ + dap_chain_datum_tx_t * l_tx =(dap_chain_datum_tx_t *) l_cur->data; + if(l_tx){ + dap_hash_fast_t * l_tx_hash = dap_chain_node_datum_tx_calc_hash(l_tx); + + // Get input token ticker + const char * l_tx_input_ticker = dap_chain_ledger_tx_get_token_ticker_by_hash( + l_net->pub.ledger, l_tx_hash); + + // Compare with token1 and token2 + if( dap_strcmp(l_tx_input_ticker, l_token1) != 0 && + dap_strcmp(l_tx_input_ticker, l_token2) != 0) + continue; + + dap_chain_tx_out_cond_t *l_out_cond_item = NULL; + int l_item_idx = 0; + do { + l_out_cond_item = (dap_chain_tx_out_cond_t*) dap_chain_datum_tx_item_get(l_tx, &l_item_idx, TX_ITEM_TYPE_OUT_COND, NULL); + l_item_idx++; + if(l_out_cond_item && l_out_cond_item->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE) { + //const char *token = l_out_cond_item->subtype.srv_xchange.token; + uint256_t value = l_out_cond_item->subtype.srv_xchange.value; + uint256_t l_total_rates_old = l_total_rates; + if(SUM_256_256(l_out_cond_item->subtype.srv_xchange.value,l_total_rates_old, &l_total_rates )!= 0) + log_it(L_ERROR, "Overflow on avarage price calculation (summing)"); + INCR_256(&l_total_rates_count); + } + } + while(l_out_cond_item); + + } + l_cur = dap_list_next(l_cur); + } + dap_list_free(l_tx_cond_list); + uint256_t l_rate_average = {0}; + DIV_256(l_total_rates,l_total_rates_count,&l_rate_average); + char *l_rate_average_str = dap_cvt_uint256_to_str(l_rate_average); + dap_string_append_printf(l_reply_str,"Average rate: %s",l_rate_average_str); + DAP_DELETE(l_rate_average_str); + *a_str_reply = dap_string_free(l_reply_str, false); + break; }else if (strcmp(l_price_subcommand,"history") == 0){ + dap_string_t *l_reply_str = dap_string_new(""); + dap_list_t *l_tx_cond_list = dap_chain_net_get_tx_cond_all_by_srv_uid(l_net, c_dap_chain_net_srv_xchange_uid, + l_time_from,l_time_to,TX_SEARCH_TYPE_NET_SPENT ); + dap_list_t * l_cur = l_tx_cond_list; + while(l_cur){ + dap_chain_datum_tx_t * l_tx =(dap_chain_datum_tx_t *) l_cur->data; + if(l_tx){ + dap_hash_fast_t * l_tx_hash = dap_chain_node_datum_tx_calc_hash(l_tx); + + // Get input token ticker + const char * l_tx_input_ticker = dap_chain_ledger_tx_get_token_ticker_by_hash( + l_net->pub.ledger, l_tx_hash); + + // Compare with token1 and token2 + if( dap_strcmp(l_tx_input_ticker, l_token1) != 0 && + dap_strcmp(l_tx_input_ticker, l_token2) != 0) + continue; + + char * l_tx_hash_str = dap_chain_hash_fast_to_str_new(l_tx_hash);\ + + char l_tx_ts_created_str[72]; + l_tx_ts_created_str[0] = '\0'; + dap_time_to_str_rfc822(l_tx_ts_created_str,sizeof(l_tx_ts_created_str),l_tx->header.ts_created); + dap_string_append_printf(l_reply_str,"Tx hash: %s\n", l_tx_hash_str); + dap_string_append_printf(l_reply_str,"\tts_created: %s\n", l_tx_ts_created_str); + DAP_DEL_Z(l_tx_hash); + DAP_DEL_Z(l_tx_hash_str); + + // Calc inputs + uint256_t l_tx_input_values = dap_chain_net_get_tx_total_value(l_net, l_tx); + + // Find output + int l_item_idx = 0; + dap_chain_tx_out_cond_t *l_out_cond_item = NULL; + do { + l_out_cond_item = (dap_chain_tx_out_cond_t*) dap_chain_datum_tx_item_get(l_tx, &l_item_idx, TX_ITEM_TYPE_OUT_COND, NULL); + l_item_idx++; + if(l_out_cond_item && l_out_cond_item->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE) { + //const char *token = l_out_cond_item->subtype.srv_xchange.token; + uint256_t l_value_to = l_out_cond_item->subtype.srv_xchange.value; + char * l_value_from_str = dap_cvt_uint256_to_str(l_tx_input_values); + char * l_value_to_str = dap_cvt_uint256_to_str(l_value_to); + dap_string_append_printf(l_reply_str, + "\tFrom %s %s To %s %s\n",l_tx_input_ticker,l_value_from_str, + l_value_to_str,l_out_cond_item->subtype.srv_xchange.token ); + DAP_DELETE(l_value_from_str); + DAP_DELETE(l_value_to_str); + } + } + while(l_out_cond_item); + } + // Delimiter between tx + dap_string_append_printf(l_reply_str,"\n\n"); - dap_string_t *l_reply_str = dap_string_new(""); + l_cur = dap_list_next(l_cur); + } - // Find SRV_XCHANGE out_cond item - dap_chain_tx_out_cond_t *l_out_cond_item = NULL; + dap_list_free(l_tx_cond_list); + + *a_str_reply = dap_string_free(l_reply_str, false); + break; } } diff --git a/modules/service/xchange/include/dap_chain_net_srv_xchange.h b/modules/service/xchange/include/dap_chain_net_srv_xchange.h index 216f1ca0edfebb895a07ec57b0b032f1c4c924b8..2c2348e12369bf8341b99f3597afdd1bf14683d7 100644 --- a/modules/service/xchange/include/dap_chain_net_srv_xchange.h +++ b/modules/service/xchange/include/dap_chain_net_srv_xchange.h @@ -71,6 +71,8 @@ typedef struct dap_chain_net_srv_xchange { bool enabled; } dap_chain_net_srv_xchange_t; +extern const dap_chain_net_srv_uid_t c_dap_chain_net_srv_xchange_uid; + int dap_chain_net_srv_xchange_init(); void dap_chain_net_srv_xchange_deinit(); bool dap_chain_net_srv_xchange_verificator(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx, bool a_owner);