From 4e32c18ce2a41b9417f6030a1bc184e351fee4b8 Mon Sep 17 00:00:00 2001
From: "ruslan.laishev" <ruslan.laishev@demlabs.net>
Date: Thu, 31 Mar 2022 04:01:29 +0000
Subject: [PATCH] [+]feature-5899: ledger tx list -all -net ... -tx_num <NR>

---
 dap-sdk/core/include/dap_list.h         |   9 +-
 dap-sdk/net/client/dap_client_pvt.c     |   2 +-
 dap-sdk/net/core/dap_proc_thread.c      |   3 +
 dap-sdk/net/core/dap_server.c           |   6 +-
 dap-sdk/net/core/dap_worker.c           |  11 +-
 modules/chain/include/dap_chain.h       |   2 +
 modules/mempool/dap_chain_mempool.c     |   2 +
 modules/net/dap_chain_node_cli.c        |   2 +-
 modules/net/dap_chain_node_cli_cmd_tx.c | 275 +++++++++++++++++++++++-
 modules/type/dag/dap_chain_cs_dag.c     |  50 +++++
 10 files changed, 349 insertions(+), 13 deletions(-)

diff --git a/dap-sdk/core/include/dap_list.h b/dap-sdk/core/include/dap_list.h
index 1bf990e7fe..3b82deca77 100755
--- a/dap-sdk/core/include/dap_list.h
+++ b/dap-sdk/core/include/dap_list.h
@@ -17,14 +17,13 @@ typedef void* (*dap_callback_copy_t)(const void * src, void* data);
 typedef int (*dap_callback_compare_t)(const void * a, const void * b);
 typedef int (*dap_callback_compare_data_t)(const void * a, const void * b, void* user_data);
 
-typedef struct _dap_list dap_list_t;
 
-struct _dap_list
+typedef struct _dap_list
 {
     void* data;
-    dap_list_t *next;
-    dap_list_t *prev;
-};
+    struct _dap_list *next;
+    struct _dap_list *prev;
+} dap_list_t;
 
 /* Doubly linked lists
  */
diff --git a/dap-sdk/net/client/dap_client_pvt.c b/dap-sdk/net/client/dap_client_pvt.c
index dc92421fe3..8a8e27a381 100644
--- a/dap-sdk/net/client/dap_client_pvt.c
+++ b/dap-sdk/net/client/dap_client_pvt.c
@@ -537,7 +537,7 @@ static bool s_stage_status_after(dap_client_pvt_t * a_client_pvt)
                                 strerror_r(l_err,l_errbuf,sizeof (l_errbuf));
                             else
                                 strncpy(l_errbuf,"Unknown Error",sizeof(l_errbuf)-1);
-                            log_it(L_ERROR, "Remote address can't connect (%s:%hu) with sock_id %"DAP_FORMAT_SOCKET": \"%s\" (code %d)", a_client_pvt->uplink_addr,
+                            log_it(L_ERROR, "Remote address can't connect (%s:%hu) with sd %"DAP_FORMAT_SOCKET": \"%s\" (code %d)", a_client_pvt->uplink_addr,
                                     a_client_pvt->uplink_port, a_client_pvt->stream_es->socket, l_errbuf, l_err);
 #ifdef DAP_OS_WINDOWS
                             closesocket(a_client_pvt->stream_socket);
diff --git a/dap-sdk/net/core/dap_proc_thread.c b/dap-sdk/net/core/dap_proc_thread.c
index a46612b599..7415ee773e 100644
--- a/dap-sdk/net/core/dap_proc_thread.c
+++ b/dap-sdk/net/core/dap_proc_thread.c
@@ -578,6 +578,9 @@ static void * s_proc_thread_function(void * a_arg)
 	
 	l_thread->signal_exit = false;
 	
+
+    log_it(L_NOTICE, "Worker thread is running on CPU #%d... ", l_thread->cpu_id);
+
     // Main loop
     while (!l_thread->signal_kill && !l_thread->signal_exit){
 
diff --git a/dap-sdk/net/core/dap_server.c b/dap-sdk/net/core/dap_server.c
index 792af68632..8976c16ce6 100644
--- a/dap-sdk/net/core/dap_server.c
+++ b/dap-sdk/net/core/dap_server.c
@@ -381,8 +381,8 @@ static void s_es_server_accept(dap_events_socket_t *a_es, SOCKET a_remote_socket
     assert(l_server);
 
     dap_events_socket_t * l_es_new = NULL;
-    log_it(L_DEBUG, "Listening socket (binded on %s:%u) got new incomming connection",l_server->address,l_server->port);
-    log_it(L_DEBUG, "Accepted new connection (sock %"DAP_FORMAT_SOCKET" from %"DAP_FORMAT_SOCKET")", a_remote_socket, a_es->socket);
+    log_it(L_DEBUG, "Listening sd:%"DAP_FORMAT_SOCKET "(binded on %s:%u) got new incomming connection", a_es->socket, l_server->address,l_server->port);
+    log_it(L_DEBUG, "Accepted new connection sd: %"DAP_FORMAT_SOCKET"", a_es->socket);
     l_es_new = s_es_server_create(a_es->events,a_remote_socket,&l_server->client_callbacks,l_server);
     //l_es_new->is_dont_reset_write_flag = true; // By default all income connection has this flag
     getnameinfo(a_remote_addr,a_remote_addr_size, l_es_new->hostaddr
@@ -393,6 +393,8 @@ static void s_es_server_accept(dap_events_socket_t *a_es, SOCKET a_remote_socket
         l_addr_remote.s_addr = ((struct sockaddr_in *) a_remote_addr)->sin_addr.s_addr;
         inet_ntop(AF_INET,&l_addr_remote,l_es_new->hostaddr,sizeof (l_addr_remote) );
     }
+
+    l_es_new->remote_addr_str = dap_strdup (l_es_new->hostaddr);
     log_it(L_INFO,"Connection accepted from %s (%s)", l_es_new->hostaddr, l_es_new->service );
     dap_worker_add_events_socket_auto(l_es_new);
 }
diff --git a/dap-sdk/net/core/dap_worker.c b/dap-sdk/net/core/dap_worker.c
index 4726a1548d..1003ce70b0 100644
--- a/dap-sdk/net/core/dap_worker.c
+++ b/dap-sdk/net/core/dap_worker.c
@@ -169,6 +169,9 @@ void *dap_worker_thread(void *arg)
     pthread_cond_broadcast(&l_worker->started_cond);
     pthread_mutex_unlock(&l_worker->started_mutex);
     bool s_loop_is_active = true;
+
+    log_it(L_NOTICE, "Worker thread %d is running ... ", l_worker->id);
+
     while(s_loop_is_active) {
 	int l_selected_sockets;
 	size_t l_sockets_max;
@@ -222,6 +225,7 @@ void *dap_worker_thread(void *arg)
 
             if (!l_cur_flags) // No events for this socket
                 continue;
+
             l_flag_hup =  l_cur_flags& POLLHUP;
             l_flag_rdhup = l_cur_flags & POLLRDHUP;
             l_flag_write = (l_cur_flags & POLLOUT) || (l_cur_flags &POLLWRNORM)|| (l_cur_flags &POLLWRBAND ) ;
@@ -231,6 +235,7 @@ void *dap_worker_thread(void *arg)
             l_flag_pri = l_cur_flags & POLLPRI;
             l_flag_msg = l_cur_flags & POLLMSG;
             l_cur = l_worker->poll_esocket[n];
+
             //log_it(L_DEBUG, "flags: returned events 0x%0X requested events 0x%0X",l_worker->poll[n].revents,l_worker->poll[n].events );
 #elif defined (DAP_EVENTS_CAPS_KQUEUE)
         l_flag_hup=l_flag_rdhup=l_flag_read=l_flag_write=l_flag_error=l_flag_nval=l_flag_msg =l_flag_pri = false;
@@ -319,12 +324,12 @@ void *dap_worker_thread(void *arg)
                 }
                 default:
                     if(s_debug_reactor)
-                        log_it(L_INFO,"RDHUP event on esocket %p (%"DAP_FORMAT_SOCKET") type %d", l_cur, l_cur->socket, l_cur->type );
+                        log_it(L_INFO,"RDHUP event on esocket %p (sd=%"DAP_FORMAT_SOCKET") type %d", l_cur, l_cur->socket, l_cur->type );
                 }
             }
 
             if(l_flag_nval ){
-                log_it(L_WARNING, "NVAL flag armed for socket %p (%"DAP_FORMAT_SOCKET")", l_cur, l_cur->socket);
+                log_it(L_WARNING, "NVAL flag armed for socket %p (sd=%"DAP_FORMAT_SOCKET")", l_cur, l_cur->socket);
                 l_cur->buf_out_size = 0;
                 l_cur->buf_in_size = 0;
                 l_cur->flags |= DAP_SOCK_SIGNAL_CLOSE;
@@ -374,7 +379,7 @@ void *dap_worker_thread(void *arg)
 
                 //log_it(L_DEBUG, "Comes connection with type %d", l_cur->type);
                 if(l_cur->buf_in_size_max && l_cur->buf_in_size >= l_cur->buf_in_size_max ) {
-                    log_it(L_WARNING, "Buffer is full when there is smth to read. Its dropped! esocket %p (%"DAP_FORMAT_SOCKET")", l_cur, l_cur->socket);
+                    log_it(L_WARNING, "Buffer is full when there is smth to read. Its dropped! esocket %p (sd=%"DAP_FORMAT_SOCKET")", l_cur, l_cur->socket);
                     l_cur->buf_in_size = 0;
                 }
 
diff --git a/modules/chain/include/dap_chain.h b/modules/chain/include/dap_chain.h
index e8f6f6bf7b..b364dea637 100644
--- a/modules/chain/include/dap_chain.h
+++ b/modules/chain/include/dap_chain.h
@@ -80,6 +80,7 @@ typedef dap_chain_datum_tx_t* (*dap_chain_callback_tx_find_by_hash_t)(dap_chain_
 typedef dap_chain_atom_ptr_t * (*dap_chain_callback_atom_iter_get_atoms_t)(dap_chain_atom_iter_t * ,size_t* ,size_t**);
 
 typedef dap_chain_atom_ptr_t (*dap_chain_callback_atom_iter_get_next_t)(dap_chain_atom_iter_t *  ,size_t*);
+typedef dap_chain_atom_ptr_t (*dap_chain_callback_atom_iter_set_nlast_t) (dap_chain_atom_iter_t *, size_t *, size_t);
 typedef void (*dap_chain_callback_atom_iter_delete_t)(dap_chain_atom_iter_t *  );
 
 typedef size_t (*dap_chain_callback_add_datums_t)(dap_chain_t * , dap_chain_datum_t **, size_t );
@@ -145,6 +146,7 @@ typedef struct dap_chain{
     dap_chain_callback_atom_iter_find_by_hash_t callback_atom_find_by_hash;
     dap_chain_callback_tx_find_by_hash_t callback_tx_find_by_hash;
     dap_chain_callback_atom_iter_get_next_t callback_atom_iter_get_next;
+    dap_chain_callback_atom_iter_set_nlast_t callback_atom_iter_set_nlast;
     dap_chain_callback_atom_iter_get_atoms_t callback_atom_iter_get_links;
     dap_chain_callback_atom_iter_get_atoms_t callback_atom_iter_get_lasts;
     dap_chain_callback_atom_iter_delete_t callback_atom_iter_delete;
diff --git a/modules/mempool/dap_chain_mempool.c b/modules/mempool/dap_chain_mempool.c
index 34a93f668d..749b19926b 100644
--- a/modules/mempool/dap_chain_mempool.c
+++ b/modules/mempool/dap_chain_mempool.c
@@ -796,6 +796,8 @@ void chain_mempool_proc(struct dap_http_simple *cl_st, void * arg)
             } else{
                 *return_code = Http_Status_BadRequest;
             }
+
+            DAP_DELETE(request_str);
         }
         else
             *return_code = Http_Status_BadRequest;
diff --git a/modules/net/dap_chain_node_cli.c b/modules/net/dap_chain_node_cli.c
index 8d70b8514e..ef95fc045b 100644
--- a/modules/net/dap_chain_node_cli.c
+++ b/modules/net/dap_chain_node_cli.c
@@ -379,7 +379,7 @@ static void* thread_one_client_func(void *args)
                 size_t l_reply_rest = l_reply_len;
                 while(l_reply_rest) {
                     size_t l_send_bytes = min(l_reply_step, l_reply_rest);
-                    int ret = send(newsockfd, reply_str + l_reply_len - l_reply_rest, l_send_bytes, 0);
+                    int ret = send(newsockfd, reply_str + l_reply_len - l_reply_rest, l_send_bytes, MSG_NOSIGNAL);
                     if(ret<=0)
                         break;
                     l_reply_rest-=l_send_bytes;
diff --git a/modules/net/dap_chain_node_cli_cmd_tx.c b/modules/net/dap_chain_node_cli_cmd_tx.c
index a31617d2f2..de7306b48e 100644
--- a/modules/net/dap_chain_node_cli_cmd_tx.c
+++ b/modules/net/dap_chain_node_cli_cmd_tx.c
@@ -25,6 +25,7 @@
 #include <stdbool.h>
 #include <stddef.h>
 #include <pthread.h>
+#include <errno.h>
 
 #include "dap_chain_wallet.h"
 #include "dap_common.h"
@@ -1102,6 +1103,265 @@ static char* dap_db_history_filter(dap_chain_t * a_chain, dap_ledger_t *a_ledger
 }
 
 
+
+
+/**
+ * @brief dap_db_history_filter
+ * Get data according the history log
+ *
+ * return history string
+ * @param a_chain
+ * @param a_ledger
+ * @param a_filter_token_name
+ * @param a_filtr_addr_base58
+ * @param a_hash_out_type
+ * @param a_datum_start
+ * @param a_datum_end
+ * @param a_total_datums
+ * @param a_tx_hash_processed
+ * @return char*
+ */
+static char* dap_db_history_filter_ext (
+        dap_chain_t * a_chain,
+        dap_ledger_t *a_ledger,
+        const char *a_filter_token_name,
+        const char *a_filtr_addr_base58,
+        const char *a_hash_out_type,
+        long a_datum_start,
+        long a_datum_end,
+        long *a_total_datums,
+        dap_chain_tx_hash_processed_ht_t *a_tx_hash_processed,
+        size_t  a_nr)
+{
+    dap_string_t *l_str_out;
+    dap_tx_data_t *l_tx_data_hash = NULL;
+    size_t l_atom_size;
+    size_t l_datum_num = 0, l_token_num = 0, l_emission_num = 0, l_tx_num = 0, l_datum_unproc_num = 0;
+    size_t l_datum_num_global = a_total_datums ? *a_total_datums : 0;
+
+    dap_chain_atom_iter_t *l_atom_iter;
+    dap_chain_atom_ptr_t l_atom;
+    char l_time_str[128], l_hash_str[128];
+
+    if ( !(l_atom_iter = a_chain->callback_atom_iter_create(a_chain)) )
+        return  log_it(L_ERROR, "Cannot create iterator for chain \"%s\"", a_chain->name), NULL;
+
+    if ( !(l_atom = a_chain->callback_atom_iter_set_nlast(l_atom_iter, &l_atom_size, a_nr)) )
+        return  log_it(L_ERROR, "Error retrieve  chain \"%s\"", a_chain->name), NULL;
+
+    if ( !(l_str_out = dap_string_new(NULL)) )
+        return  log_it(L_ERROR, "Error retrieve  chain \"%s\", errno=%d", a_chain->name, errno), NULL;
+
+
+    /*
+     * Point <dap_chain_atom_iter_t> to <tail>-NR position
+     */
+    while(l_atom && l_atom_size) {
+        size_t l_datums_count = 0;
+        dap_chain_datum_t **l_datums;
+
+        l_datums = (a_chain->callback_atom_get_datums && l_atom && l_atom_size) ?
+                        a_chain->callback_atom_get_datums(l_atom, l_atom_size, &l_datums_count) : NULL;
+
+        if(!l_datums)
+            return  log_it(L_WARNING, "Not defined callback_atom_get_datums for chain \"%s\"", a_chain->name), NULL;
+
+        for(size_t l_datum_n = 0; l_datum_n < l_datums_count; l_datum_n++) {
+
+            dap_chain_datum_t *l_datum = l_datums[l_datum_n];
+            if(!l_datum)
+                continue;
+
+            // 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: {
+
+                // no token necessary for addr
+                if(a_filtr_addr_base58)
+                        break;
+
+                dap_chain_datum_token_t *l_token = (dap_chain_datum_token_t*) l_datum->data;
+                // datum out of page
+                if(a_datum_start >= 0 && (l_datum_num+l_datum_num_global < (size_t)a_datum_start || l_datum_num+l_datum_num_global >= (size_t)a_datum_end)){
+                    l_token_num++;
+                    break;
+                }
+                if(!a_filter_token_name || !dap_strcmp(l_token->ticker, a_filter_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: %.0Lf(%"DAP_UINT64_FORMAT_U"), 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: %"DAP_UINT64_FORMAT_U", 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: %"DAP_UINT64_FORMAT_U", 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);
+                        char * l_balance = dap_chain_balance_to_coins(l_token->header_public.total_supply);
+                        dap_string_append_printf(l_str_out,
+                                " total_supply: %s(%s), flags: 0x%x\n, premine_supply: %s, premine_address '%s'\n",
+                                //l_token->header_public.total_supply / DATOSHI_LD,
+                                dap_chain_balance_to_coins(l_token->header_public.total_supply),
+                                dap_chain_balance_print(l_token->header_public.total_supply),
+                                l_token->header_public.flags,
+                                dap_chain_balance_print(l_token->header_public.premine_supply),
+                                l_addr ? l_addr : "-");
+                        DAP_DELETE(l_addr);
+                        DAP_DELETE(l_balance);
+                    }
+                        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: {
+                // datum out of page
+                if(a_datum_start >= 0 && (l_datum_num+l_datum_num_global < (size_t)a_datum_start || l_datum_num+l_datum_num_global >= (size_t)a_datum_end)) {
+                     l_emission_num++;
+                     break;
+                }
+                size_t l_emission_size = dap_chain_datum_emission_get_size(l_datum->data);
+                dap_chain_datum_token_emission_t *l_token_em = dap_chain_datum_emission_read(l_datum->data, &l_emission_size);
+                if(!a_filter_token_name || !dap_strcmp(l_token_em->hdr.ticker, a_filter_token_name)) {
+                    char * l_token_emission_address_str = dap_chain_addr_to_str(&(l_token_em->hdr.address));
+                    // filter for addr
+                    if (a_filtr_addr_base58 && dap_strcmp(a_filtr_addr_base58,l_token_emission_address_str)) {
+                         break;
+                    }
+
+                    dap_string_append_printf(l_str_out, "emission: %.0Lf(%"DAP_UINT64_FORMAT_U") %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);
+                    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: %.0Lf(%"DAP_UINT64_FORMAT_U"), 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++;
+                }
+                DAP_DELETE(l_token_em);
+            } break;
+
+            // transaction
+            case DAP_CHAIN_DATUM_TX:{
+                // datum out of page
+                if(a_datum_start >= 0 && (l_datum_num+l_datum_num_global < (size_t)a_datum_start || l_datum_num+l_datum_num_global >= (size_t)a_datum_end)) {
+                    l_tx_num++;
+                    break;
+                }
+                dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*)l_datum->data;
+                //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);
+                dap_chain_tx_hash_processed_ht_t *l_sht = NULL;
+                HASH_FIND(hh, a_tx_hash_processed, &l_tx_hash, sizeof(dap_chain_hash_fast_t), l_sht);
+                if ( (l_sht != NULL) || !s_dap_chain_datum_tx_out_data(l_tx, a_ledger, l_str_out, a_hash_out_type, &l_tx_hash))
+                {
+                    l_datum_num--;
+                    l_datum_unproc_num++;
+
+                    dap_hash_fast_to_str(&l_tx_hash, l_hash_str, sizeof(l_hash_str) );
+                    dap_string_append_printf(l_str_out, "	%s - unprocessed\n", l_hash_str);
+
+                    break;
+                }
+                l_sht = DAP_NEW_Z(dap_chain_tx_hash_processed_ht_t);
+                memcpy(&l_sht->hash, &l_tx_hash, sizeof(dap_chain_hash_fast_t));
+                HASH_ADD(hh, a_tx_hash_processed, hash, sizeof(dap_chain_hash_fast_t), l_sht);
+                l_tx_num++;
+            }
+                break;
+            default:
+                dap_string_append_printf(l_str_out, "unknown datum type=%d\n", l_datum->header.type_id);
+                break;
+            }
+            l_datum_num++;
+        }
+            l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter, &l_atom_size);
+    }
+    a_chain->callback_atom_iter_delete(l_atom_iter);
+    //total
+    dap_string_append_printf(l_str_out,
+            "---------------\ntokens: %zu\nemissions: %zu\ntransactions: %zu\ntotal datums: %zu (%zu unprocessed)", l_token_num,
+            l_emission_num, l_tx_num, l_datum_num, l_datum_unproc_num);
+
+    // return total datums
+    if(a_total_datums)
+        *a_total_datums = 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;
+}
+
+
+
+
+
+
+
 /**
  * @brief com_ledger
  * ledger command
@@ -1114,12 +1374,13 @@ static char* dap_db_history_filter(dap_chain_t * a_chain, dap_ledger_t *a_ledger
 int com_ledger(int a_argc, char ** a_argv, char **a_str_reply)
 {
     enum { CMD_NONE, CMD_LIST, CMD_TX_HISTORY, CMD_TX_INFO };
-    int arg_index = 1;
+    int arg_index = 1, l_nr = 0;
     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;
+    const char *l_nr_str = NULL;
 
     dap_chain_t * l_chain = NULL;
     dap_chain_net_t * l_net = NULL;
@@ -1149,6 +1410,10 @@ int com_ledger(int a_argc, char ** a_argv, char **a_str_reply)
         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);
+        dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-tx_num", &l_nr_str);
+
+        l_nr = l_nr_str ? strtoul(l_nr_str, NULL, 10) : 0;
+
         dap_chain_tx_hash_processed_ht_t *l_list_tx_hash_processd = NULL;
 
         if(!l_is_all && !l_addr_base58 && !l_wallet_name && !l_tx_hash_str) {
@@ -1238,9 +1503,17 @@ int com_ledger(int a_argc, char ** a_argv, char **a_str_reply)
                 dap_string_append_printf(l_str_ret, "chain: %s\n", l_chain_cur->name);
                 dap_ledger_t *l_ledger = dap_chain_ledger_by_net_name(l_net_str);
                 if(l_is_all) {
+                    if ( l_nr )
+                    {
+                        l_str_out = dap_db_history_filter_ext(l_chain_cur, l_ledger, NULL, NULL, l_hash_out_type, -1, 0, NULL, l_list_tx_hash_processd, l_nr);
+                        dap_string_append_printf(l_str_ret, "all history:\n%s\n", l_str_out ? l_str_out : " empty");
+                    }
+                    else {
+
                     // without filters
                     l_str_out = dap_db_history_filter(l_chain_cur, l_ledger, NULL, NULL, l_hash_out_type, -1, 0, NULL, l_list_tx_hash_processd);
                     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 ?
diff --git a/modules/type/dag/dap_chain_cs_dag.c b/modules/type/dag/dap_chain_cs_dag.c
index 1fd96ce954..15c8b394cd 100644
--- a/modules/type/dag/dap_chain_cs_dag.c
+++ b/modules/type/dag/dap_chain_cs_dag.c
@@ -109,6 +109,9 @@ static dap_chain_atom_ptr_t *s_chain_callback_atom_iter_get_links( dap_chain_ato
 static dap_chain_atom_ptr_t *s_chain_callback_atom_iter_get_lasts( dap_chain_atom_iter_t * a_atom_iter ,size_t *a_links_size,
                                                                   size_t ** a_lasts_size_ptr );  //    Get list of linked events
 
+static dap_chain_atom_ptr_t s_chain_callback_atom_iter_set_nlast (dap_chain_atom_iter_t * a_atom_iter, size_t *a_ret_size, size_t a_count);
+
+
 // Delete iterator
 static void s_chain_callback_atom_iter_delete(dap_chain_atom_iter_t * a_atom_iter );                  //    Get the fisrt event from dag
 
@@ -233,6 +236,8 @@ int dap_chain_cs_dag_new(dap_chain_t * a_chain, dap_config_t * a_chain_cfg)
 
     a_chain->callback_add_datums = s_chain_callback_datums_pool_proc;
 
+    a_chain->callback_atom_iter_set_nlast = s_chain_callback_atom_iter_set_nlast;
+
     // Datum operations callbacks
 /*
     a_chain->callback_datum_iter_create = s_chain_callback_datum_iter_create; // Datum iterator create
@@ -1832,3 +1837,48 @@ static dap_list_t *s_dap_chain_callback_get_txs(dap_chain_t *a_chain, size_t a_c
     return l_list;
 }
 
+
+static dap_chain_atom_ptr_t s_chain_callback_atom_iter_set_nlast (
+        dap_chain_atom_iter_t * a_atom_iter,
+                    size_t * a_ret_size,
+                    size_t a_count)
+{
+const char    *l_func = __FUNCTION__;
+dap_chain_cs_dag_event_item_t *l_event_item;
+dap_chain_cs_dag_t * l_dag;
+dap_chain_cs_dag_pvt_t *l_dag_pvt;
+
+    if ( !a_atom_iter )
+        return   log_it(L_ERROR, "NULL iterator on input for %s() function", l_func), NULL;
+
+    assert ( (l_dag = DAP_CHAIN_CS_DAG(a_atom_iter->chain)) );
+    assert ( (l_dag_pvt = PVT(l_dag)) );
+
+                                                                            /* Point <l_event_item> to first element in table */
+    if ( !(l_event_item = (dap_chain_cs_dag_event_item_t *) l_dag_pvt->events) )
+        return  NULL;
+
+    while ( l_event_item->hh.next )                                         /* Run over records to end of list */
+        l_event_item = l_event_item->hh.next;
+
+
+    for (int i = a_count; i; i--)                                           /* Rewind <l_event_item> to <a-count> elements */
+        {
+        if ( !l_event_item->hh.prev )                                       /* Stop is no previous element */
+            break;
+
+        l_event_item = (dap_chain_cs_dag_event_item_t*) l_event_item->hh.prev;/* Point <l_event_item> to previous element */
+        }
+
+
+    a_atom_iter->cur_item = l_event_item;                                   /* So , reconstruct the iterator */
+
+    a_atom_iter->cur = l_event_item->event;
+    a_atom_iter->cur_size = a_atom_iter->cur ? l_event_item->event_size : 0;
+    a_atom_iter->cur_hash = &l_event_item->hash;
+
+    if (a_ret_size)
+        *a_ret_size = a_atom_iter->cur_size;
+
+    return a_atom_iter->cur;
+}
-- 
GitLab