From 27a39e0118f8ffd90048872f28d85d5893e3cf58 Mon Sep 17 00:00:00 2001
From: "alexey.stratulat" <alexey.stratulat@demlabs.net>
Date: Wed, 5 Apr 2023 14:22:26 +0000
Subject: [PATCH] Features 8374

---
 modules/net/dap_chain_node_cli_cmd.c | 215 ++++++++++++++++++++-------
 1 file changed, 160 insertions(+), 55 deletions(-)

diff --git a/modules/net/dap_chain_node_cli_cmd.c b/modules/net/dap_chain_node_cli_cmd.c
index 908352deaf..17a0469ec5 100644
--- a/modules/net/dap_chain_node_cli_cmd.c
+++ b/modules/net/dap_chain_node_cli_cmd.c
@@ -2442,6 +2442,102 @@ const char *s_ticker_list_get_main_ticker(dap_list_t *a_tickers, const char *a_n
     return mt;
 }
 
+/**
+ * @breif s_tickers_list_created
+ *
+ * @param a_tx
+ * @param a_net
+ * @param a_unchained
+ * @param a_token_ticker
+ * @return dap_list_t*
+ */
+dap_list_t *s_tickers_list_created(dap_chain_datum_tx_t *a_tx, dap_chain_net_t *a_net, bool *a_unchained,
+                                  const char **a_token_ticker) {
+    dap_list_t* l_tickers = NULL;
+    const char *l_token_ticker = NULL;
+    dap_chain_datum_tx_t *l_tx_parent = NULL;
+    int l_item_in_size = 0;
+    void *l_item_in = dap_chain_datum_tx_item_get(a_tx, NULL, TX_ITEM_TYPE_IN_ALL, &l_item_in_size);
+    dap_hash_fast_t l_parent_hash = {0};
+    int l_parrent_tx_out_idx;
+    for (int l_item_in_size_current = 0; l_item_in_size_current < l_item_in_size && !l_token_ticker;) {
+        size_t l_tmp_size = dap_chain_datum_item_tx_get_size(l_item_in);
+        if (l_tmp_size == 0)
+            break;
+        l_item_in_size_current += l_tmp_size;
+        switch (dap_chain_datum_tx_item_get_type(l_item_in)) {
+            case TX_ITEM_TYPE_IN:
+                l_parent_hash = ((dap_chain_tx_in_t*)l_item_in)->header.tx_prev_hash;
+                l_parrent_tx_out_idx = ((dap_chain_tx_in_t*)l_item_in)->header.tx_out_prev_idx;
+                l_tx_parent = dap_chain_ledger_tx_find_by_hash(a_net->pub.ledger, &((dap_chain_tx_in_t*)l_item_in)->header.tx_prev_hash);
+                break;
+            case TX_ITEM_TYPE_IN_COND:
+                l_parent_hash = ((dap_chain_tx_in_cond_t*)l_item_in)->header.tx_prev_hash;
+                l_parrent_tx_out_idx = ((dap_chain_tx_in_cond_t*)l_item_in)->header.tx_out_prev_idx;
+                l_tx_parent = dap_chain_ledger_tx_find_by_hash(a_net->pub.ledger, &((dap_chain_tx_in_cond_t*)l_item_in)->header.tx_prev_hash);
+                break;
+        }
+        if (!l_tx_parent) {
+            *a_unchained = true;
+            break;
+        }
+        const char *l_current_token = NULL;
+        void *l_out_unknown = (dap_chain_tx_out_cond_t*)dap_chain_datum_tx_item_get_nth(
+                l_tx_parent, TX_ITEM_TYPE_OUT_ALL, l_parrent_tx_out_idx);
+        switch(dap_chain_datum_tx_item_get_type(l_out_unknown)) {
+            case TX_ITEM_TYPE_OUT:
+                l_current_token = dap_chain_ledger_tx_get_token_ticker_by_hash(a_net->pub.ledger, &l_parent_hash);
+                l_tickers = dap_list_append(l_tickers, (void *)l_current_token);
+                break;
+            case TX_ITEM_TYPE_OUT_EXT:
+                l_current_token = ((dap_chain_tx_out_ext_t*)l_out_unknown)->token;
+                l_tickers = dap_list_append(l_tickers, (void *)l_current_token);
+                break;
+            case TX_ITEM_TYPE_OUT_COND:
+                if(((dap_chain_tx_out_cond_t*)l_out_unknown)->header.subtype != DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE) {
+                    l_token_ticker = dap_chain_ledger_tx_get_token_ticker_by_hash(a_net->pub.ledger, &l_parent_hash);
+                }
+                break;
+        }
+    }
+    if (!(*a_unchained) && !l_token_ticker) {
+        return l_tickers;
+    } else {
+        DAP_DELETE(l_tickers);
+        *a_token_ticker = l_token_ticker;
+        return NULL;
+    }
+}
+
+/**
+ * @breif s_tx_get_main_ticker
+ *
+ * @param a_tx
+ * @param a_net
+ * @param a_unchained
+ * @return const char*
+ */
+const char* s_tx_get_main_ticker(dap_chain_datum_tx_t *a_tx, dap_chain_net_t *a_net, bool *a_unchained) {
+    dap_chain_tx_in_ems_t *obj_token = (dap_chain_tx_in_ems_t*)dap_chain_datum_tx_item_get(a_tx, NULL, TX_ITEM_TYPE_IN_EMS, NULL);
+    if (obj_token) {
+        return obj_token->header.ticker;
+    } else {
+        const char *l_token_ticker = NULL;
+        dap_list_t *l_tickers_list = NULL;
+        if (a_unchained) {
+            l_tickers_list = s_tickers_list_created(a_tx, a_net, a_unchained, &l_token_ticker);
+        } else {
+            bool l_unchained = false;
+            l_tickers_list = s_tickers_list_created(a_tx, a_net, &l_unchained, &l_token_ticker);
+        }
+        if (l_tickers_list) {
+            l_token_ticker = s_ticker_list_get_main_ticker(l_tickers_list, a_net->pub.native_ticker);
+            DAP_DELETE(l_tickers_list);
+        }
+        return l_token_ticker;
+    }
+}
+
 /**
  * @brief s_com_mempool_list_print_for_chain
  *
@@ -2541,55 +2637,7 @@ void s_com_mempool_list_print_for_chain(dap_chain_net_t * a_net, dap_chain_t * a
                     l_token_ticker = obj_token->header.ticker;
                 } else {
                     if (!a_fast) {
-                        dap_list_t* l_tickers = NULL;
-                        dap_chain_datum_tx_t *l_tx_parent = NULL;
-                        int l_item_in_size = 0;
-                        void *l_item_in = dap_chain_datum_tx_item_get((dap_chain_datum_tx_t*)l_datum->data, NULL, TX_ITEM_TYPE_IN_ALL, &l_item_in_size);
-                        dap_hash_fast_t l_parent_hash = {0};
-                        int l_parrent_tx_out_idx;
-                        for (int l_item_in_size_current = 0; l_item_in_size_current < l_item_in_size && !l_token_ticker;) {
-                            size_t l_tmp_size = dap_chain_datum_item_tx_get_size(l_item_in);
-                            if (l_tmp_size == 0)
-                                break;
-                            l_item_in_size_current += l_tmp_size;
-                            switch (dap_chain_datum_tx_item_get_type(l_item_in)) {
-                                case TX_ITEM_TYPE_IN:
-                                    l_parent_hash = ((dap_chain_tx_in_t*)l_item_in)->header.tx_prev_hash;
-                                    l_parrent_tx_out_idx = ((dap_chain_tx_in_t*)l_item_in)->header.tx_out_prev_idx;
-                                    l_tx_parent = dap_chain_ledger_tx_find_by_hash(a_net->pub.ledger, &((dap_chain_tx_in_t*)l_item_in)->header.tx_prev_hash);
-                                    break;
-                                case TX_ITEM_TYPE_IN_COND:
-                                    l_parent_hash = ((dap_chain_tx_in_cond_t*)l_item_in)->header.tx_prev_hash;
-                                    l_parrent_tx_out_idx = ((dap_chain_tx_in_cond_t*)l_item_in)->header.tx_out_prev_idx;
-                                    l_tx_parent = dap_chain_ledger_tx_find_by_hash(a_net->pub.ledger, &((dap_chain_tx_in_cond_t*)l_item_in)->header.tx_prev_hash);
-                                    break;
-                            }
-                            if (!l_tx_parent) {
-                                l_is_unchained = true;
-                                break;
-                            }
-                            const char *l_current_token = NULL;
-                            void *l_out_unknown = (dap_chain_tx_out_cond_t*)dap_chain_datum_tx_item_get_nth(
-                                    l_tx_parent, TX_ITEM_TYPE_OUT_ALL, l_parrent_tx_out_idx);
-                            switch(dap_chain_datum_tx_item_get_type(l_out_unknown)) {
-                                case TX_ITEM_TYPE_OUT:
-                                    l_current_token = dap_chain_ledger_tx_get_token_ticker_by_hash(a_net->pub.ledger, &l_parent_hash);
-                                    l_tickers = dap_list_append(l_tickers, (void *)l_current_token);
-                                    break;
-                                case TX_ITEM_TYPE_OUT_EXT:
-                                    l_current_token = ((dap_chain_tx_out_ext_t*)l_out_unknown)->token;
-                                    l_tickers = dap_list_append(l_tickers, (void *)l_current_token);
-                                    break;
-                                case TX_ITEM_TYPE_OUT_COND:
-                                    if(((dap_chain_tx_out_cond_t*)l_out_unknown)->header.subtype != DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE) {
-                                        l_token_ticker = dap_chain_ledger_tx_get_token_ticker_by_hash(a_net->pub.ledger, &l_parent_hash);
-                                    }
-                                    break;
-                            }
-                        }
-                        if (!l_is_unchained && !l_token_ticker)
-                            l_token_ticker = s_ticker_list_get_main_ticker(l_tickers, a_net->pub.native_ticker);
-                        dap_list_free(l_tickers);
+                        l_token_ticker = s_tx_get_main_ticker((dap_chain_datum_tx_t*)l_datum->data, a_net, &l_is_unchained);
                     }
                 }
             }
@@ -5203,12 +5251,14 @@ int com_tx_history(int a_argc, char ** a_argv, char **a_str_reply)
     dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-chain", &l_chain_str);
     dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-tx", &l_tx_hash_str);
 
-    if (!l_addr_base58 && !l_wallet_name && !l_tx_hash_str) {
+    bool l_is_tx_all = dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-all", NULL);
+
+    if (!l_addr_base58 && !l_wallet_name && !l_tx_hash_str && !l_is_tx_all) {
         dap_cli_server_cmd_set_reply_text(a_str_reply, "tx_history requires parameter '-addr' or '-w' or '-tx'");
         return -1;
     }
 
-    if (!l_net_str && !l_addr_base58) {
+    if (!l_net_str && !l_addr_base58&& !l_is_tx_all) {
         dap_cli_server_cmd_set_reply_text(a_str_reply, "tx_history requires parameter '-net' or '-addr'");
         return -2;
     }
@@ -5280,9 +5330,64 @@ int com_tx_history(int a_argc, char ** a_argv, char **a_str_reply)
                                           l_net_str);
         return -8;
     }
-    char *l_str_out = l_tx_hash_str ?
-                                      dap_db_history_tx(&l_tx_hash, l_chain, l_hash_out_type) :
-                                      dap_db_history_addr(l_addr, l_chain, l_hash_out_type);
+    char *l_str_out = NULL;
+    if (l_tx_hash_str) {
+        l_str_out = dap_db_history_tx(&l_tx_hash, l_chain, l_hash_out_type);
+    } else if (l_addr) {
+        l_str_out = dap_db_history_addr(l_addr, l_chain, l_hash_out_type);
+    } else if (l_is_tx_all) {
+        dap_time_t l_time_T = dap_time_now();
+        char out[80] = {"\0"};
+        dap_time_to_str_rfc822(out,80, l_time_T);
+        log_it(L_DEBUG, "Start getting tx from chain: %s", out);
+        dap_ledger_t *l_ledger = l_net->pub.ledger;
+        dap_string_t *l_tx_all_str = dap_string_new("");
+        size_t l_tx_count = 0;
+        size_t l_tx_ledger_accepted = 0;
+        size_t l_tx_ledger_rejected = 0;
+        dap_chain_cell_t    *l_cell = NULL,
+                            *l_cell_tmp = NULL;
+        dap_chain_atom_iter_t *l_iter = NULL;
+        HASH_ITER(hh, l_chain->cells, l_cell, l_cell_tmp) {
+            l_iter = l_chain->callback_atom_iter_create(l_chain, l_cell->id, 0);
+            size_t l_atom_size = 0;
+            dap_chain_atom_ptr_t l_ptr = l_chain->callback_atom_iter_get_first(l_iter, &l_atom_size);
+            while (l_ptr && l_atom_size) {
+                size_t l_datums_count = 0;
+                dap_chain_datum_t **l_datums = l_cell->chain->callback_atom_get_datums(l_ptr, l_atom_size, &l_datums_count);
+                for (size_t i = 0; i < l_datums_count; i++) {
+                    if (l_datums[i]->header.type_id == DAP_CHAIN_DATUM_TX) {
+                        l_tx_count++;
+                        dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*)l_datums[i]->data;
+                        dap_hash_fast_t l_ttx_hash = {0};
+                        dap_hash_fast(l_tx, l_datums[i]->header.data_size, &l_ttx_hash);
+                        const char *l_token_ticker = NULL;
+                        if ((l_token_ticker = dap_chain_ledger_tx_get_token_ticker_by_hash(l_ledger, &l_ttx_hash))) {
+                            dap_string_append_printf(l_tx_all_str, "\t\t↓↓↓ Ledger accepted ↓↓↓\n");
+                            l_tx_ledger_accepted++;
+                        } else {
+                            l_token_ticker = s_tx_get_main_ticker(l_tx, l_net, NULL);
+                            dap_string_append_printf(l_tx_all_str, "\t\t↓↓↓ Ledger rejected ↓↓↓\n");
+                            l_tx_ledger_rejected++;
+                        }
+                        dap_chain_datum_dump_tx(l_tx, l_token_ticker, l_tx_all_str, l_hash_out_type, &l_ttx_hash);
+                    }
+                }
+                DAP_DEL_Z(l_datums);
+                l_ptr = l_chain->callback_atom_iter_get_next(l_iter, &l_atom_size);
+            }
+            l_cell->chain->callback_atom_iter_delete(l_iter);
+        }
+        l_time_T = dap_time_now();
+        dap_time_to_str_rfc822(out,80, l_time_T);
+        log_it(L_DEBUG, "END getting tx from chain: %s", out);
+        dap_string_append_printf(l_tx_all_str, "Chain %s in network %s contains %zu transactions.\n"
+                                               "Of which %zu were accepted into the ledger and %zu were rejected.\n",
+                                 l_net->pub.name, l_chain->name, l_tx_count, l_tx_ledger_accepted, l_tx_ledger_rejected);
+
+        l_str_out = l_str_out ? dap_strdup_printf("%s%s", l_str_out, dap_strdup(l_tx_all_str->str)) : dap_strdup(l_tx_all_str->str);
+        dap_string_free(l_tx_all_str, true);
+    }
 
     char *l_str_ret = NULL;
     if(l_tx_hash_str) {
@@ -5294,7 +5399,7 @@ int com_tx_history(int a_argc, char ** a_argv, char **a_str_reply)
         l_str_ret = dap_strdup_printf("History for addr %s:\n%s", l_addr_str,
                 l_str_out ? l_str_out : " empty");
         DAP_DELETE(l_addr_str);
-    }
+    } else if (l_is_tx_all) {l_str_ret = dap_strdup(l_str_out);}
     dap_cli_server_cmd_set_reply_text(a_str_reply, "%s", l_str_ret);
     DAP_DELETE(l_str_out);
     DAP_DELETE(l_str_ret);
-- 
GitLab