From d75a523ab289a4290760e323489ea1a9e9da5eb0 Mon Sep 17 00:00:00 2001
From: "Dmitriy A. Gerasimov" <dmitriy.gerasimov@demlabs.net>
Date: Sat, 17 Oct 2020 02:00:21 +0700
Subject: [PATCH] [*] Changed callback_atom_get_datum chain callback to
 callback_atom_get_datums to let to return more then one datum from a chain
 atom [+] More blockchain callback realized

---
 modules/chain/include/dap_chain.h             |   4 +-
 modules/net/dap_chain_node_cli_cmd_tx.c       | 590 +++++++++---------
 modules/type/blocks/dap_chain_block.c         | 129 ++++
 modules/type/blocks/dap_chain_block_cache.c   |  13 +-
 modules/type/blocks/dap_chain_cs_blocks.c     | 131 +++-
 modules/type/blocks/include/dap_chain_block.h |  17 +
 .../blocks/include/dap_chain_block_cache.h    |   2 +-
 modules/type/dag/dap_chain_cs_dag.c           |  22 +-
 8 files changed, 579 insertions(+), 329 deletions(-)

diff --git a/modules/chain/include/dap_chain.h b/modules/chain/include/dap_chain.h
index baad228c86..8cb420e0b5 100644
--- a/modules/chain/include/dap_chain.h
+++ b/modules/chain/include/dap_chain.h
@@ -71,7 +71,7 @@ typedef size_t (*dap_chain_callback_atom_get_hdr_size_t)(void);
 typedef dap_chain_atom_iter_t* (*dap_chain_callback_atom_iter_create_t)(dap_chain_t * );
 typedef dap_chain_atom_iter_t* (*dap_chain_callback_atom_iter_create_from_t)(dap_chain_t * ,dap_chain_atom_ptr_t, size_t);
 typedef dap_chain_atom_ptr_t (*dap_chain_callback_atom_iter_get_first_t)(dap_chain_atom_iter_t * , size_t*);
-typedef dap_chain_datum_t* (*dap_chain_callback_atom_get_datum)(dap_chain_atom_ptr_t, size_t );
+typedef dap_chain_datum_t** (*dap_chain_callback_atom_get_datum_t)(dap_chain_atom_ptr_t, size_t, size_t * );
 typedef dap_chain_atom_ptr_t (*dap_chain_callback_atom_iter_find_by_hash_t)(dap_chain_atom_iter_t * ,dap_chain_hash_fast_t *,size_t*);
 typedef dap_chain_datum_tx_t* (*dap_chain_callback_tx_find_by_hash_t)(dap_chain_t * ,dap_chain_hash_fast_t *);
 
@@ -132,7 +132,7 @@ typedef struct dap_chain{
     dap_chain_callback_atom_iter_create_t callback_atom_iter_create;
     dap_chain_callback_atom_iter_create_from_t callback_atom_iter_create_from;
     dap_chain_callback_atom_iter_get_first_t callback_atom_iter_get_first;
-    dap_chain_callback_atom_get_datum callback_atom_get_datum;
+    dap_chain_callback_atom_get_datum_t callback_atom_get_datums;
 
     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;
diff --git a/modules/net/dap_chain_node_cli_cmd_tx.c b/modules/net/dap_chain_node_cli_cmd_tx.c
index 7838fc1920..67cc3f8066 100644
--- a/modules/net/dap_chain_node_cli_cmd_tx.c
+++ b/modules/net/dap_chain_node_cli_cmd_tx.c
@@ -316,168 +316,129 @@ char* dap_db_history_addr(dap_chain_addr_t * a_addr, dap_chain_t * a_chain, cons
     }
 
     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, l_atom_size) :
-                                                                        (dap_chain_datum_t*)l_atom;
-        if(!l_datum || l_datum->header.type_id != DAP_CHAIN_DATUM_TX) {
-            // go to next transaction
-            l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter, &l_atom_size);
-            continue;
+        size_t l_datums_count =0;
+        dap_chain_datum_t **l_datums = a_chain->callback_atom_get_datums ? a_chain->callback_atom_get_datums(l_atom, l_atom_size, &l_datums_count) :
+                                                                          NULL;
+        if (! l_datums){
+            log_it(L_WARNING,"Not defined callback_atom_get_datums for chain \"%s\"", a_chain->name);
+            break;
         }
-        // transaction
-        dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*) l_datum->data;
-        dap_list_t *l_records_out = NULL;
-        // transaction time
-        char *l_time_str = NULL;
-        {
-            if(l_tx->header.ts_created > 0) {
-                time_t rawtime = (time_t) l_tx->header.ts_created;
-                struct tm * timeinfo;
-                timeinfo = localtime(&rawtime);
-                if(timeinfo)
-                    l_time_str = dap_strdup(asctime(timeinfo));
-            }
-            else
-                l_time_str = dap_strdup(" ");
-        }
-
-        // 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);
 
-        // list of dap_tx_data_t*; info about OUT item in current transaction
-        dap_list_t *l_list_out_info = NULL;
-
-        // find OUT items
-        dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT, NULL);
-        dap_list_t *l_list_out_items_tmp = l_list_out_items;
-        while(l_list_out_items_tmp) {
-            const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_list_out_items_tmp->data;
-            // save OUT item l_tx_out
+        for (size_t d=0; d< l_datums_count; d++){
+            dap_chain_datum_t *l_datum = l_datums && l_datums_count ? l_datums[d] :NULL;
+            if(!l_datum || l_datum->header.type_id != DAP_CHAIN_DATUM_TX) {
+                // go to next transaction
+                l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter, &l_atom_size);
+                continue;
+            }
+            // transaction
+            dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*) l_datum->data;
+            dap_list_t *l_records_out = NULL;
+            // transaction time
+            char *l_time_str = NULL;
             {
-                // save tx hash
-                // info about OUT item in current transaction
-                dap_tx_data_t *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_tx_data && 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));
+                if(l_tx->header.ts_created > 0) {
+                    time_t rawtime = (time_t) l_tx->header.ts_created;
+                    struct tm * timeinfo;
+                    timeinfo = localtime(&rawtime);
+                    if(timeinfo)
+                        l_time_str = dap_strdup(asctime(timeinfo));
                 }
-                HASH_ADD(hh, l_tx_data_hash, tx_hash, sizeof(dap_chain_hash_fast_t), l_tx_data);
-
-                // save OUT items to list
-                l_records_out = dap_list_append(l_records_out, (void*) l_tx_out);
-                // save info about OUT items to list
-                l_list_out_info = dap_list_append(l_list_out_info, (void*) l_tx_data);
+                else
+                    l_time_str = dap_strdup(" ");
             }
-            l_list_out_items_tmp = dap_list_next(l_list_out_items_tmp);
-        }
-
-        // 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_in_items_tmp = l_list_in_items;
-        // find cur addr in prev OUT items
-        //bool l_is_use_all_cur_out = false;
-        {
-            while(l_list_in_items_tmp) {
-                const dap_chain_tx_in_t *l_tx_in = (const dap_chain_tx_in_t*) l_list_in_items_tmp->data;
-                dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash;
-
-                //find prev OUT item
-                dap_tx_data_t *l_tx_data_prev = NULL;
-                HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), l_tx_data_prev);
-                if(l_tx_data_prev != NULL) {
-                    // fill token in all l_tx_data from prev transaction
-
-                    dap_list_t *l_list_out_info_tmp = l_list_out_info;
-                    while(l_list_out_info_tmp) {
-                        dap_tx_data_t *l_tx_data = (dap_tx_data_t*) l_list_out_info_tmp->data;
-                        if(l_tx_data) {
-                            // get token from prev tx
-                            memcpy(l_tx_data->token_ticker, l_tx_data_prev->token_ticker,
-                                    sizeof(l_tx_data->token_ticker));
-                            dap_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;
-                            if(l_tx_prev_out && !memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t)))
-                                l_tx_data->is_use_all_cur_out = true;
 
-                        }
-                        l_list_out_info_tmp = dap_list_next(l_list_out_info_tmp);
+            // 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);
+
+            // list of dap_tx_data_t*; info about OUT item in current transaction
+            dap_list_t *l_list_out_info = NULL;
+
+            // find OUT items
+            dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT, NULL);
+            dap_list_t *l_list_out_items_tmp = l_list_out_items;
+            while(l_list_out_items_tmp) {
+                const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_list_out_items_tmp->data;
+                // save OUT item l_tx_out
+                {
+                    // save tx hash
+                    // info about OUT item in current transaction
+                    dap_tx_data_t *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_tx_data && 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));
                     }
+                    HASH_ADD(hh, l_tx_data_hash, tx_hash, sizeof(dap_chain_hash_fast_t), l_tx_data);
+
+                    // save OUT items to list
+                    l_records_out = dap_list_append(l_records_out, (void*) l_tx_out);
+                    // save info about OUT items to list
+                    l_list_out_info = dap_list_append(l_list_out_info, (void*) l_tx_data);
                 }
-                l_list_in_items_tmp = dap_list_next(l_list_in_items_tmp);
+                l_list_out_items_tmp = dap_list_next(l_list_out_items_tmp);
             }
-            // find prev OUT items for IN items
-            dap_list_t *l_list_in_items2_tmp = l_list_in_items; // go to begin of list
-            while(l_list_in_items2_tmp) {
-                const dap_chain_tx_in_t *l_tx_in = (const dap_chain_tx_in_t*) l_list_in_items2_tmp->data;
-                dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash;
-                // if first transaction - empty prev OUT item
-                if(dap_hash_fast_is_blank(&tx_prev_hash)) {
-
-                    dap_tx_data_t *l_tx_data = NULL;
-                    dap_list_t *l_list_out_info_tmp = l_list_out_info;
-                    while(l_list_out_info_tmp) {
-                        l_tx_data = (dap_tx_data_t*) l_list_out_info_tmp->data;
-                        if(l_tx_data->token_ticker[0])
-                            break;
-                        l_list_out_info_tmp = dap_list_next(l_list_out_info_tmp);
-                    }
 
-                    // add emit info to ret string
-                    if(l_tx_data && !memcmp(&l_tx_data->addr, a_addr, sizeof(dap_chain_addr_t))) {
-                        dap_list_t *l_records_tmp = l_records_out;
-                        while(l_records_tmp) {
-                            char *tx_hash_str;
-                            if(!dap_strcmp(a_hash_out_type,"hex"))
-                                tx_hash_str = dap_strdup( l_tx_data->tx_hash_str);
-                            else
-                                tx_hash_str = dap_enc_base58_from_hex_str_to_str( l_tx_data->tx_hash_str);
-                            const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_records_tmp->data;
-
-                            if(!dap_strcmp(a_hash_out_type,"hex")){
-                            dap_string_append_printf(l_str_out, "tx hash %s \n emit %lu %s\n",
-                                    tx_hash_str,//l_tx_data->tx_hash_str,
-                                    l_tx_out->header.value,
-                                    l_tx_data->token_ticker);
-                            }
-                            else {
-                                dap_string_append_printf(l_str_out, "tx hash %s \n emit %lu %s\n",
-                                        l_tx_data->tx_hash_str,
-                                        l_tx_out->header.value,
-                                        l_tx_data->token_ticker);
-                            }
-                            DAP_DELETE(tx_hash_str);
-                            l_records_tmp = dap_list_next(l_records_tmp);
-                        }
-                    }
-                    //dap_list_free(l_records_out);
-                }
-                // in other transactions except first one
-                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_in_items_tmp = l_list_in_items;
+            // find cur addr in prev OUT items
+            //bool l_is_use_all_cur_out = false;
+            {
+                while(l_list_in_items_tmp) {
+                    const dap_chain_tx_in_t *l_tx_in = (const dap_chain_tx_in_t*) l_list_in_items_tmp->data;
+                    dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash;
+
                     //find prev OUT item
                     dap_tx_data_t *l_tx_data_prev = NULL;
                     HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), l_tx_data_prev);
                     if(l_tx_data_prev != NULL) {
-                        char *l_src_str = NULL;
-                        bool l_src_str_is_cur = false;
+                        // fill token in all l_tx_data from prev transaction
+
+                        dap_list_t *l_list_out_info_tmp = l_list_out_info;
+                        while(l_list_out_info_tmp) {
+                            dap_tx_data_t *l_tx_data = (dap_tx_data_t*) l_list_out_info_tmp->data;
+                            if(l_tx_data) {
+                                // get token from prev tx
+                                memcpy(l_tx_data->token_ticker, l_tx_data_prev->token_ticker,
+                                        sizeof(l_tx_data->token_ticker));
+                                dap_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;
+                                if(l_tx_prev_out && !memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t)))
+                                    l_tx_data->is_use_all_cur_out = true;
+
+                            }
+                            l_list_out_info_tmp = dap_list_next(l_list_out_info_tmp);
+                        }
+                    }
+                    l_list_in_items_tmp = dap_list_next(l_list_in_items_tmp);
+                }
+                // find prev OUT items for IN items
+                dap_list_t *l_list_in_items2_tmp = l_list_in_items; // go to begin of list
+                while(l_list_in_items2_tmp) {
+                    const dap_chain_tx_in_t *l_tx_in = (const dap_chain_tx_in_t*) l_list_in_items2_tmp->data;
+                    dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash;
+                    // if first transaction - empty prev OUT item
+                    if(dap_hash_fast_is_blank(&tx_prev_hash)) {
 
                         dap_tx_data_t *l_tx_data = NULL;
                         dap_list_t *l_list_out_info_tmp = l_list_out_info;
@@ -487,174 +448,223 @@ char* dap_db_history_addr(dap_chain_addr_t * a_addr, dap_chain_t * a_chain, cons
                                 break;
                             l_list_out_info_tmp = dap_list_next(l_list_out_info_tmp);
                         }
-                        if(l_tx_data) {
-                            // get token from prev tx
-                            memcpy(l_tx_data->token_ticker, l_tx_data_prev->token_ticker,
-                                    sizeof(l_tx_data->token_ticker));
-
-                            dap_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;
-                            // if use src addr
-                            bool l_is_use_src_addr = false;
-                            // find source addrs
-                            dap_string_t *l_src_addr = dap_string_new(NULL);
-                            {
-                                // find IN items in prev datum - for get destination addr
-                                dap_list_t *l_list_in_prev_items = dap_chain_datum_tx_items_get(l_tx_prev,
-                                        TX_ITEM_TYPE_IN, NULL);
-                                dap_list_t *l_list_tmp = l_list_in_prev_items;
-                                while(l_list_tmp) {
-                                    dap_chain_tx_in_t *l_tx_prev_in = l_list_tmp->data;
-                                    dap_chain_hash_fast_t l_tx_prev_prev_hash =
-                                            l_tx_prev_in->header.tx_prev_hash;
-                                    //find prev OUT item
-                                    dap_tx_data_t *l_tx_data_prev_prev = NULL;
-                                    HASH_FIND(hh, l_tx_data_hash, &l_tx_prev_prev_hash,
-                                            sizeof(dap_chain_hash_fast_t), l_tx_data_prev_prev);
-                                    if(l_tx_data_prev_prev) {
-                                        // if use src addr
-                                        if(l_tx_data_prev_prev &&
-                                                !memcmp(&l_tx_data_prev_prev->addr, a_addr,
-                                                        sizeof(dap_chain_addr_t)))
-                                            l_is_use_src_addr = true;
-                                        char *l_str = dap_chain_addr_to_str(&l_tx_data_prev_prev->addr);
-                                        if(l_src_addr->len > 0)
-                                            dap_string_append_printf(l_src_addr, "\n   %s", l_str);
-                                        else
-                                            dap_string_append_printf(l_src_addr, "%s", l_str); // first record
-                                        DAP_DELETE(l_str);
-                                    }
-                                    l_list_tmp = dap_list_next(l_list_tmp);
+
+                        // add emit info to ret string
+                        if(l_tx_data && !memcmp(&l_tx_data->addr, a_addr, sizeof(dap_chain_addr_t))) {
+                            dap_list_t *l_records_tmp = l_records_out;
+                            while(l_records_tmp) {
+                                char *tx_hash_str;
+                                if(!dap_strcmp(a_hash_out_type,"hex"))
+                                    tx_hash_str = dap_strdup( l_tx_data->tx_hash_str);
+                                else
+                                    tx_hash_str = dap_enc_base58_from_hex_str_to_str( l_tx_data->tx_hash_str);
+                                const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_records_tmp->data;
+
+                                if(!dap_strcmp(a_hash_out_type,"hex")){
+                                dap_string_append_printf(l_str_out, "tx hash %s \n emit %lu %s\n",
+                                        tx_hash_str,//l_tx_data->tx_hash_str,
+                                        l_tx_out->header.value,
+                                        l_tx_data->token_ticker);
                                 }
+                                else {
+                                    dap_string_append_printf(l_str_out, "tx hash %s \n emit %lu %s\n",
+                                            l_tx_data->tx_hash_str,
+                                            l_tx_out->header.value,
+                                            l_tx_data->token_ticker);
+                                }
+                                DAP_DELETE(tx_hash_str);
+                                l_records_tmp = dap_list_next(l_records_tmp);
                             }
-
-                            l_src_str_is_cur = l_is_use_src_addr;
-                            if(l_src_addr->len <= 1) {
-                                l_src_str =
-                                        (l_tx_data) ? dap_chain_addr_to_str(&l_tx_data->addr) :
-                                        NULL;
-                                if(l_tx_prev_out && !memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t)))
-                                    l_src_str_is_cur = true;
-                                dap_string_free(l_src_addr, true);
+                        }
+                        //dap_list_free(l_records_out);
+                    }
+                    // in other transactions except first one
+                    else {
+                        //find prev OUT item
+                        dap_tx_data_t *l_tx_data_prev = NULL;
+                        HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), l_tx_data_prev);
+                        if(l_tx_data_prev != NULL) {
+                            char *l_src_str = NULL;
+                            bool l_src_str_is_cur = false;
+
+                            dap_tx_data_t *l_tx_data = NULL;
+                            dap_list_t *l_list_out_info_tmp = l_list_out_info;
+                            while(l_list_out_info_tmp) {
+                                l_tx_data = (dap_tx_data_t*) l_list_out_info_tmp->data;
+                                if(l_tx_data->token_ticker[0])
+                                    break;
+                                l_list_out_info_tmp = dap_list_next(l_list_out_info_tmp);
                             }
-                            else
-                                l_src_str = dap_string_free(l_src_addr, false);
-
-                            if(l_tx_prev_out) {
-                                char *l_dst_to_str = dap_chain_addr_to_str(&l_tx_prev_out->addr);
-                                // if use dst addr
-                                bool l_is_use_dst_addr = false;
-                                if(!memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t)))
-                                    l_is_use_dst_addr = true;
-                                char *tx_hash_str;
-                                if(!dap_strcmp(a_hash_out_type, "hex"))
-                                    tx_hash_str = dap_strdup(l_tx_data->tx_hash_str);
+                            if(l_tx_data) {
+                                // get token from prev tx
+                                memcpy(l_tx_data->token_ticker, l_tx_data_prev->token_ticker,
+                                        sizeof(l_tx_data->token_ticker));
+
+                                dap_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;
+                                // if use src addr
+                                bool l_is_use_src_addr = false;
+                                // find source addrs
+                                dap_string_t *l_src_addr = dap_string_new(NULL);
+                                {
+                                    // find IN items in prev datum - for get destination addr
+                                    dap_list_t *l_list_in_prev_items = dap_chain_datum_tx_items_get(l_tx_prev,
+                                            TX_ITEM_TYPE_IN, NULL);
+                                    dap_list_t *l_list_tmp = l_list_in_prev_items;
+                                    while(l_list_tmp) {
+                                        dap_chain_tx_in_t *l_tx_prev_in = l_list_tmp->data;
+                                        dap_chain_hash_fast_t l_tx_prev_prev_hash =
+                                                l_tx_prev_in->header.tx_prev_hash;
+                                        //find prev OUT item
+                                        dap_tx_data_t *l_tx_data_prev_prev = NULL;
+                                        HASH_FIND(hh, l_tx_data_hash, &l_tx_prev_prev_hash,
+                                                sizeof(dap_chain_hash_fast_t), l_tx_data_prev_prev);
+                                        if(l_tx_data_prev_prev) {
+                                            // if use src addr
+                                            if(l_tx_data_prev_prev &&
+                                                    !memcmp(&l_tx_data_prev_prev->addr, a_addr,
+                                                            sizeof(dap_chain_addr_t)))
+                                                l_is_use_src_addr = true;
+                                            char *l_str = dap_chain_addr_to_str(&l_tx_data_prev_prev->addr);
+                                            if(l_src_addr->len > 0)
+                                                dap_string_append_printf(l_src_addr, "\n   %s", l_str);
+                                            else
+                                                dap_string_append_printf(l_src_addr, "%s", l_str); // first record
+                                            DAP_DELETE(l_str);
+                                        }
+                                        l_list_tmp = dap_list_next(l_list_tmp);
+                                    }
+                                }
+
+                                l_src_str_is_cur = l_is_use_src_addr;
+                                if(l_src_addr->len <= 1) {
+                                    l_src_str =
+                                            (l_tx_data) ? dap_chain_addr_to_str(&l_tx_data->addr) :
+                                            NULL;
+                                    if(l_tx_prev_out && !memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t)))
+                                        l_src_str_is_cur = true;
+                                    dap_string_free(l_src_addr, true);
+                                }
                                 else
-                                    tx_hash_str = dap_enc_base58_from_hex_str_to_str(l_tx_data->tx_hash_str);
-                                if(l_is_use_src_addr && !l_is_use_dst_addr) {
-                                    dap_string_append_printf(l_str_out,
-                                            "tx hash %s \n %s in send  %lu %s from %s\n to %s\n",
-                                            tx_hash_str,//l_tx_data->tx_hash_str,
-                                            l_time_str ? l_time_str : "",
-                                            l_tx_prev_out->header.value,
-                                            l_tx_data->token_ticker,
-                                            l_src_str ? l_src_str : "",
-                                            l_dst_to_str);
-                                } else if(l_is_use_dst_addr && !l_is_use_src_addr) {
-                                    if(!l_src_str_is_cur)
+                                    l_src_str = dap_string_free(l_src_addr, false);
+
+                                if(l_tx_prev_out) {
+                                    char *l_dst_to_str = dap_chain_addr_to_str(&l_tx_prev_out->addr);
+                                    // if use dst addr
+                                    bool l_is_use_dst_addr = false;
+                                    if(!memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t)))
+                                        l_is_use_dst_addr = true;
+                                    char *tx_hash_str;
+                                    if(!dap_strcmp(a_hash_out_type, "hex"))
+                                        tx_hash_str = dap_strdup(l_tx_data->tx_hash_str);
+                                    else
+                                        tx_hash_str = dap_enc_base58_from_hex_str_to_str(l_tx_data->tx_hash_str);
+                                    if(l_is_use_src_addr && !l_is_use_dst_addr) {
                                         dap_string_append_printf(l_str_out,
-                                                "tx hash %s \n %s in recv %lu %s from %s\n",
+                                                "tx hash %s \n %s in send  %lu %s from %s\n to %s\n",
                                                 tx_hash_str,//l_tx_data->tx_hash_str,
                                                 l_time_str ? l_time_str : "",
                                                 l_tx_prev_out->header.value,
                                                 l_tx_data->token_ticker,
-                                                l_src_str ? l_src_str : "");
+                                                l_src_str ? l_src_str : "",
+                                                l_dst_to_str);
+                                    } else if(l_is_use_dst_addr && !l_is_use_src_addr) {
+                                        if(!l_src_str_is_cur)
+                                            dap_string_append_printf(l_str_out,
+                                                    "tx hash %s \n %s in recv %lu %s from %s\n",
+                                                    tx_hash_str,//l_tx_data->tx_hash_str,
+                                                    l_time_str ? l_time_str : "",
+                                                    l_tx_prev_out->header.value,
+                                                    l_tx_data->token_ticker,
+                                                    l_src_str ? l_src_str : "");
+                                    }
+                                    DAP_DELETE(tx_hash_str);
+                                    DAP_DELETE(l_dst_to_str);
                                 }
-                                DAP_DELETE(tx_hash_str);
-                                DAP_DELETE(l_dst_to_str);
+                                dap_list_free(l_list_out_prev_items);
                             }
-                            dap_list_free(l_list_out_prev_items);
-                        }
-
-                        // OUT items
-                        dap_list_t *l_records_tmp = l_records_out;
-                        while(l_records_tmp) {
 
-                            const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_records_tmp->data;
-
-                            if(l_tx_data->is_use_all_cur_out
-                                    || !memcmp(&l_tx_out->addr, a_addr, sizeof(dap_chain_addr_t))) {
-
-                                char *l_addr_str = (l_tx_out) ? dap_chain_addr_to_str(&l_tx_out->addr) : NULL;
-
-                                char *tx_hash_str;
-                                if(!dap_strcmp(a_hash_out_type, "hex"))
-                                    tx_hash_str = dap_strdup(l_tx_data->tx_hash_str);
-                                else
-                                    tx_hash_str = dap_enc_base58_from_hex_str_to_str(l_tx_data->tx_hash_str);
-                                if(!memcmp(&l_tx_out->addr, a_addr, sizeof(dap_chain_addr_t))) {
-                                    if(!l_src_str_is_cur)
+                            // OUT items
+                            dap_list_t *l_records_tmp = l_records_out;
+                            while(l_records_tmp) {
+
+                                const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_records_tmp->data;
+
+                                if(l_tx_data->is_use_all_cur_out
+                                        || !memcmp(&l_tx_out->addr, a_addr, sizeof(dap_chain_addr_t))) {
+
+                                    char *l_addr_str = (l_tx_out) ? dap_chain_addr_to_str(&l_tx_out->addr) : NULL;
+
+                                    char *tx_hash_str;
+                                    if(!dap_strcmp(a_hash_out_type, "hex"))
+                                        tx_hash_str = dap_strdup(l_tx_data->tx_hash_str);
+                                    else
+                                        tx_hash_str = dap_enc_base58_from_hex_str_to_str(l_tx_data->tx_hash_str);
+                                    if(!memcmp(&l_tx_out->addr, a_addr, sizeof(dap_chain_addr_t))) {
+                                        if(!l_src_str_is_cur)
+                                            dap_string_append_printf(l_str_out,
+                                                    "tx hash %s \n %s recv %lu %s from %s\n",
+                                                    tx_hash_str,//l_tx_data->tx_hash_str,
+                                                    l_time_str ? l_time_str : "",
+                                                    l_tx_out->header.value,
+                                                    l_tx_data_prev->token_ticker,
+                                                    l_src_str ? l_src_str : "?");
+                                        // break search prev OUT items for IN items
+                                        l_list_in_items2_tmp = NULL;
+                                    }
+                                    else {
                                         dap_string_append_printf(l_str_out,
-                                                "tx hash %s \n %s recv %lu %s from %s\n",
+                                                "tx hash %s \n %s send %lu %s to %s\n",
                                                 tx_hash_str,//l_tx_data->tx_hash_str,
                                                 l_time_str ? l_time_str : "",
                                                 l_tx_out->header.value,
                                                 l_tx_data_prev->token_ticker,
-                                                l_src_str ? l_src_str : "?");
-                                    // break search prev OUT items for IN items
-                                    l_list_in_items2_tmp = NULL;
-                                }
-                                else {
-                                    dap_string_append_printf(l_str_out,
-                                            "tx hash %s \n %s send %lu %s to %s\n",
-                                            tx_hash_str,//l_tx_data->tx_hash_str,
-                                            l_time_str ? l_time_str : "",
-                                            l_tx_out->header.value,
-                                            l_tx_data_prev->token_ticker,
-                                            l_addr_str ? l_addr_str : "");
-                                    l_list_in_items2_tmp = NULL;
+                                                l_addr_str ? l_addr_str : "");
+                                        l_list_in_items2_tmp = NULL;
+                                    }
+                                    DAP_DELETE(tx_hash_str);
+                                    DAP_DELETE(l_addr_str);
                                 }
-                                DAP_DELETE(tx_hash_str);
-                                DAP_DELETE(l_addr_str);
+
+                                l_records_tmp = dap_list_next(l_records_tmp);
                             }
+                            //dap_list_free(l_records_out);
+                            DAP_DELETE(l_src_str);
 
-                            l_records_tmp = dap_list_next(l_records_tmp);
                         }
-                        //dap_list_free(l_records_out);
-                        DAP_DELETE(l_src_str);
-
                     }
+                    l_list_in_items2_tmp = dap_list_next(l_list_in_items2_tmp);
                 }
-                l_list_in_items2_tmp = dap_list_next(l_list_in_items2_tmp);
+    //                l_list_in_items_tmp = dap_list_next(l_list_in_items_tmp);
+    //            }
             }
-//                l_list_in_items_tmp = dap_list_next(l_list_in_items_tmp);
-//            }
-        }
 
-        if(l_list_tx_token)
-            dap_list_free(l_list_tx_token);
-        if(l_list_out_items)
-            dap_list_free(l_list_out_items);
-        if(l_list_in_items)
-            dap_list_free(l_list_in_items);
-        dap_list_free(l_records_out);
-        dap_list_free(l_list_out_info);
-        DAP_DELETE(l_time_str);
+            if(l_list_tx_token)
+                dap_list_free(l_list_tx_token);
+            if(l_list_out_items)
+                dap_list_free(l_list_out_items);
+            if(l_list_in_items)
+                dap_list_free(l_list_in_items);
+            dap_list_free(l_records_out);
+            dap_list_free(l_list_out_info);
+            DAP_DELETE(l_time_str);
 
-        // go to next transaction
-        l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter, &l_atom_size);
+            // go to next transaction
+            l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter, &l_atom_size);
+        }
+        DAP_DELETE(l_datums);
     }
 
     // delete hashes
diff --git a/modules/type/blocks/dap_chain_block.c b/modules/type/blocks/dap_chain_block.c
index f4c204bd8b..e1999a5c67 100644
--- a/modules/type/blocks/dap_chain_block.c
+++ b/modules/type/blocks/dap_chain_block.c
@@ -102,3 +102,132 @@ size_t dap_chain_block_datum_del_by_hash(dap_chain_block_t * a_block, size_t a_b
     return a_block_size;
 }
 
+dap_chain_datum_t** dap_chain_block_get_datums(dap_chain_block_t * a_block, size_t a_block_size,size_t * a_datums_count )
+{
+
+}
+
+dap_chain_block_meta_t** dap_chain_block_get_meta(dap_chain_block_t * a_block, size_t a_block_size,size_t * a_meta_count )
+{
+
+}
+
+/**
+ * @brief dap_chain_block_meta_extract_generals
+ * @param a_meta
+ * @param a_meta_count
+ * @param a_block_prev_hash
+ * @param a_block_anchor_hash
+ * @param a_is_genesis
+ * @param a_nonce
+ * @param a_nonce2
+ */
+void dap_chain_block_meta_extract(dap_chain_block_meta_t ** a_meta, size_t a_meta_count,
+                                    dap_chain_hash_fast_t * a_block_prev_hash,
+                                    dap_chain_hash_fast_t * a_block_anchor_hash,
+                                    dap_chain_hash_fast_t ** a_block_links,
+                                    size_t *a_block_links_count,
+                                    bool * a_is_genesis,
+                                    uint64_t *a_nonce,
+                                    uint64_t *a_nonce2
+                                  )
+{
+    // Check for meta that could be faced only once
+    bool l_was_prev = false;
+    bool l_was_genesis = false;
+    bool l_was_anchor = false;
+    bool l_was_nonce = false;
+    bool l_was_nonce2 = false;
+    // Init links parsing
+    size_t l_links_count_max = 5;
+    if (a_block_links_count)
+        *a_block_links_count = 0;
+
+
+    for(size_t i = 0; i < a_meta_count; i++){
+        dap_chain_block_meta_t * l_meta = a_meta[i];
+        switch (l_meta->hdr.type) {
+            case DAP_CHAIN_BLOCK_META_GENESIS:
+                if(l_was_genesis){
+                    log_it(L_WARNING, "Genesis meta could be only one in the block, meta #%u is ignored ", i);
+                    break;
+                }
+                l_was_genesis = true;
+                if (a_is_genesis)
+                    *a_is_genesis = true;
+            break;
+            case DAP_CHAIN_BLOCK_META_PREV:
+                if(l_was_prev){
+                    log_it(L_WARNING, "Prev meta could be only one in the block, meta #%u is ignored ", i);
+                    break;
+                }
+                l_was_prev = true;
+                if (a_block_prev_hash){
+                    if (l_meta->hdr.size == sizeof (*a_block_prev_hash) )
+                        memcpy(a_block_prev_hash, l_meta->data, l_meta->hdr.size);
+                    else
+                        log_it(L_WARNING, "Meta  #%zd PREV has wrong size %zd when expecting %zd",i, l_meta->hdr.size, sizeof (*a_block_prev_hash));
+                }
+            break;
+            case DAP_CHAIN_BLOCK_META_ANCHOR:
+                if(l_was_anchor){
+                    log_it(L_WARNING, "Anchor meta could be only one in the block, meta #%u is ignored ", i);
+                    break;
+                }
+                l_was_anchor = true;
+                if ( a_block_anchor_hash){
+                    if (l_meta->hdr.size == sizeof (*a_block_anchor_hash) )
+                        memcpy(a_block_anchor_hash, l_meta->data, l_meta->hdr.size);
+                    else
+                        log_it(L_WARNING, "Anchor meta #%zd has wrong size %zd when expecting %zd",i, l_meta->hdr.size, sizeof (*a_block_prev_hash));
+                }
+            break;
+            case DAP_CHAIN_BLOCK_META_LINK:
+                if ( a_block_links && a_block_links_count){
+                    if ( *a_block_links_count == 0 ){
+                        *a_block_links = DAP_NEW_Z_SIZE(dap_chain_hash_fast_t, sizeof (dap_chain_hash_fast_t *) *l_links_count_max);
+                        *a_block_links_count = 0;
+                    }else if ( *a_block_links_count == l_links_count_max ){
+                        l_links_count_max *=2;
+                        *a_block_links = DAP_REALLOC(*a_block_links, l_links_count_max);
+                    }
+
+                    if (l_meta->hdr.size == sizeof (**a_block_links) ){
+                        memcpy(&a_block_links[*a_block_links_count], l_meta->data, l_meta->hdr.size);
+                        (*a_block_links_count)++;
+                    }else
+                        log_it(L_WARNING, "Link meta #%zd has wrong size %zd when expecting %zd", i, l_meta->hdr.size, sizeof (*a_block_prev_hash));
+                }
+            break;
+            case DAP_CHAIN_BLOCK_META_NONCE:
+                if(l_was_nonce){
+                    log_it(L_WARNING, "NONCE could be only one in the block, meta #%u is ignored ", i);
+                    break;
+                }
+                l_was_nonce = true;
+
+                if ( a_nonce){
+                    if (l_meta->hdr.size == sizeof (*a_nonce ) )
+                        memcpy(a_nonce, l_meta->data, l_meta->hdr.size);
+                    else
+                        log_it(L_WARNING, "NONCE meta #%zd has wrong size %zd when expecting %zd",i, l_meta->hdr.size, sizeof (*a_nonce));
+                }
+            break;
+            case DAP_CHAIN_BLOCK_META_NONCE2:
+                if(l_was_nonce2){
+                    log_it(L_WARNING, "NONCE2 could be only one in the block, meta #%u is ignored ", i);
+                    break;
+                }
+                l_was_nonce2 = true;
+                if ( a_nonce2){
+                    if (l_meta->hdr.size == sizeof (*a_nonce2 ) )
+                        memcpy(a_nonce2, l_meta->data, l_meta->hdr.size);
+                    else
+                        log_it(L_WARNING, "NONCE2 meta #%zd has wrong size %zd when expecting %zd",i, l_meta->hdr.size, sizeof (*a_nonce2));
+                }
+            break;
+            default: { log_it(L_WARNING, "Unknown meta #%zd type 0x%02x (size %zd), possible corrupted block or you need to upgrade your software",
+                                 i, l_meta->hdr.type, l_meta->hdr.type); }
+        }
+    }
+}
diff --git a/modules/type/blocks/dap_chain_block_cache.c b/modules/type/blocks/dap_chain_block_cache.c
index 2d4defa28e..98c58befea 100644
--- a/modules/type/blocks/dap_chain_block_cache.c
+++ b/modules/type/blocks/dap_chain_block_cache.c
@@ -80,16 +80,6 @@ dap_chain_block_cache_t * dap_chain_block_cache_dup(dap_chain_block_cache_t * a_
     return l_ret;
 }
 
-/**
- * @brief dap_chain_block_cache_get_by_hash
- * @param a_block_hash
- * @return
- */
-dap_chain_block_cache_t * dap_chain_block_cache_get_by_hash(dap_chain_hash_fast_t a_block_hash)
-{
-    return NULL;
-}
-
 /**
  * @brief dap_chain_block_cache_update
  * @param a_block_cache
@@ -99,6 +89,9 @@ void dap_chain_block_cache_update(dap_chain_block_cache_t * a_block_cache)
     assert(a_block_cache);
     assert(a_block_cache->block);
     dap_hash_fast(a_block_cache->block, a_block_cache->block_size, &a_block_cache->block_hash);
+    if (a_block_cache->datum)
+        DAP_DELETE(a_block_cache->datum);
+    a_block_cache->datum = dap_chain_block_get_datums( a_block_cache->block, a_block_cache->block_size, &a_block_cache->datum_count );
 }
 
 /**
diff --git a/modules/type/blocks/dap_chain_cs_blocks.c b/modules/type/blocks/dap_chain_cs_blocks.c
index 4553ae187e..9c245fc597 100644
--- a/modules/type/blocks/dap_chain_cs_blocks.c
+++ b/modules/type/blocks/dap_chain_cs_blocks.c
@@ -69,8 +69,15 @@ typedef struct dap_chain_cs_blocks_pvt
     size_t block_new_size;
 } dap_chain_cs_blocks_pvt_t;
 
+typedef struct dap_chain_cs_blocks_iter
+{
+    dap_chain_cs_blocks_t * blocks;
+} dap_chain_cs_blocks_iter_t;
+
 #define PVT(a) ((dap_chain_cs_blocks_pvt_t *) a->_pvt )
 
+#define ITER_PVT(a) ((dap_chain_cs_blocks_iter_t *) a->_inheritor )
+
 static int s_cli_parse_cmd_hash(char ** a_argv, int a_arg_index, int a_argc, char **a_str_reply,const char * a_param, dap_chain_hash_fast_t * a_datum_hash);
 static void s_cli_meta_hash_print(  dap_string_t * a_str_tmp, const char * a_meta_title, dap_chain_block_meta_t * a_meta);
 static int s_cli_blocks(int a_argc, char ** a_argv, void *a_arg_func, char **a_str_reply);
@@ -93,9 +100,9 @@ static dap_chain_atom_iter_t* s_callback_atom_iter_create_from(dap_chain_t *  ,
 static dap_chain_atom_ptr_t s_callback_atom_iter_find_by_hash(dap_chain_atom_iter_t * a_atom_iter ,
                                                                        dap_chain_hash_fast_t * a_atom_hash, size_t * a_atom_size);
 static dap_chain_datum_tx_t* s_callback_atom_iter_find_by_tx_hash(dap_chain_t * a_chain ,
-                                                                       dap_chain_hash_fast_t * a_atom_hash);
+                                                                       dap_chain_hash_fast_t * a_tx_hash);
 
-static dap_chain_datum_t* s_callback_atom_get_datum(dap_chain_atom_ptr_t a_event, size_t a_atom_size);
+static dap_chain_datum_t** s_callback_atom_get_datums(dap_chain_atom_ptr_t a_atom, size_t a_atom_size, size_t * a_datums_count);
 //    Get blocks
 static dap_chain_atom_ptr_t s_callback_atom_iter_get_first( dap_chain_atom_iter_t * a_atom_iter, size_t *a_atom_size ); //    Get the fisrt block
 static dap_chain_atom_ptr_t s_callback_atom_iter_get_next( dap_chain_atom_iter_t * a_atom_iter,size_t *a_atom_size );  //    Get the next block
@@ -181,7 +188,7 @@ int dap_chain_cs_blocks_new(dap_chain_t * a_chain, dap_config_t * a_chain_config
     // Linear pass through
     a_chain->callback_atom_iter_get_first = s_callback_atom_iter_get_first; // Get the fisrt element from chain
     a_chain->callback_atom_iter_get_next = s_callback_atom_iter_get_next; // Get the next element from chain from the current one
-    a_chain->callback_atom_get_datum = s_callback_atom_get_datum;
+    a_chain->callback_atom_get_datums = s_callback_atom_get_datums;
 
     a_chain->callback_atom_iter_get_links = s_callback_atom_iter_get_links; // Get the next element from chain from the current one
     a_chain->callback_atom_iter_get_lasts = s_callback_atom_iter_get_lasts;
@@ -226,6 +233,20 @@ void dap_chain_cs_blocks_delete(dap_chain_t * a_chain)
    pthread_rwlock_destroy(&PVT(a_chain)->rwlock );
 }
 
+/**
+ * @brief dap_chain_block_cs_cache_get_by_hash
+ * @param a_blocks
+ * @param a_block_hash
+ * @return
+ */
+dap_chain_block_cache_t * dap_chain_block_cs_cache_get_by_hash(dap_chain_cs_blocks_t * a_blocks,  dap_chain_hash_fast_t *a_block_hash)
+{
+    dap_chain_block_cache_t * l_ret = NULL;
+    pthread_rwlock_rdlock(& PVT(a_blocks)->rwlock);
+    HASH_FIND(hh, PVT(a_blocks)->blocks,a_block_hash, sizeof (*a_block_hash), l_ret );
+    pthread_rwlock_unlock(& PVT(a_blocks)->rwlock);
+    return l_ret;
+}
 
 /**
  * @brief s_cli_parse_cmd_hash
@@ -322,9 +343,6 @@ static int s_cli_blocks(int a_argc, char ** a_argv, void *a_arg_func, char **a_s
 
     int arg_index = 1;
 
-    const char * l_net_name = NULL;
-    const char * l_chain_name = NULL;
-
     dap_chain_t * l_chain = NULL;
     dap_chain_cs_blocks_t * l_blocks = NULL;
     dap_chain_net_t * l_net = NULL;
@@ -429,7 +447,7 @@ static int s_cli_blocks(int a_argc, char ** a_argv, void *a_arg_func, char **a_s
             dap_enc_base58_hex_to_hash( l_subcmd_str_arg, &l_block_hash); // Convert argument to hash
             l_block = (dap_chain_block_t*) dap_chain_get_atom_by_hash( l_chain, &l_block_hash, &l_block_size);
             if ( l_block){
-                dap_chain_block_cache_t *l_block_cache = dap_chain_block_cache_get(l_block, l_block_size);
+                dap_chain_block_cache_t *l_block_cache = dap_chain_block_cs_cache_get_by_hash(l_blocks, &l_block_hash);
                 if ( l_block_cache ){
                     dap_string_t * l_str_tmp = dap_string_new(NULL);
                     char buf[50];
@@ -649,6 +667,10 @@ static int s_add_atom_to_blocks(dap_chain_cs_blocks_t * a_blocks, dap_ledger_t *
         //All correct, no matter for result
         pthread_rwlock_wrlock( &PVT(a_blocks)->rwlock );
         HASH_ADD(hh, PVT(a_blocks)->blocks,block_hash,sizeof (a_block_cache->block_hash), a_block_cache);
+        if (! (PVT(a_blocks)->block_cache_first ) )
+                PVT(a_blocks)->block_cache_first = a_block_cache;
+        PVT(a_blocks)->block_cache_last = a_block_cache;
+
     } else {
         log_it(L_WARNING,"Dag event %s check failed: code %d", a_block_cache->block_hash_str,  res );
     }
@@ -671,11 +693,11 @@ static dap_chain_atom_verify_res_t s_callback_atom_add(dap_chain_t * a_chain, da
     dap_chain_hash_fast_t l_block_hash;
     size_t l_block_size = a_atom_size;
     dap_hash_fast(a_atom,a_atom_size, & l_block_hash);
-    dap_chain_block_cache_t * l_block_cache = dap_chain_block_cache_get_by_hash(l_block_hash);
+    dap_chain_block_cache_t * l_block_cache = dap_chain_block_cs_cache_get_by_hash(l_blocks, &l_block_hash);
     if (l_block_cache ){
-        ret = ATOM_PASS;
         log_it(L_DEBUG, "... already present in blocks %s",l_block_cache->block_hash_str);
-    } else{
+        return ATOM_PASS;
+    } else {
         l_block_cache = dap_chain_block_cache_new( l_block, l_block_size);
         log_it(L_DEBUG, "... new block %s",l_block_cache->block_hash_str);
         ret = ATOM_ACCEPT;
@@ -702,9 +724,6 @@ static dap_chain_atom_verify_res_t s_callback_atom_add(dap_chain_t * a_chain, da
              ret = ATOM_REJECT;
         }
     }
-    // will added in callback s_chain_callback_atom_add_from_treshold()
-    //while(dap_chain_cs_dag_proc_treshold(l_dag, a_chain->ledger));
-
     if(ret == ATOM_PASS){
         DAP_DELETE(l_block_cache);
     }
@@ -720,7 +739,58 @@ static dap_chain_atom_verify_res_t s_callback_atom_add(dap_chain_t * a_chain, da
  */
 static dap_chain_atom_verify_res_t s_callback_atom_verify(dap_chain_t * a_chain, dap_chain_atom_ptr_t a_atom , size_t a_atom_size)
 {
+    dap_chain_cs_blocks_t * l_blocks = DAP_CHAIN_CS_BLOCKS(a_chain);
+    assert(l_blocks);
+    dap_chain_cs_blocks_pvt_t * l_blocks_pvt = PVT(l_blocks);
+    assert(l_blocks_pvt);
+    dap_chain_block_t * l_block = (dap_chain_block_t *) a_atom;
+    dap_chain_atom_verify_res_t res = ATOM_ACCEPT;
 
+    if(sizeof (l_block->hdr) >= a_atom_size){
+        log_it(L_WARNING,"Size of block is %zd that is equal or less then block's header size %zd",a_atom_size,sizeof (l_block->hdr));
+        return  ATOM_REJECT;
+    }
+    size_t l_meta_count = 0;
+    dap_chain_block_meta_t ** l_meta=  dap_chain_block_get_meta(l_block, a_atom_size, & l_meta_count);
+    // Parse metadata
+    bool l_is_genesis=false;
+    dap_chain_hash_fast_t l_block_prev_hash = {0};
+    dap_chain_hash_fast_t l_block_anchor_hash = {0};
+    uint64_t l_nonce = 0;
+    uint64_t l_nonce2 = 0;
+    dap_chain_block_meta_extract(l_meta, l_meta_count,
+                                        &l_block_prev_hash,
+                                        &l_block_anchor_hash,
+                                        NULL,
+                                        NULL,
+                                        &l_is_genesis,
+                                        &l_nonce,
+                                        &l_nonce2 ) ;
+    // genesis or seed mode
+    if ( l_is_genesis){
+        if( s_seed_mode && ! l_blocks_pvt->blocks ){
+            log_it(L_NOTICE,"Accepting new genesis block");
+            return ATOM_ACCEPT;
+        }else if(s_seed_mode){
+            log_it(L_WARNING,"Cant accept genesis blockt: already present data in blockchain");
+            return  ATOM_REJECT;
+        }
+    }else{
+        dap_chain_block_cache_t * l_block_prev_cache = dap_chain_block_cs_cache_get_by_hash(l_blocks,&l_block_prev_hash);
+        if ( ! l_block_prev_cache ){
+            res = ATOM_REJECT;
+            log_it(L_WARNING, "Can't find PREV hash (but because still no bysantium consensus we just drop such block off)");
+            // TODO make treshold and bysantium consensus
+        }
+    }
+
+    // 2nd level consensus
+    if(res == ATOM_ACCEPT)
+        if(l_blocks->callback_block_verify)
+            if (l_blocks->callback_block_verify(l_blocks, l_block, a_atom_size))
+                res = ATOM_REJECT;
+
+    return res;
 }
 
 /**
@@ -729,7 +799,7 @@ static dap_chain_atom_verify_res_t s_callback_atom_verify(dap_chain_t * a_chain,
  */
 static size_t s_callback_atom_get_static_hdr_size(void)
 {
-
+    return sizeof (dap_chain_block_hdr_t);
 }
 
 /**
@@ -739,7 +809,10 @@ static size_t s_callback_atom_get_static_hdr_size(void)
  */
 static dap_chain_atom_iter_t* s_callback_atom_iter_create(dap_chain_t * a_chain )
 {
-
+    dap_chain_atom_iter_t * l_atom_iter = DAP_NEW_Z(dap_chain_atom_iter_t);
+    l_atom_iter->chain = a_chain;
+    l_atom_iter->_inheritor = DAP_NEW_Z(dap_chain_cs_blocks_iter_t);
+    return l_atom_iter;
 }
 
 /**
@@ -773,7 +846,7 @@ static dap_chain_atom_ptr_t s_callback_atom_iter_find_by_hash(dap_chain_atom_ite
  * @param a_atom_hash
  * @return
  */
-static dap_chain_datum_tx_t* s_callback_atom_iter_find_by_tx_hash(dap_chain_t * a_chain, dap_chain_hash_fast_t * a_atom_hash)
+static dap_chain_datum_tx_t* s_callback_atom_iter_find_by_tx_hash(dap_chain_t * a_chain, dap_chain_hash_fast_t * a_tx_hash)
 {
 
 }
@@ -784,9 +857,11 @@ static dap_chain_datum_tx_t* s_callback_atom_iter_find_by_tx_hash(dap_chain_t *
  * @param a_atom_size
  * @return
  */
-static dap_chain_datum_t* s_callback_atom_get_datum(dap_chain_atom_ptr_t a_event, size_t a_atom_size)
+static dap_chain_datum_t** s_callback_atom_get_datums(dap_chain_atom_ptr_t a_atom, size_t a_atom_size, size_t * a_datums_count)
 {
-
+    assert(a_datums_count);
+    dap_chain_datum_t ** l_ret = dap_chain_block_get_datums(a_atom, a_atom_size,a_datums_count);
+    return l_ret;
 }
 
 /**
@@ -797,7 +872,23 @@ static dap_chain_datum_t* s_callback_atom_get_datum(dap_chain_atom_ptr_t a_event
  */
 static dap_chain_atom_ptr_t s_callback_atom_iter_get_first( dap_chain_atom_iter_t * a_atom_iter, size_t *a_atom_size )
 {
-
+    if(! a_atom_iter){
+        log_it(L_ERROR, "NULL iterator on input for atom_iter_get_first function");
+        return NULL;
+    }
+    dap_chain_cs_blocks_t * l_blocks = DAP_CHAIN_CS_BLOCKS(a_atom_iter->chain);
+    dap_chain_cs_blocks_pvt_t *l_blocks_pvt = l_blocks ? PVT(l_blocks) : NULL;
+    assert(l_blocks_pvt);
+    a_atom_iter->cur_item = l_blocks_pvt->block_cache_last ;
+    a_atom_iter->cur = l_blocks_pvt->block_cache_last ?  l_blocks_pvt->block_cache_last->block : NULL  ;
+    a_atom_iter->cur_size = l_blocks_pvt->block_cache_first ? l_blocks_pvt->block_cache_first->block_size : 0;
+
+//    a_atom_iter->cur =  a_atom_iter->cur ?
+//                (dap_chain_cs_dag_event_t*) PVT (DAP_CHAIN_CS_DAG( a_atom_iter->chain) )->events->event : NULL;
+//    a_atom_iter->cur_item = PVT (DAP_CHAIN_CS_DAG( a_atom_iter->chain) )->events;
+    if (a_atom_size)
+        *a_atom_size = a_atom_iter->cur_size;
+    return a_atom_iter->cur;
 }
 
 /**
@@ -841,7 +932,7 @@ static dap_chain_atom_ptr_t *s_callback_atom_iter_get_lasts( dap_chain_atom_iter
  */
 static void s_callback_atom_iter_delete(dap_chain_atom_iter_t * a_atom_iter )
 {
-
+    DAP_DELETE(a_atom_iter);
 }
 
 /**
diff --git a/modules/type/blocks/include/dap_chain_block.h b/modules/type/blocks/include/dap_chain_block.h
index cad3a15179..3b75aa4e72 100644
--- a/modules/type/blocks/include/dap_chain_block.h
+++ b/modules/type/blocks/include/dap_chain_block.h
@@ -93,3 +93,20 @@ size_t dap_chain_block_meta_add(dap_chain_block_t * a_block, size_t a_block_size
 // Add datum in block
 size_t dap_chain_block_datum_add(dap_chain_block_t * a_block, size_t a_block_size, dap_chain_datum_t * a_datum, size_t a_datum_size);
 size_t dap_chain_block_datum_del_by_hash(dap_chain_block_t * a_block, size_t a_block_size, dap_chain_hash_fast_t* a_datum_hash);
+
+// Create and return datums list
+dap_chain_datum_t** dap_chain_block_get_datums(dap_chain_block_t * a_block, size_t a_block_size,size_t * a_datums_count );
+
+// Create and return meta parameters  list
+dap_chain_block_meta_t** dap_chain_block_get_meta(dap_chain_block_t * a_block, size_t a_block_size,size_t * a_meta_count );
+
+void dap_chain_block_meta_extract(dap_chain_block_meta_t ** a_meta, size_t a_meta_count,
+                                    dap_chain_hash_fast_t * a_block_prev_hash,
+                                    dap_chain_hash_fast_t * a_block_anchor_hash,
+                                    dap_chain_hash_fast_t ** a_block_links,
+                                    size_t *a_block_links_count,
+                                    bool * a_is_genesis,
+                                    uint64_t *a_nonce,
+                                    uint64_t *a_nonce2
+                                  );
+
diff --git a/modules/type/blocks/include/dap_chain_block_cache.h b/modules/type/blocks/include/dap_chain_block_cache.h
index d29cf3aaf4..1018855d18 100644
--- a/modules/type/blocks/include/dap_chain_block_cache.h
+++ b/modules/type/blocks/include/dap_chain_block_cache.h
@@ -36,7 +36,7 @@ typedef struct dap_chain_block_cache{
     time_t ts_created;
 
     // Block's datums
-    uint32_t datum_count;
+    size_t datum_count;
     dap_chain_datum_t ** datum;
 
     // Block's metadatas
diff --git a/modules/type/dag/dap_chain_cs_dag.c b/modules/type/dag/dap_chain_cs_dag.c
index 6823ea1c67..b21506e4e5 100644
--- a/modules/type/dag/dap_chain_cs_dag.c
+++ b/modules/type/dag/dap_chain_cs_dag.c
@@ -97,7 +97,7 @@ static dap_chain_atom_ptr_t s_chain_callback_atom_iter_find_by_hash(dap_chain_at
 static dap_chain_datum_tx_t* s_chain_callback_atom_iter_find_by_tx_hash(dap_chain_t * a_chain ,
                                                                        dap_chain_hash_fast_t * a_atom_hash);
 
-static dap_chain_datum_t* s_chain_callback_atom_get_datum(dap_chain_atom_ptr_t a_event, size_t a_atom_size);
+static dap_chain_datum_t** s_chain_callback_atom_get_datum(dap_chain_atom_ptr_t a_event, size_t a_atom_size, size_t *a_datums_count);
 //    Get event(s) from dag
 static dap_chain_atom_ptr_t s_chain_callback_atom_iter_get_first( dap_chain_atom_iter_t * a_atom_iter, size_t *a_atom_size ); //    Get the fisrt event from dag
 static dap_chain_atom_ptr_t s_chain_callback_atom_iter_get_next( dap_chain_atom_iter_t * a_atom_iter,size_t *a_atom_size );  //    Get the next event from dag
@@ -187,7 +187,7 @@ int dap_chain_cs_dag_new(dap_chain_t * a_chain, dap_config_t * a_chain_cfg)
     // Linear pass through
     a_chain->callback_atom_iter_get_first = s_chain_callback_atom_iter_get_first; // Get the fisrt element from chain
     a_chain->callback_atom_iter_get_next = s_chain_callback_atom_iter_get_next; // Get the next element from chain from the current one
-    a_chain->callback_atom_get_datum = s_chain_callback_atom_get_datum;
+    a_chain->callback_atom_get_datums = s_chain_callback_atom_get_datum;
 
     a_chain->callback_atom_iter_get_links = s_chain_callback_atom_iter_get_links; // Get the next element from chain from the current one
     a_chain->callback_atom_iter_get_lasts = s_chain_callback_atom_iter_get_lasts;
@@ -928,13 +928,23 @@ static dap_chain_atom_iter_t* s_chain_callback_atom_iter_create(dap_chain_t * a_
 /**
  * @brief s_chain_callback_atom_get_datum Get the datum from event
  * @param a_atom_iter
+ * @param a_datums_count
  * @return
  */
-static dap_chain_datum_t* s_chain_callback_atom_get_datum(dap_chain_atom_ptr_t a_event, size_t a_atom_size)
+static dap_chain_datum_t** s_chain_callback_atom_get_datum(dap_chain_atom_ptr_t a_event, size_t a_atom_size, size_t *a_datums_count)
 {
-    if(a_event)
-        return dap_chain_cs_dag_event_get_datum((dap_chain_cs_dag_event_t*) a_event, a_atom_size);
-    return NULL;
+    assert(a_datums_count);
+    if(a_event){
+        dap_chain_datum_t * l_datum = dap_chain_cs_dag_event_get_datum((dap_chain_cs_dag_event_t*) a_event, a_atom_size);
+        if (l_datum){
+            dap_chain_datum_t ** l_datums = DAP_NEW_SIZE(dap_chain_datum_t*, sizeof(dap_chain_datum_t*));
+            *a_datums_count = 1;
+            l_datums[0] = l_datum;
+            return l_datums;
+        }else
+            return NULL;
+    }else
+        return NULL;
 }
 
 /**
-- 
GitLab