From afda7c4de85a94560a9be0c5097efbf34db78117 Mon Sep 17 00:00:00 2001
From: Roman Khlopkov <roman.khlopkov@demlabs.net>
Date: Fri, 22 Mar 2024 12:38:17 +0000
Subject: [PATCH] feature-10492-atomic

---
 modules/chain/dap_chain.c                     |   5 +-
 modules/chain/dap_chain_cell.c                |   6 +-
 modules/chain/dap_chain_ch.c                  | 743 ++++++++++++------
 modules/chain/dap_chain_ch_pkt.c              |  13 +-
 modules/chain/include/dap_chain.h             |  36 +-
 modules/chain/include/dap_chain_ch.h          |  45 +-
 modules/chain/include/dap_chain_ch_pkt.h      | 104 +--
 .../chain-net/dap_stream_ch_chain_net.c       |   4 +-
 modules/net/dap_chain_net.c                   | 137 +++-
 modules/net/dap_chain_node_cli_cmd.c          |   8 +-
 modules/net/dap_chain_node_cli_cmd_tx.c       |  18 +-
 modules/net/dap_chain_node_client.c           |  42 +-
 modules/type/blocks/dap_chain_cs_blocks.c     | 240 ++----
 modules/type/dag/dap_chain_cs_dag.c           | 355 +++------
 modules/type/none/dap_chain_cs_none.c         | 169 +---
 15 files changed, 1009 insertions(+), 916 deletions(-)

diff --git a/modules/chain/dap_chain.c b/modules/chain/dap_chain.c
index bf022e9559..70870d8821 100644
--- a/modules/chain/dap_chain.c
+++ b/modules/chain/dap_chain.c
@@ -683,8 +683,7 @@ void dap_chain_add_callback_notify(dap_chain_t * a_chain, dap_chain_callback_not
 bool dap_chain_get_atom_last_hash(dap_chain_t *a_chain, dap_hash_fast_t *a_atom_hash, dap_chain_cell_id_t a_cell_id)
 {
     dap_chain_atom_iter_t *l_iter = a_chain->callback_atom_iter_create(a_chain, a_cell_id, false);
-    dap_chain_atom_ptr_t *l_ptr_list = a_chain->callback_atom_iter_get_lasts(l_iter, NULL, NULL);
-    DAP_DEL_Z(l_ptr_list);
+    a_chain->callback_atom_iter_get(l_iter, DAP_CHAIN_ITER_OP_LAST, NULL);
     *a_atom_hash = l_iter->cur_hash ? *l_iter->cur_hash : (dap_hash_fast_t){0};
     bool l_ret = l_iter->cur_hash;
     a_chain->callback_atom_iter_delete(l_iter);
@@ -722,7 +721,7 @@ ssize_t dap_chain_atom_save(dap_chain_cell_t *a_chain_cell, const uint8_t *a_ato
             dap_chain_ch_pkt_t *l_pkt = dap_chain_ch_pkt_new(l_chain->net_id.uint64, l_chain->id.uint64,
                                                              a_chain_cell->id.uint64, a_atom, a_atom_size);
             if (l_pkt) {
-                dap_gossip_msg_issue(l_net_cluster, DAP_STREAM_CH_CHAIN_ID, l_pkt, l_pkt_size, a_new_atom_hash);
+                dap_gossip_msg_issue(l_net_cluster, DAP_CHAIN_CH_ID, l_pkt, l_pkt_size, a_new_atom_hash);
                 DAP_DELETE(l_pkt);
             }
         }
diff --git a/modules/chain/dap_chain_cell.c b/modules/chain/dap_chain_cell.c
index 9d72785363..8149da8d74 100644
--- a/modules/chain/dap_chain_cell.c
+++ b/modules/chain/dap_chain_cell.c
@@ -347,12 +347,12 @@ ssize_t dap_chain_cell_file_append(dap_chain_cell_t *a_cell, const void *a_atom,
             pthread_rwlock_unlock(&a_cell->storage_rwlock);
             return -4;
         }
-        dap_chain_atom_iter_t *l_atom_iter = a_cell->chain->callback_atom_iter_create(a_cell->chain, a_cell->id, 0);
+        dap_chain_atom_iter_t *l_atom_iter = a_cell->chain->callback_atom_iter_create(a_cell->chain, a_cell->id, NULL);
         dap_chain_atom_ptr_t l_atom;
         uint64_t l_atom_size = 0;
-        for (l_atom = a_cell->chain->callback_atom_iter_get_first(l_atom_iter, &l_atom_size);
+        for (l_atom = a_cell->chain->callback_atom_iter_get(l_atom_iter, DAP_CHAIN_ITER_OP_FIRST, &l_atom_size);
              l_atom && l_atom_size;
-             l_atom = a_cell->chain->callback_atom_iter_get_next(l_atom_iter, &l_atom_size))
+             l_atom = a_cell->chain->callback_atom_iter_get(l_atom_iter, DAP_CHAIN_ITER_OP_NEXT, &l_atom_size))
         {
             if (s_file_atom_add(a_cell, l_atom, l_atom_size)) {
                 l_err = true;
diff --git a/modules/chain/dap_chain_ch.c b/modules/chain/dap_chain_ch.c
index 4187dec847..5276e274ac 100644
--- a/modules/chain/dap_chain_ch.c
+++ b/modules/chain/dap_chain_ch.c
@@ -95,15 +95,34 @@ struct sync_request
     };
 };
 
+enum sync_context_state {
+    SYNC_STATE_IDLE,
+    SYNC_STATE_READY,
+    SYNC_STATE_BUSY,
+    SYNC_STATE_OVER
+};
+
+struct sync_context {
+    atomic_uint_fast64_t allowed_num;
+    atomic_uint_fast16_t state;
+    dap_chain_atom_iter_t *iter;
+    dap_stream_node_addr_t addr;
+    dap_chain_net_id_t net_id;
+    dap_chain_id_t chain_id;
+    dap_chain_cell_id_t cell_id;
+    uint64_t num_last;
+    dap_time_t last_activity;
+};
+
 static void s_ch_chain_go_idle(dap_chain_ch_t *a_ch_chain);
-static inline bool s_ch_chain_get_idle(dap_chain_ch_t *a_ch_chain) { return a_ch_chain->state == CHAIN_STATE_IDLE; }
+static inline bool s_ch_chain_get_idle(dap_chain_ch_t *a_ch_chain) { return a_ch_chain->state == DAP_CHAIN_CH_STATE_IDLE; }
 
 static void s_stream_ch_new(dap_stream_ch_t* a_ch, void* a_arg);
 static void s_stream_ch_delete(dap_stream_ch_t* a_ch, void* a_arg);
 static void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg);
 static bool s_stream_ch_packet_out(dap_stream_ch_t* a_ch, void* a_arg);
 static void s_stream_ch_io_complete(dap_events_socket_t *a_es, void *a_arg);
-static void s_stream_ch_write_error_unsafe(dap_stream_ch_t *a_ch, uint64_t a_net_id, uint64_t a_chain_id, uint64_t a_cell_id, const char * a_err_string);
+static void s_stream_ch_write_error_unsafe(dap_stream_ch_t *a_ch, uint64_t a_net_id, uint64_t a_chain_id, uint64_t a_cell_id, dap_chain_ch_error_type_t a_error);
 
 static bool s_sync_out_chains_proc_callback(void *a_arg);
 static void s_sync_out_chains_last_worker_callback(dap_worker_t *a_worker, void *a_arg);
@@ -119,14 +138,20 @@ static bool s_gdb_in_pkt_proc_set_raw_callback(dap_global_db_instance_t *a_dbi,
                                                const size_t a_values_total, const size_t a_values_count,
                                                dap_store_obj_t *a_values, void *a_arg);
 static void s_gdb_in_pkt_error_worker_callback(dap_worker_t *a_thread, void *a_arg);
-static void s_free_log_list_gdb ( dap_chain_ch_t * a_ch_chain);
 
 static void s_stream_ch_chain_pkt_write(dap_stream_ch_t *a_ch, uint8_t a_type, uint64_t a_net_id,
                                         uint64_t a_chain_id, uint64_t a_cell_id,
                                         const void * a_data, size_t a_data_size);
 static void s_gossip_payload_callback(void *a_payload, size_t a_payload_size, dap_stream_node_addr_t a_sender_addr);
+static bool s_chain_iter_callback(void *a_arg);
+static bool s_chain_iter_delete_callback(void *a_arg);
+static bool s_sync_timer_callback(void *a_arg);
+
+static bool s_debug_more = false;
+static uint32_t s_sync_timeout = 30;
+static uint32_t s_sync_packets_per_thread_call = 10;
+static uint32_t s_sync_ack_window_size = 100; // atoms
 
-static bool s_debug_more=false;
 static uint_fast16_t s_update_pack_size=100; // Number of hashes packed into the one packet
 static uint_fast16_t s_skip_in_reactor_count=50; // Number of hashes packed to skip in one reactor loop callback out packet
 
@@ -139,6 +164,30 @@ static  dap_memstat_rec_t   s_memstat [MEMSTAT$K_NR] = {
 
 #endif
 
+static const char *s_error_type_to_string(dap_chain_ch_error_type_t a_error)
+{
+    switch (a_error) {
+    case DAP_CHAIN_CH_ERROR_SYNC_REQUEST_ALREADY_IN_PROCESS:
+        return "SYNC_REQUEST_ALREADY_IN_PROCESS";
+    case DAP_CHAIN_CH_ERROR_INCORRECT_SYNC_SEQUENCE:
+        return "INCORRECT_SYNC_SEQUENCE";
+    case DAP_CHAIN_CH_ERROR_CHAIN_PKT_DATA_SIZE:
+        return "IVALID_PACKET_SIZE";
+    case DAP_CHAIN_CH_ERROR_NET_INVALID_ID:
+        return "INVALID_NET_ID";
+    case DAP_CHAIN_CH_ERROR_CHAIN_NOT_FOUND:
+        return "CHAIN_NOT_FOUND";
+    case DAP_CHAIN_CH_ERROR_ATOM_NOT_FOUND:
+        return "ATOM_NOT_FOUND";
+    case DAP_CHAIN_CH_ERROR_UNKNOWN_CHAIN_PKT_TYPE:
+        return "UNKNOWN_CHAIN_PACKET_TYPE";
+    case DAP_CHAIN_CH_ERROR_GLOBAL_DB_INTERNAL_NOT_SAVED:
+        return "GLOBAL_DB_INTERNAL_SAVING_ERROR";
+    default:
+        return "UNKNOWN_ERROR";
+    }
+}
+
 /**
  * @brief dap_chain_ch_init
  * @return
@@ -146,15 +195,17 @@ static  dap_memstat_rec_t   s_memstat [MEMSTAT$K_NR] = {
 int dap_chain_ch_init()
 {
     log_it(L_NOTICE, "Chains and global db exchange channel initialized");
-    dap_stream_ch_proc_add(DAP_STREAM_CH_CHAIN_ID, s_stream_ch_new, s_stream_ch_delete, s_stream_ch_packet_in,
+    dap_stream_ch_proc_add(DAP_CHAIN_CH_ID, s_stream_ch_new, s_stream_ch_delete, s_stream_ch_packet_in,
             s_stream_ch_packet_out);
-    s_debug_more = dap_config_get_item_bool_default(g_config,"stream_ch_chain","debug_more",false);
-    s_update_pack_size = dap_config_get_item_int16_default(g_config,"stream_ch_chain","update_pack_size",100);
+    s_sync_timeout = dap_config_get_item_uint32_default(g_config, "chain", "sync_timeout", s_sync_timeout);
+    s_sync_ack_window_size = dap_config_get_item_uint32_default(g_config, "chain", "sync_ack_window_size", s_sync_ack_window_size);
+    s_sync_packets_per_thread_call = dap_config_get_item_int16_default(g_config, "chain", "pack_size", s_sync_packets_per_thread_call);
+    s_debug_more = dap_config_get_item_bool_default(g_config, "chain", "debug_more", false);
 #ifdef  DAP_SYS_DEBUG
     for (int i = 0; i < MEMSTAT$K_NR; i++)
         dap_memstat_reg(&s_memstat[i]);
 #endif
-    assert(!dap_stream_ch_gossip_callback_add(DAP_STREAM_CH_CHAIN_ID, s_gossip_payload_callback));
+    assert(!dap_stream_ch_gossip_callback_add(DAP_CHAIN_CH_ID, s_gossip_payload_callback));
     return 0;
 }
 
@@ -178,7 +229,7 @@ void s_stream_ch_new(dap_stream_ch_t* a_ch, void* a_arg)
         log_it(L_CRITICAL, "Memory allocation error");
         return;
     };
-    dap_chain_ch_t *l_ch_chain = DAP_STREAM_CH_CHAIN(a_ch);
+    dap_chain_ch_t *l_ch_chain = DAP_CHAIN_CH(a_ch);
     l_ch_chain->_inheritor = a_ch;
     a_ch->stream->esocket->callbacks.write_finished_callback = s_stream_ch_io_complete;
 
@@ -196,9 +247,9 @@ void s_stream_ch_new(dap_stream_ch_t* a_ch, void* a_arg)
 static void s_stream_ch_delete(dap_stream_ch_t *a_ch, void *a_arg)
 {
     UNUSED(a_arg);
-    dap_chain_ch_t *l_ch_chain = DAP_STREAM_CH_CHAIN(a_ch);
+    dap_chain_ch_t *l_ch_chain = DAP_CHAIN_CH(a_ch);
     if (l_ch_chain->callback_notify_packet_out)
-        l_ch_chain->callback_notify_packet_out(l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_DELETE, NULL, 0,
+        l_ch_chain->callback_notify_packet_out(l_ch_chain, DAP_CHAIN_CH_PKT_TYPE_DELETE, NULL, 0,
                                                l_ch_chain->callback_notify_arg);
     s_ch_chain_go_idle(l_ch_chain);
     debug_if(s_debug_more, L_DEBUG, "[stm_ch_chain:%p] --- deleted chain:%p", a_ch, l_ch_chain);
@@ -253,25 +304,25 @@ static void s_sync_out_chains_first_worker_callback(dap_worker_t *a_worker, void
         return;
     }
 
-    dap_chain_ch_t * l_ch_chain = DAP_STREAM_CH_CHAIN(l_ch);
+    dap_chain_ch_t * l_ch_chain = DAP_CHAIN_CH(l_ch);
     if (!l_ch_chain) {
         log_it(L_CRITICAL, "Channel without chain, dump it");
         s_sync_request_delete(l_sync_request);
         return;
     }
-    if (l_ch_chain->state != CHAIN_STATE_UPDATE_CHAINS_REMOTE) {
+    if (l_ch_chain->state != DAP_CHAIN_CH_STATE_UPDATE_CHAINS_REMOTE) {
         log_it(L_INFO, "Timeout fired before we sent the reply");
         s_sync_request_delete(l_sync_request);
         return;
     }
 
-    l_ch_chain->state = CHAIN_STATE_SYNC_CHAINS;
+    l_ch_chain->state = DAP_CHAIN_CH_STATE_SYNC_CHAINS;
     l_ch_chain->request_atom_iter = l_sync_request->chain.request_atom_iter;
 
     if (s_debug_more )
-        log_it(L_INFO,"Out: DAP_STREAM_CH_CHAIN_PKT_TYPE_FIRST_CHAIN");
+        log_it(L_INFO,"Out: DAP_CHAIN_CH_PKT_TYPE_FIRST_CHAIN");
 
-    dap_chain_ch_pkt_write_unsafe(l_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_FIRST_CHAIN,
+    dap_chain_ch_pkt_write_unsafe(l_ch, DAP_CHAIN_CH_PKT_TYPE_FIRST_CHAIN,
             l_ch_chain->request_hdr.net_id.uint64, l_ch_chain->request_hdr.chain_id.uint64,
             l_ch_chain->request_hdr.cell_id.uint64, &g_node_addr, sizeof(dap_chain_node_addr_t));
     DAP_DELETE(l_sync_request);
@@ -293,7 +344,7 @@ static void s_sync_out_chains_last_worker_callback(dap_worker_t *a_worker, void
         return;
     }
 
-    dap_chain_ch_t * l_ch_chain = DAP_STREAM_CH_CHAIN(l_ch);
+    dap_chain_ch_t * l_ch_chain = DAP_CHAIN_CH(l_ch);
     if (!l_ch_chain) {
         log_it(L_CRITICAL, "Channel without chain, dump it");
         s_sync_request_delete(l_sync_request);
@@ -303,13 +354,13 @@ static void s_sync_out_chains_last_worker_callback(dap_worker_t *a_worker, void
     // last packet
     dap_chain_ch_sync_request_t l_request = {};
     if (s_debug_more )
-        log_it(L_INFO,"Out: DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_CHAINS");
-    dap_chain_ch_pkt_write_unsafe(l_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_CHAINS,
+        log_it(L_INFO,"Out: DAP_CHAIN_CH_PKT_TYPE_SYNCED_CHAINS");
+    dap_chain_ch_pkt_write_unsafe(l_ch, DAP_CHAIN_CH_PKT_TYPE_SYNCED_CHAINS,
             l_sync_request->request_hdr.net_id.uint64, l_sync_request->request_hdr.chain_id.uint64,
             l_sync_request->request_hdr.cell_id.uint64, &l_request, sizeof(l_request));
     s_ch_chain_go_idle(l_ch_chain);
     if (l_ch_chain->callback_notify_packet_out)
-        l_ch_chain->callback_notify_packet_out(l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_CHAINS,
+        l_ch_chain->callback_notify_packet_out(l_ch_chain, DAP_CHAIN_CH_PKT_TYPE_SYNCED_CHAINS,
                                                 NULL, 0, l_ch_chain->callback_notify_arg);
     DAP_DELETE(l_sync_request);
 }
@@ -325,9 +376,9 @@ static bool s_sync_out_chains_proc_callback(void *a_arg)
 
     dap_chain_t * l_chain = dap_chain_find_by_id(l_sync_request->request_hdr.net_id, l_sync_request->request_hdr.chain_id);
     assert(l_chain);
-    l_sync_request->chain.request_atom_iter = l_chain->callback_atom_iter_create(l_chain, l_sync_request->request_hdr.cell_id, 1);
+    l_sync_request->chain.request_atom_iter = l_chain->callback_atom_iter_create(l_chain, l_sync_request->request_hdr.cell_id, NULL);
     size_t l_first_size = 0;
-    dap_chain_atom_ptr_t l_atom = l_chain->callback_atom_iter_get_first(l_sync_request->chain.request_atom_iter, &l_first_size);
+    dap_chain_atom_ptr_t l_atom = l_chain->callback_atom_iter_get(l_sync_request->chain.request_atom_iter, DAP_CHAIN_ITER_OP_FIRST, &l_first_size);
     if (l_atom && l_first_size) {
         // first packet
         dap_chain_hash_fast_t l_hash_from = l_sync_request->request.hash_from;
@@ -359,7 +410,7 @@ static void s_sync_out_gdb_first_worker_callback(dap_worker_t *a_worker, void *a
         return;
     }
 
-    dap_chain_ch_t *l_ch_chain = DAP_STREAM_CH_CHAIN( l_ch );
+    dap_chain_ch_t *l_ch_chain = DAP_CHAIN_CH( l_ch );
 
     if (!l_ch_chain) {
         log_it(L_CRITICAL, "Channel without chain, dump it");
@@ -367,21 +418,21 @@ static void s_sync_out_gdb_first_worker_callback(dap_worker_t *a_worker, void *a
         return;
     }
 
-    if (l_ch_chain->state != CHAIN_STATE_UPDATE_GLOBAL_DB_REMOTE) {
+    if (l_ch_chain->state != DAP_CHAIN_CH_STATE_UPDATE_GLOBAL_DB_REMOTE) {
         log_it(L_INFO, "Timeout fired before we sent the reply");
         s_sync_request_delete(l_sync_request);
         return;
     }
 
     // Add it to outgoing list
-    l_ch_chain->state = CHAIN_STATE_SYNC_GLOBAL_DB;
+    l_ch_chain->state = DAP_CHAIN_CH_STATE_SYNC_GLOBAL_DB;
     if (s_debug_more )
-        log_it(L_INFO,"Out: DAP_STREAM_CH_CHAIN_PKT_TYPE_FIRST_GLOBAL_DB");
-    dap_chain_ch_pkt_write_unsafe(DAP_STREAM_CH(l_ch_chain), DAP_STREAM_CH_CHAIN_PKT_TYPE_FIRST_GLOBAL_DB,
+        log_it(L_INFO,"Out: DAP_CHAIN_CH_PKT_TYPE_FIRST_GLOBAL_DB");
+    dap_chain_ch_pkt_write_unsafe(DAP_STREAM_CH(l_ch_chain), DAP_CHAIN_CH_PKT_TYPE_FIRST_GLOBAL_DB,
             l_ch_chain->request_hdr.net_id.uint64, l_ch_chain->request_hdr.chain_id.uint64,
             l_ch_chain->request_hdr.cell_id.uint64, &g_node_addr, sizeof(dap_chain_node_addr_t));
     if(l_ch_chain->callback_notify_packet_out)
-        l_ch_chain->callback_notify_packet_out(l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_FIRST_GLOBAL_DB,
+        l_ch_chain->callback_notify_packet_out(l_ch_chain, DAP_CHAIN_CH_PKT_TYPE_FIRST_GLOBAL_DB,
                                                 NULL, 0, l_ch_chain->callback_notify_arg);
 
     if( a_worker){ // We send NULL to prevent delete
@@ -404,7 +455,7 @@ static void s_sync_out_gdb_last_worker_callback(dap_worker_t *a_worker, void *a_
         return;
     }
 
-    dap_chain_ch_t *l_ch_chain = DAP_STREAM_CH_CHAIN( l_ch );
+    dap_chain_ch_t *l_ch_chain = DAP_CHAIN_CH( l_ch );
     if (!l_ch_chain) {
         log_it(L_CRITICAL, "Channel without chain, dump it");
         s_sync_request_delete(l_sync_request);
@@ -413,13 +464,13 @@ static void s_sync_out_gdb_last_worker_callback(dap_worker_t *a_worker, void *a_
     s_sync_out_gdb_first_worker_callback(NULL,a_arg); // NULL to say callback not to delete request
 
     if (s_debug_more )
-        log_it(L_INFO,"Out: DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB");
-    dap_chain_ch_pkt_write_unsafe(DAP_STREAM_CH(l_ch_chain), DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB,
+        log_it(L_INFO,"Out: DAP_CHAIN_CH_PKT_TYPE_SYNCED_GLOBAL_DB");
+    dap_chain_ch_pkt_write_unsafe(DAP_STREAM_CH(l_ch_chain), DAP_CHAIN_CH_PKT_TYPE_SYNCED_GLOBAL_DB,
                                          l_ch_chain->request_hdr.net_id.uint64, l_ch_chain->request_hdr.chain_id.uint64,
                                          l_ch_chain->request_hdr.cell_id.uint64, NULL, 0);
     s_ch_chain_go_idle(l_ch_chain);
     if(l_ch_chain->callback_notify_packet_out)
-        l_ch_chain->callback_notify_packet_out(l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB,
+        l_ch_chain->callback_notify_packet_out(l_ch_chain, DAP_CHAIN_CH_PKT_TYPE_SYNCED_GLOBAL_DB,
                                                 NULL, 0, l_ch_chain->callback_notify_arg);
     s_sync_request_delete(l_sync_request);
 }
@@ -441,7 +492,7 @@ static bool s_sync_out_gdb_proc_callback(void *a_arg)
         s_sync_request_delete(l_sync_request);
         return true;
     }
-    dap_chain_ch_t *l_ch_chain = DAP_STREAM_CH_CHAIN(l_ch);
+    dap_chain_ch_t *l_ch_chain = DAP_CHAIN_CH(l_ch);
     if (!l_ch_chain) {
         log_it(L_CRITICAL, "Channel without chain, dump it");
         s_sync_request_delete(l_sync_request);
@@ -478,11 +529,11 @@ static void s_sync_update_gdb_start_worker_callback(dap_worker_t *a_worker, void
         s_sync_request_delete(l_sync_request);
         return;
     }
-    dap_chain_ch_pkt_write_unsafe(l_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_START,
+    dap_chain_ch_pkt_write_unsafe(l_ch, DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB_START,
                                          l_sync_request->request_hdr.net_id.uint64, l_sync_request->request_hdr.chain_id.uint64,
                                          l_sync_request->request_hdr.cell_id.uint64, NULL, 0);
     if (s_debug_more)
-        log_it(L_INFO, "Out: DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_START for net_id 0x%016"DAP_UINT64_FORMAT_x" "
+        log_it(L_INFO, "Out: DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB_START for net_id 0x%016"DAP_UINT64_FORMAT_x" "
                        "chain_id 0x%016"DAP_UINT64_FORMAT_x" cell_id 0x%016"DAP_UINT64_FORMAT_x"",
                        l_sync_request->request_hdr.net_id.uint64, l_sync_request->request_hdr.chain_id.uint64, l_sync_request->request_hdr.cell_id.uint64);
     DAP_DELETE(l_sync_request);
@@ -504,13 +555,13 @@ static bool s_sync_update_gdb_proc_callback(void *a_arg)
         log_it(L_INFO, "Client disconnected before we sent the reply");
         DAP_DELETE(l_sync_request);
         return true;
-    } else if (!DAP_STREAM_CH_CHAIN(l_ch)) {
+    } else if (!DAP_CHAIN_CH(l_ch)) {
         log_it(L_CRITICAL, "Channel without chain, dump it");
         DAP_DELETE(l_sync_request);
         return true;
     }
 
-    dap_chain_ch_t *l_ch_chain = DAP_STREAM_CH_CHAIN(l_ch);
+    dap_chain_ch_t *l_ch_chain = DAP_CHAIN_CH(l_ch);
     int l_flags = 0;
     if (dap_chain_net_get_extra_gdb_group(l_net, l_sync_request->request.node_addr))
         l_flags |= F_DB_LOG_ADD_EXTRA_GROUPS;
@@ -519,7 +570,7 @@ static bool s_sync_update_gdb_proc_callback(void *a_arg)
     if (l_ch_chain->request_db_log != NULL)
         dap_db_log_list_delete(l_ch_chain->request_db_log);
     l_ch_chain->request_db_log = dap_db_log_list_start(l_net->pub.name, l_sync_request->request.node_addr.uint64, l_flags);
-    l_ch_chain->state = CHAIN_STATE_UPDATE_GLOBAL_DB;
+    l_ch_chain->state = DAP_CHAIN_CH_STATE_UPDATE_GLOBAL_DB;
     l_sync_request->gdb.db_log = l_ch_chain->request_db_log;
     l_sync_request->request.node_addr.uint64 = dap_chain_net_get_cur_addr_int(l_net);
      dap_worker_exec_callback_on(dap_events_worker_get(l_sync_request->worker->id), s_sync_update_gdb_start_worker_callback, l_sync_request);
@@ -532,6 +583,12 @@ static bool s_gdb_in_pkt_proc_callback(void *a_arg)
     return false;
 }
 
+struct atom_processing_args {
+    dap_stream_node_addr_t addr;
+    bool ack_req;
+    byte_t data[];
+};
+
 /**
  * @brief s_sync_in_chains_callback
  * @param a_thread dap_proc_thread_t
@@ -540,8 +597,10 @@ static bool s_gdb_in_pkt_proc_callback(void *a_arg)
  */
 static bool s_sync_in_chains_callback(void *a_arg)
 {
-    dap_chain_ch_pkt_t *l_chain_pkt = a_arg;
-    if (!l_chain_pkt || !l_chain_pkt->hdr.data_size) {
+    assert(a_arg);
+    struct atom_processing_args *l_args = a_arg;
+    dap_chain_ch_pkt_t *l_chain_pkt = (dap_chain_ch_pkt_t *)l_args->data;
+    if (!l_chain_pkt->hdr.data_size) {
         log_it(L_CRITICAL, "Proc thread received corrupted chain packet!");
         return false;
     }
@@ -549,8 +608,8 @@ static bool s_sync_in_chains_callback(void *a_arg)
     uint64_t l_atom_size = l_chain_pkt->hdr.data_size;
     dap_chain_t *l_chain = dap_chain_find_by_id(l_chain_pkt->hdr.net_id, l_chain_pkt->hdr.chain_id);
     if (!l_chain) {
-        debug_if(s_debug_more, L_WARNING, "No chain found for DAP_STREAM_CH_CHAIN_PKT_TYPE_CHAIN");
-        DAP_DELETE(l_chain_pkt);
+        debug_if(s_debug_more, L_WARNING, "No chain found for DAP_CHAIN_CH_PKT_TYPE_CHAIN");
+        DAP_DELETE(l_args);
         return false;
     }
     char *l_atom_hash_str = NULL;
@@ -561,19 +620,25 @@ static bool s_sync_in_chains_callback(void *a_arg)
     case ATOM_PASS:
         debug_if(s_debug_more, L_WARNING, "Atom with hash %s for %s:%s not accepted (code ATOM_PASS, already present)",
                                                 l_atom_hash_str, l_chain->net_name, l_chain->name);
-        //dap_chain_db_set_last_hash_remote(l_sync_request->request.node_addr.uint64, l_chain, &l_atom_hash);
         break;
     case ATOM_MOVE_TO_THRESHOLD:
         debug_if(s_debug_more, L_INFO, "Thresholded atom with hash %s for %s:%s", l_atom_hash_str, l_chain->net_name, l_chain->name);
-        //dap_chain_db_set_last_hash_remote(l_sync_request->request.node_addr.uint64, l_chain, &l_atom_hash);
         break;
     case ATOM_ACCEPT:
         debug_if(s_debug_more, L_INFO,"Accepted atom with hash %s for %s:%s", l_atom_hash_str, l_chain->net_name, l_chain->name);
-        int l_res = dap_chain_atom_save(l_chain->cells, l_atom, l_atom_size, NULL);
-        if(l_res < 0) {
+        if (dap_chain_atom_save(l_chain->cells, l_atom, l_atom_size, NULL) < 0)
             log_it(L_ERROR, "Can't save atom %s to the file", l_atom_hash_str);
-        } else {
-            //dap_chain_db_set_last_hash_remote(l_sync_request->request.node_addr.uint64, l_chain, &l_atom_hash);
+        if (l_args->ack_req) {
+            uint64_t l_ack_num = (l_chain_pkt->hdr.num_hi << 16) | l_chain_pkt->hdr.num_lo;
+            dap_chain_ch_pkt_t *l_pkt = dap_chain_ch_pkt_new(l_chain_pkt->hdr.net_id.uint64, l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
+                                                             &l_ack_num, sizeof(uint64_t));
+            dap_stream_ch_pkt_send_by_addr(&l_args->addr, DAP_CHAIN_CH_ID, DAP_CHAIN_CH_PKT_TYPE_CHAIN_ACK, l_pkt, dap_chain_ch_pkt_get_size(l_pkt));
+            DAP_DELETE(l_pkt);
+            debug_if(s_debug_more, L_DEBUG, "Out: CHAIN_ACK %s for net %s to destination " NODE_ADDR_FP_STR " with num %" DAP_UINT64_FORMAT_U,
+                                    l_chain ? l_chain->name : "(null)",
+                                                l_chain ? l_chain->net_name : "(null)",
+                                                                NODE_ADDR_FP_ARGS_S(l_args->addr),
+                                    l_ack_num);
         }
         break;
     case ATOM_REJECT: {
@@ -584,11 +649,11 @@ static bool s_sync_in_chains_callback(void *a_arg)
         log_it(L_CRITICAL, "Wtf is this ret code? %d", l_atom_add_res);
         break;
     }
-    DAP_DELETE(l_chain_pkt);
+    DAP_DELETE(l_args);
     return false;
 }
 
-static void s_gossip_payload_callback(void *a_payload, size_t a_payload_size, dap_stream_node_addr_t UNUSED_ARG a_sender_addr)
+static void s_gossip_payload_callback(void *a_payload, size_t a_payload_size, dap_stream_node_addr_t a_sender_addr)
 {
     assert(a_payload && a_payload_size);
     dap_chain_ch_pkt_t *l_chain_pkt = a_payload;
@@ -597,12 +662,15 @@ static void s_gossip_payload_callback(void *a_payload, size_t a_payload_size, da
         log_it(L_WARNING, "Incorrect chain GOSSIP packet size");
         return;
     }
-    dap_chain_ch_pkt_t *l_chain_pkt_copy = DAP_DUP_SIZE(a_payload, a_payload_size);
-    if (!l_chain_pkt_copy) {
-        log_it(L_CRITICAL, "Not enough memory");
+    struct atom_processing_args *l_args = DAP_NEW_SIZE(struct atom_processing_args, a_payload_size + sizeof(struct atom_processing_args));
+    if (!l_args) {
+        log_it(L_CRITICAL, g_error_memory_alloc);
         return;
     }
-    dap_proc_thread_callback_add(NULL, s_sync_in_chains_callback, l_chain_pkt_copy);
+    l_args->addr = a_sender_addr;
+    l_args->ack_req = false;
+    memcpy(l_args->data, a_payload, a_payload_size);
+    dap_proc_thread_callback_add(NULL, s_sync_in_chains_callback, l_args);
 }
 
 /**
@@ -615,14 +683,13 @@ static void s_gdb_in_pkt_error_worker_callback(dap_worker_t *a_worker, void *a_a
     struct sync_request *l_sync_request = (struct sync_request *) a_arg;
 
     dap_stream_ch_t *l_ch = dap_stream_ch_find_by_uuid_unsafe(DAP_STREAM_WORKER(a_worker), l_sync_request->ch_uuid);
-    if( l_ch == NULL ) {
+    if (l_ch == NULL)
         log_it(L_INFO,"Client disconnected before we sent the reply");
-    } else {
-        dap_chain_ch_pkt_write_error_unsafe(l_ch, l_sync_request->request_hdr.net_id.uint64,
-                                                   l_sync_request->request_hdr.chain_id.uint64,
-                                                   l_sync_request->request_hdr.cell_id.uint64,
-                                                   "%s : err %d", "ERROR_GLOBAL_DB_INTERNAL_NOT_SAVED", l_sync_request->last_err);
-    }
+    else
+        s_stream_ch_write_error_unsafe(l_ch, l_sync_request->request_hdr.net_id.uint64,
+                                           l_sync_request->request_hdr.chain_id.uint64,
+                                           l_sync_request->request_hdr.cell_id.uint64,
+                                           DAP_CHAIN_CH_ERROR_GLOBAL_DB_INTERNAL_NOT_SAVED);
     DAP_DELETE(l_sync_request);
 }
 
@@ -633,7 +700,7 @@ static void s_gdb_in_pkt_error_worker_callback(dap_worker_t *a_worker, void *a_a
  */
 struct sync_request *dap_chain_ch_create_sync_request(dap_chain_ch_pkt_t *a_chain_pkt, dap_stream_ch_t* a_ch)
 {
-    dap_chain_ch_t * l_ch_chain = DAP_STREAM_CH_CHAIN(a_ch);
+    dap_chain_ch_t * l_ch_chain = DAP_CHAIN_CH(a_ch);
     if (!l_ch_chain) {
         log_it(L_CRITICAL, "Channel without chain, dump it");
         return NULL;
@@ -653,15 +720,15 @@ struct sync_request *dap_chain_ch_create_sync_request(dap_chain_ch_pkt_t *a_chai
     return l_sync_request;
 }
 
-static void s_stream_ch_write_error_unsafe(dap_stream_ch_t *a_ch, uint64_t a_net_id, uint64_t a_chain_id, uint64_t a_cell_id, const char * a_err_string)
+static void s_stream_ch_write_error_unsafe(dap_stream_ch_t *a_ch, uint64_t a_net_id, uint64_t a_chain_id, uint64_t a_cell_id, dap_chain_ch_error_type_t a_error)
 {
-    dap_chain_ch_t *l_ch_chain = DAP_STREAM_CH_CHAIN(a_ch);
+    dap_chain_ch_t *l_ch_chain = DAP_CHAIN_CH(a_ch);
     if (!l_ch_chain) {
         log_it(L_CRITICAL, "Channel without chain, dump it");
         return;
     }
     s_ch_chain_go_idle(l_ch_chain);
-    dap_chain_ch_pkt_write_error_unsafe(a_ch, a_net_id, a_chain_id, a_cell_id, "%s", a_err_string);
+    dap_chain_ch_pkt_write_error_unsafe(a_ch, a_net_id, a_chain_id, a_cell_id, "%s", s_error_type_to_string(a_error));
 }
 
 static bool s_chain_timer_callback(void *a_arg)
@@ -672,32 +739,32 @@ static bool s_chain_timer_callback(void *a_arg)
         DAP_DELETE(a_arg);
         return false;
     }
-    dap_chain_ch_t *l_ch_chain = DAP_STREAM_CH_CHAIN(l_ch);
+    dap_chain_ch_t *l_ch_chain = DAP_CHAIN_CH(l_ch);
     if (!l_ch_chain) {
         log_it(L_CRITICAL, "Channel without chain, dump it");
         DAP_DELETE(a_arg);
         return false;
     }
-    if (l_ch_chain->timer_shots++ >= DAP_SYNC_TICKS_PER_SECOND * DAP_CHAIN_NODE_SYNC_TIMEOUT) {
+    if (l_ch_chain->timer_shots++ >= DAP_SYNC_TICKS_PER_SECOND * s_sync_timeout) {
         if (!s_ch_chain_get_idle(l_ch_chain)) {
             s_ch_chain_go_idle(l_ch_chain);
             if (l_ch_chain->callback_notify_packet_out)
-                l_ch_chain->callback_notify_packet_out(l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_TIMEOUT, NULL, 0,
+                l_ch_chain->callback_notify_packet_out(l_ch_chain, DAP_CHAIN_CH_PKT_TYPE_TIMEOUT, NULL, 0,
                                                       l_ch_chain->callback_notify_arg);
         }
         DAP_DELETE(a_arg);
         l_ch_chain->activity_timer = NULL;
         return false;
     }
-    if (l_ch_chain->state != CHAIN_STATE_WAITING && l_ch_chain->sent_breaks) {
+    if (l_ch_chain->state != DAP_CHAIN_CH_STATE_WAITING && l_ch_chain->sent_breaks) {
         s_stream_ch_packet_out(l_ch, a_arg);
         if (l_ch_chain->activity_timer == NULL)
             return false;
     }
     // Sending dumb packet with nothing to inform remote thats we're just skiping atoms of GDB's, nothing freezed
-    if (l_ch_chain->state == CHAIN_STATE_SYNC_CHAINS && l_ch_chain->sent_breaks >= 3 * DAP_SYNC_TICKS_PER_SECOND) {
+    if (l_ch_chain->state == DAP_CHAIN_CH_STATE_SYNC_CHAINS && l_ch_chain->sent_breaks >= 3 * DAP_SYNC_TICKS_PER_SECOND) {
         debug_if(s_debug_more, L_INFO, "Send one chain TSD packet");
-        dap_chain_ch_pkt_write_unsafe(l_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS_TSD,
+        dap_chain_ch_pkt_write_unsafe(l_ch, DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS_TSD,
                                              l_ch_chain->request_hdr.net_id.uint64, l_ch_chain->request_hdr.chain_id.uint64,
                                              l_ch_chain->request_hdr.cell_id.uint64, NULL, 0);
         l_ch_chain->sent_breaks = 0;
@@ -729,46 +796,245 @@ void dap_chain_ch_timer_start(dap_chain_ch_t *a_ch_chain)
  */
 void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
 {
-    dap_chain_ch_t * l_ch_chain = DAP_STREAM_CH_CHAIN(a_ch);
+    dap_chain_ch_t *l_ch_chain = DAP_CHAIN_CH(a_ch);
     if (!l_ch_chain || l_ch_chain->_inheritor != a_ch) {
         log_it(L_ERROR, "No chain in channel, returning");
         return;
     }
     dap_stream_ch_pkt_t * l_ch_pkt = (dap_stream_ch_pkt_t *) a_arg;
-    dap_chain_ch_pkt_t * l_chain_pkt = (dap_chain_ch_pkt_t *) l_ch_pkt->data;
-    if (!l_chain_pkt) {
-        log_it(L_ERROR, "No chain packet in channel packet, returning");
+    if (l_ch_pkt->hdr.data_size < sizeof(dap_chain_ch_pkt_t)) {
+        log_it(L_ERROR, "Corrupted packet: too small size %u, smaller then header size %zu",
+                                            l_ch_pkt->hdr.data_size, sizeof(dap_chain_ch_pkt_t));
         return;
     }
-    if (l_ch_pkt->hdr.data_size < sizeof (l_chain_pkt->hdr)){
-        log_it(L_ERROR, "Corrupted packet: too small size %u, smaller then header size %zu", l_ch_pkt->hdr.data_size,
-               sizeof(l_chain_pkt->hdr));
+
+    dap_chain_ch_pkt_t *l_chain_pkt = (dap_chain_ch_pkt_t *)l_ch_pkt->data;
+    size_t l_chain_pkt_data_size = l_ch_pkt->hdr.data_size - sizeof(l_chain_pkt->hdr);
+
+    if (l_chain_pkt->hdr.version > DAP_CHAIN_CH_PKT_VERSION) {
+        debug_if(s_debug_more, L_ATT, "Unsupported protocol version %d, current version %d",
+                 l_chain_pkt->hdr.version, DAP_CHAIN_CH_PKT_VERSION);
         return;
     }
-    if (l_chain_pkt->hdr.version != DAP_STREAM_CH_CHAIN_PKT_VERSION) {
-        debug_if(s_debug_more, L_ATT, "Unsupported protocol version %d, current version %d",
-                 l_chain_pkt->hdr.version, DAP_STREAM_CH_CHAIN_PKT_VERSION);
+    if (l_chain_pkt->hdr.version >= 2 &&
+                l_chain_pkt_data_size != l_chain_pkt->hdr.data_size) {
+        log_it(L_WARNING, "Incorrect chain packet size %zu, expected %u",
+                            l_chain_pkt_data_size, l_chain_pkt->hdr.data_size);
         return;
     }
-    size_t l_chain_pkt_data_size = l_ch_pkt->hdr.data_size - sizeof(l_chain_pkt->hdr);
 
     s_chain_timer_reset(l_ch_chain);
+
     switch (l_ch_pkt->hdr.type) {
+
+    /* *** New synchronization protocol *** */
+
+    case DAP_CHAIN_CH_PKT_TYPE_ERROR:{
+        char * l_error_str = (char*)l_chain_pkt->data;
+        if(l_chain_pkt_data_size>1)
+            l_error_str[l_chain_pkt_data_size-1]='\0'; // To be sure that nobody sends us garbage
+                                                       // without trailing zero
+        log_it(L_WARNING, "In: from remote addr %s chain id 0x%016"DAP_UINT64_FORMAT_x" got error on his side: '%s'",
+               DAP_STREAM_CH(l_ch_chain)->stream->esocket->remote_addr_str,
+               l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt_data_size ? l_error_str : "<empty>");
+    } break;
+
+    case DAP_CHAIN_CH_PKT_TYPE_CHAIN: {
+        dap_cluster_t *l_cluster = dap_cluster_find(dap_cluster_guuid_compose(l_chain_pkt->hdr.net_id.uint64, 0));
+        if (!l_cluster) {
+            log_it(L_WARNING, "Can't find cluster with ID 0x%" DAP_UINT64_FORMAT_X, l_chain_pkt->hdr.net_id.uint64);
+            s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
+                    l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
+                    DAP_CHAIN_CH_ERROR_CHAIN_NOT_FOUND);
+            break;
+        }
+        dap_cluster_member_t *l_check = dap_cluster_member_find_unsafe(l_cluster, &a_ch->stream->node);
+        if (!l_check) {
+            log_it(L_WARNING, "Node with addr "NODE_ADDR_FP_STR" isn't a member of cluster %s",
+                                        NODE_ADDR_FP_ARGS_S(a_ch->stream->node), l_cluster->mnemonim);
+            break;
+        }
+        struct atom_processing_args *l_args = DAP_NEW_SIZE(struct atom_processing_args, l_ch_pkt->hdr.data_size + sizeof(struct atom_processing_args));
+        if (!l_args) {
+            log_it(L_CRITICAL, g_error_memory_alloc);
+            return;
+        }
+        l_args->addr = a_ch->stream->node;
+        l_args->ack_req = true;
+        if (l_chain_pkt->hdr.version < 2)
+            l_chain_pkt->hdr.data_size = l_chain_pkt_data_size;
+        memcpy(l_args->data, l_chain_pkt, l_ch_pkt->hdr.data_size);
+        if (s_debug_more) {
+            char *l_atom_hash_str;
+            dap_get_data_hash_str_static(l_chain_pkt->data, l_chain_pkt_data_size, l_atom_hash_str);
+            log_it(L_INFO, "In: CHAIN pkt: atom hash %s (size %zd)", l_atom_hash_str, l_chain_pkt_data_size);
+        }
+        dap_proc_thread_callback_add(a_ch->stream_worker->worker->proc_queue_input, s_sync_in_chains_callback, l_args);
+    } break;
+
+    case DAP_CHAIN_CH_PKT_TYPE_CHAIN_REQ: {
+        if (l_chain_pkt_data_size != sizeof(dap_hash_fast_t)) {
+            log_it(L_WARNING, "DAP_CHAIN_CH_PKT_TYPE_CHAIN_REQ: Wrong chain packet size %zd when expected %zd",
+                                                                            l_chain_pkt_data_size, sizeof(dap_hash_fast_t));
+            s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
+                    l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
+                    DAP_CHAIN_CH_ERROR_CHAIN_PKT_DATA_SIZE);
+            break;
+        }
+        if (s_debug_more)
+            log_it(L_INFO, "In: CHAIN_REQ pkt: net 0x%016" DAP_UINT64_FORMAT_x " chain 0x%016" DAP_UINT64_FORMAT_x " cell 0x%016" DAP_UINT64_FORMAT_x,
+                            l_chain_pkt->hdr.net_id.uint64, l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64);
+        if (l_ch_chain->sync_context) {
+            log_it(L_WARNING, "Can't process CHAIN_REQ request cause already busy with syncronization");
+            dap_chain_ch_pkt_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
+                    l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
+                    DAP_CHAIN_CH_ERROR_SYNC_REQUEST_ALREADY_IN_PROCESS);
+            break;
+        }
+        dap_chain_t *l_chain = dap_chain_find_by_id(l_chain_pkt->hdr.net_id, l_chain_pkt->hdr.chain_id);
+        if (!l_chain) {
+            log_it(L_WARNING, "Not found chain id 0x%016" DAP_UINT64_FORMAT_x " with net id 0x%016" DAP_UINT64_FORMAT_x,
+                                                        l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.net_id.uint64);
+            s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
+                    l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
+                    DAP_CHAIN_CH_ERROR_CHAIN_NOT_FOUND);
+            break;
+        }
+        dap_hash_fast_t *l_requested_hash = (dap_hash_fast_t *)l_chain_pkt->data;
+        if (dap_hash_fast_is_blank(l_requested_hash))
+            l_requested_hash = NULL;
+        dap_chain_atom_iter_t *l_iter = l_chain->callback_atom_iter_create(l_chain, l_chain_pkt->hdr.cell_id, l_requested_hash);
+        if (!l_requested_hash)
+            l_chain->callback_atom_iter_get(l_iter, DAP_CHAIN_ITER_OP_FIRST, NULL);
+        if (l_iter->cur) {
+            dap_chain_ch_summary_t l_sum = { .num_cur = l_requested_hash ? l_iter->cur_num : 0, .num_last = l_chain->callback_count_atom(l_chain) };
+            if (l_sum.num_last - l_sum.num_cur) {
+                dap_chain_ch_pkt_write_unsafe(a_ch, DAP_CHAIN_CH_PKT_TYPE_CHAIN_SUMMARY,
+                                                l_chain_pkt->hdr.net_id.uint64, l_chain_pkt->hdr.chain_id.uint64,
+                                                l_chain_pkt->hdr.cell_id.uint64, &l_sum, sizeof(l_sum));
+                debug_if(s_debug_more, L_DEBUG, "Out: CHAIN_SUMMARY %s for net %s to destination " NODE_ADDR_FP_STR,
+                                        l_chain ? l_chain->name : "(null)",
+                                                    l_chain ? l_chain->net_name : "(null)",
+                                                                    NODE_ADDR_FP_ARGS_S(a_ch->stream->node));
+                struct sync_context *l_context = DAP_NEW_Z(struct sync_context);
+                l_context->addr = a_ch->stream->node;
+                l_context->iter = l_iter;
+                l_context->net_id = l_chain_pkt->hdr.net_id;
+                l_context->chain_id = l_chain_pkt->hdr.chain_id;
+                l_context->cell_id = l_chain_pkt->hdr.cell_id;
+                l_context->num_last = l_sum.num_last;
+                l_context->last_activity = dap_time_now();
+                atomic_store_explicit(&l_context->state, SYNC_STATE_READY, memory_order_relaxed);
+                atomic_store(&l_context->allowed_num, l_sum.num_cur + s_sync_ack_window_size);
+                dap_proc_thread_callback_add(a_ch->stream_worker->worker->proc_queue_input, s_chain_iter_callback, l_context);
+                l_ch_chain->sync_context = l_context;
+                l_ch_chain->sync_timer = dap_timerfd_start_on_worker(a_ch->stream_worker->worker, 1000, s_sync_timer_callback, l_ch_chain);
+                break;
+            }
+        } else
+            debug_if(s_debug_more, L_DEBUG, "Requested atom with hash %s not found", dap_hash_fast_to_str_static(l_requested_hash));
+        dap_chain_ch_pkt_write_unsafe(a_ch, DAP_CHAIN_CH_PKT_TYPE_SYNCED_CHAIN,
+                                      l_chain_pkt->hdr.net_id.uint64, l_chain_pkt->hdr.chain_id.uint64,
+                                      l_chain_pkt->hdr.cell_id.uint64, NULL, 0);
+        debug_if(s_debug_more, L_DEBUG, "Out: SYNCED_CHAIN %s for net %s to destination " NODE_ADDR_FP_STR,
+                                l_chain ? l_chain->name : "(null)",
+                                            l_chain ? l_chain->net_name : "(null)",
+                                                            NODE_ADDR_FP_ARGS_S(a_ch->stream->node));
+        l_chain->callback_atom_iter_delete(l_iter);
+    } break;
+
+    case DAP_CHAIN_CH_PKT_TYPE_CHAIN_SUMMARY: {
+        if (l_chain_pkt_data_size != sizeof(dap_chain_ch_summary_t)) {
+            log_it(L_WARNING, "DAP_CHAIN_CH_PKT_TYPE_CHAIN_SUMMARY: Wrong chain packet size %zd when expected %zd",
+                                                                            l_chain_pkt_data_size, sizeof(dap_chain_ch_summary_t));
+            s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
+                    l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
+                    DAP_CHAIN_CH_ERROR_CHAIN_PKT_DATA_SIZE);
+            break;
+        }
+        dap_chain_t *l_chain = dap_chain_find_by_id(l_chain_pkt->hdr.net_id, l_chain_pkt->hdr.chain_id);
+        dap_chain_ch_summary_t *l_sum = (dap_chain_ch_summary_t *)l_chain_pkt->data;
+        debug_if(s_debug_more, L_DEBUG, "In: CHAIN_SUMMARY of %s for net %s from source " NODE_ADDR_FP_STR
+                                            " with %" DAP_UINT64_FORMAT_U " atoms to sync from %" DAP_UINT64_FORMAT_U " to %" DAP_UINT64_FORMAT_U,
+                                l_chain ? l_chain->name : "(null)",
+                                            l_chain ? l_chain->net_name : "(null)",
+                                                            NODE_ADDR_FP_ARGS_S(a_ch->stream->node),
+                                l_sum->num_last - l_sum->num_cur, l_sum->num_cur, l_sum->num_last);
+    } break;
+
+    case DAP_CHAIN_CH_PKT_TYPE_CHAIN_ACK: {
+        if (l_chain_pkt_data_size != sizeof(uint64_t)) {
+            log_it(L_WARNING, "DAP_CHAIN_CH_PKT_TYPE_CHAIN_ACK: Wrong chain packet size %zd when expected %zd",
+                                                                            l_chain_pkt_data_size, sizeof(uint64_t));
+            s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
+                    l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
+                    DAP_CHAIN_CH_ERROR_CHAIN_PKT_DATA_SIZE);
+            break;
+        }
+        uint64_t l_ack_num = *(uint64_t *)l_chain_pkt->data;
+        dap_chain_t *l_chain = dap_chain_find_by_id(l_chain_pkt->hdr.net_id, l_chain_pkt->hdr.chain_id);
+        debug_if(s_debug_more, L_DEBUG, "In: CHAIN_ACK %s for net %s from source " NODE_ADDR_FP_STR " with num %" DAP_UINT64_FORMAT_U,
+                                l_chain ? l_chain->name : "(null)",
+                                            l_chain ? l_chain->net_name : "(null)",
+                                                            NODE_ADDR_FP_ARGS_S(a_ch->stream->node),
+                                l_ack_num);
+        struct sync_context *l_context = l_ch_chain->sync_context;
+        if (!l_context) {
+            log_it(L_WARNING, "CHAIN_ACK: No active sync context");
+            s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
+                    l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
+                    DAP_CHAIN_CH_ERROR_INCORRECT_SYNC_SEQUENCE);
+            break;
+        }
+        if (l_context->num_last == l_ack_num) {
+            dap_chain_ch_pkt_write_unsafe(a_ch, DAP_CHAIN_CH_PKT_TYPE_SYNCED_CHAIN,
+                                                 l_chain_pkt->hdr.net_id.uint64, l_chain_pkt->hdr.chain_id.uint64,
+                                                 l_chain_pkt->hdr.cell_id.uint64, NULL, 0);
+            s_ch_chain_go_idle(l_ch_chain);
+            break;
+        }
+        l_context->last_activity = dap_time_now();
+        if (atomic_load_explicit(&l_context->state, memory_order_relaxed) == SYNC_STATE_OVER)
+            break;
+        atomic_store_explicit(&l_context->allowed_num,
+                              dap_min(l_ack_num + s_sync_ack_window_size, l_context->num_last),
+                              memory_order_release);
+        if (atomic_exchange(&l_context->state, SYNC_STATE_READY) == SYNC_STATE_IDLE)
+            dap_proc_thread_callback_add(a_ch->stream_worker->worker->proc_queue_input, s_chain_iter_callback, l_context);
+    } break;
+
+    case DAP_CHAIN_CH_PKT_TYPE_SYNCED_CHAIN: {
+        dap_chain_t *l_chain = dap_chain_find_by_id(l_chain_pkt->hdr.net_id, l_chain_pkt->hdr.chain_id);
+        log_it(L_INFO, "In: SYNCED_CHAIN %s for net %s from source " NODE_ADDR_FP_STR,
+                    l_chain ? l_chain->name : "(null)",
+                                l_chain ? l_chain->net_name : "(null)",
+                                                NODE_ADDR_FP_ARGS_S(a_ch->stream->node));
+    } break;
+
+    default:
+        s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
+                                            l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
+                                            DAP_CHAIN_CH_ERROR_UNKNOWN_CHAIN_PKT_TYPE);
+
+//    }
+//}
+
+    /* *** Legacy *** */
+
         /// --- GDB update ---
         // Request for gdbs list update
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_REQ:{
-            if(l_ch_chain->state != CHAIN_STATE_IDLE){
+        case DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB_REQ:{
+            if(l_ch_chain->state != DAP_CHAIN_CH_STATE_IDLE){
                 log_it(L_WARNING, "Can't process UPDATE_GLOBAL_DB_REQ request because its already busy with syncronization");
                 dap_chain_ch_pkt_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
                         l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
-                        "ERROR_SYNC_REQUEST_ALREADY_IN_PROCESS");
+                        DAP_CHAIN_CH_ERROR_SYNC_REQUEST_ALREADY_IN_PROCESS);
                 break;
             }
             log_it(L_INFO, "In:  UPDATE_GLOBAL_DB_REQ pkt: net 0x%016"DAP_UINT64_FORMAT_x" chain 0x%016"DAP_UINT64_FORMAT_x" cell 0x%016"DAP_UINT64_FORMAT_x,
                             l_chain_pkt->hdr.net_id.uint64, l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64);
             if (l_chain_pkt_data_size == (size_t)sizeof(dap_chain_ch_sync_request_t))
                 l_ch_chain->request = *(dap_chain_ch_sync_request_t*)l_chain_pkt->data;
-            l_ch_chain->request.id_start = 0;
             struct sync_request *l_sync_request = dap_chain_ch_create_sync_request(l_chain_pkt, a_ch);
             l_ch_chain->stats_request_gdb_processed = 0;
             l_ch_chain->request_hdr = l_chain_pkt->hdr;
@@ -776,37 +1042,37 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
         } break;
 
         // Response with metadata organized in TSD
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_TSD: {
+        case DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB_TSD: {
             if (s_debug_more)
                 log_it(L_DEBUG, "Global DB TSD packet detected");
         } break;
 
         // If requested - begin to recieve record's hashes
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_START:{
+        case DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB_START:{
             if (s_debug_more)
                 log_it(L_INFO, "In:  UPDATE_GLOBAL_DB_START pkt net 0x%016"DAP_UINT64_FORMAT_x" chain 0x%016"DAP_UINT64_FORMAT_x" cell 0x%016"DAP_UINT64_FORMAT_x,
                                 l_chain_pkt->hdr.net_id.uint64, l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64);
-            if (l_ch_chain->state != CHAIN_STATE_IDLE){
+            if (l_ch_chain->state != DAP_CHAIN_CH_STATE_IDLE){
                 log_it(L_WARNING, "Can't process UPDATE_GLOBAL_DB_START request because its already busy with syncronization");
                 dap_chain_ch_pkt_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
                         l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
-                        "ERROR_SYNC_REQUEST_ALREADY_IN_PROCESS");
+                        DAP_CHAIN_CH_ERROR_SYNC_REQUEST_ALREADY_IN_PROCESS);
                 break;
             }
             l_ch_chain->request_hdr = l_chain_pkt->hdr;
-            l_ch_chain->state = CHAIN_STATE_UPDATE_GLOBAL_DB_REMOTE;
+            l_ch_chain->state = DAP_CHAIN_CH_STATE_UPDATE_GLOBAL_DB_REMOTE;
         } break;
         // Response with gdb element hashes and sizes
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB:{
+        case DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB:{
             if(s_debug_more)
                 log_it(L_INFO, "In: UPDATE_GLOBAL_DB pkt data_size=%zu", l_chain_pkt_data_size);
-            if (l_ch_chain->state != CHAIN_STATE_UPDATE_GLOBAL_DB_REMOTE ||
+            if (l_ch_chain->state != DAP_CHAIN_CH_STATE_UPDATE_GLOBAL_DB_REMOTE ||
                     memcmp(&l_ch_chain->request_hdr.net_id, &l_chain_pkt->hdr.net_id,
                            sizeof(dap_chain_net_id_t) + sizeof(dap_chain_id_t) + sizeof(dap_chain_cell_id_t))) {
                 log_it(L_WARNING, "Can't process UPDATE_GLOBAL_DB request because its already busy with syncronization");
                 s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
                         l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
-                        "ERROR_SYNC_REQUEST_ALREADY_IN_PROCESS");
+                        DAP_CHAIN_CH_ERROR_SYNC_REQUEST_ALREADY_IN_PROCESS);
                 break;
             }
             for ( dap_chain_ch_update_element_t * l_element =(dap_chain_ch_update_element_t *) l_chain_pkt->data;
@@ -836,15 +1102,15 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
             }
         } break;
         // End of response with starting of DB sync
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_END: {
+        case DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB_END: {
             if(l_chain_pkt_data_size == sizeof(dap_chain_ch_sync_request_t)) {
-                if (l_ch_chain->state != CHAIN_STATE_UPDATE_GLOBAL_DB_REMOTE ||
+                if (l_ch_chain->state != DAP_CHAIN_CH_STATE_UPDATE_GLOBAL_DB_REMOTE ||
                         memcmp(&l_ch_chain->request_hdr.net_id, &l_chain_pkt->hdr.net_id,
                                sizeof(dap_chain_net_id_t) + sizeof(dap_chain_id_t) + sizeof(dap_chain_cell_id_t))) {
                     log_it(L_WARNING, "Can't process UPDATE_GLOBAL_DB_END request because its already busy with syncronization");
                     s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
                             l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
-                            "ERROR_SYNC_REQUEST_ALREADY_IN_PROCESS");
+                            DAP_CHAIN_CH_ERROR_SYNC_REQUEST_ALREADY_IN_PROCESS);
                     break;
                 }
                 debug_if(s_debug_more, L_INFO, "In: UPDATE_GLOBAL_DB_END pkt with total count %d hashes",
@@ -854,14 +1120,14 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                 struct sync_request *l_sync_request = dap_chain_ch_create_sync_request(l_chain_pkt, a_ch);
                 dap_proc_thread_callback_add(a_ch->stream_worker->worker->proc_queue_input, s_sync_out_gdb_proc_callback, l_sync_request);
             } else {
-                log_it(L_WARNING, "DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_END: Wrong chain packet size %zd when expected %zd", l_chain_pkt_data_size, sizeof(l_ch_chain->request));
+                log_it(L_WARNING, "DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB_END: Wrong chain packet size %zd when expected %zd", l_chain_pkt_data_size, sizeof(l_ch_chain->request));
                 s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
                         l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
-                        "ERROR_CHAIN_PKT_DATA_SIZE" );
+                        DAP_CHAIN_CH_ERROR_CHAIN_PKT_DATA_SIZE);
             }
         } break;
         // first packet of data with source node address
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_FIRST_GLOBAL_DB: {
+        case DAP_CHAIN_CH_PKT_TYPE_FIRST_GLOBAL_DB: {
             if(l_chain_pkt_data_size == (size_t)sizeof(dap_chain_node_addr_t)){
                l_ch_chain->request.node_addr = *(dap_chain_node_addr_t*)l_chain_pkt->data;
                l_ch_chain->stats_request_gdb_processed = 0;
@@ -869,14 +1135,14 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                               " from address "NODE_ADDR_FP_STR, l_chain_pkt_data_size,   l_chain_pkt->hdr.net_id.uint64 ,
                               l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64, NODE_ADDR_FP_ARGS_S(l_ch_chain->request.node_addr) );
             }else {
-               log_it(L_WARNING,"Incorrect data size %zu in packet DAP_STREAM_CH_CHAIN_PKT_TYPE_FIRST_GLOBAL_DB", l_chain_pkt_data_size);
+               log_it(L_WARNING,"Incorrect data size %zu in packet DAP_CHAIN_CH_PKT_TYPE_FIRST_GLOBAL_DB", l_chain_pkt_data_size);
                s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
                        l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
-                       "ERROR_CHAIN_PACKET_TYPE_FIRST_GLOBAL_DB_INCORRET_DATA_SIZE");
+                       DAP_CHAIN_CH_ERROR_CHAIN_PKT_DATA_SIZE);
             }
         } break;
 
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_GLOBAL_DB: {
+        case DAP_CHAIN_CH_PKT_TYPE_GLOBAL_DB: {
             if(s_debug_more)
                 log_it(L_INFO, "In: GLOBAL_DB data_size=%zu", l_chain_pkt_data_size);
             // get transaction and save it to global_db
@@ -890,29 +1156,29 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                 log_it(L_WARNING, "Packet with GLOBAL_DB atom has zero body size");
                 s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
                         l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
-                        "ERROR_GLOBAL_DB_PACKET_EMPTY");
+                        DAP_CHAIN_CH_ERROR_CHAIN_PKT_DATA_SIZE);
             }
         }  break;
 
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB: {
+        case DAP_CHAIN_CH_PKT_TYPE_SYNCED_GLOBAL_DB: {
                 log_it(L_INFO, "In:  SYNCED_GLOBAL_DB: net 0x%016"DAP_UINT64_FORMAT_x" chain 0x%016"DAP_UINT64_FORMAT_x" cell 0x%016"DAP_UINT64_FORMAT_x,
                                 l_chain_pkt->hdr.net_id.uint64, l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64);
                 if (!l_ch_chain->callback_notify_packet_in) { // we haven't node client waitng, so reply to other side
                     dap_chain_ch_sync_request_t l_sync_gdb = {};
                     l_sync_gdb.node_addr.uint64 = g_node_addr.uint64;
-                    dap_chain_ch_pkt_write_unsafe(a_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_REQ, l_chain_pkt->hdr.net_id.uint64,
+                    dap_chain_ch_pkt_write_unsafe(a_ch, DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB_REQ, l_chain_pkt->hdr.net_id.uint64,
                                                   l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64, &l_sync_gdb, sizeof(l_sync_gdb));
                 }
         } break;
 
         /// --- Chains update ---
         // Request for atoms list update
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS_REQ:{
-            if (l_ch_chain->state != CHAIN_STATE_IDLE) {
+        case DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS_REQ:{
+            if (l_ch_chain->state != DAP_CHAIN_CH_STATE_IDLE) {
                 log_it(L_WARNING, "Can't process UPDATE_CHAINS_REQ request because its already busy with syncronization");
-                dap_chain_ch_pkt_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
+                s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
                         l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
-                        "ERROR_SYNC_REQUEST_ALREADY_IN_PROCESS");
+                        DAP_CHAIN_CH_ERROR_SYNC_REQUEST_ALREADY_IN_PROCESS);
                 break;
             }
             if(s_debug_more)
@@ -920,31 +1186,31 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                                 l_chain_pkt->hdr.net_id.uint64, l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64);
             dap_chain_t * l_chain = dap_chain_find_by_id(l_chain_pkt->hdr.net_id, l_chain_pkt->hdr.chain_id);
             if (l_chain) {
-                l_ch_chain->state = CHAIN_STATE_UPDATE_CHAINS;
+                l_ch_chain->state = DAP_CHAIN_CH_STATE_UPDATE_CHAINS;
                 if(s_debug_more)
                     log_it(L_INFO, "Out: UPDATE_CHAINS_START pkt: net %s chain %s cell 0x%016"DAP_UINT64_FORMAT_X, l_chain->name,
                                         l_chain->net_name, l_chain_pkt->hdr.cell_id.uint64);
-                l_ch_chain->request_atom_iter = l_chain->callback_atom_iter_create(l_chain, l_chain_pkt->hdr.cell_id, 1);
-                l_chain->callback_atom_iter_get_first(l_ch_chain->request_atom_iter, NULL);
+                l_ch_chain->request_atom_iter = l_chain->callback_atom_iter_create(l_chain, l_chain_pkt->hdr.cell_id, NULL);
+                l_chain->callback_atom_iter_get(l_ch_chain->request_atom_iter, DAP_CHAIN_ITER_OP_FIRST, NULL);
                 l_ch_chain->request_hdr = l_chain_pkt->hdr;
-                dap_chain_ch_pkt_write_unsafe(a_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS_START,
+                dap_chain_ch_pkt_write_unsafe(a_ch, DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS_START,
                                                      l_chain_pkt->hdr.net_id.uint64,l_chain_pkt->hdr.chain_id.uint64,
                                                      l_chain_pkt->hdr.cell_id.uint64, NULL, 0);
             }
         } break;
         // Response with metadata organized in TSD
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS_TSD :{
+        case DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS_TSD :{
             if (s_debug_more)
                 log_it(L_DEBUG, "Chain TSD packet detected");
         } break;
 
         // If requested - begin to send atom hashes
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS_START:{
-            if (l_ch_chain->state != CHAIN_STATE_IDLE) {
+        case DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS_START:{
+            if (l_ch_chain->state != DAP_CHAIN_CH_STATE_IDLE) {
                 log_it(L_WARNING, "Can't process UPDATE_CHAINS_START request because its already busy with syncronization");
                 dap_chain_ch_pkt_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
                         l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
-                        "ERROR_SYNC_REQUEST_ALREADY_IN_PROCESS");
+                        DAP_CHAIN_CH_ERROR_SYNC_REQUEST_ALREADY_IN_PROCESS);
                 break;
             }
             dap_chain_t * l_chain = dap_chain_find_by_id(l_chain_pkt->hdr.net_id, l_chain_pkt->hdr.chain_id);
@@ -955,18 +1221,18 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                                 l_chain_pkt->hdr.cell_id.uint64);
                 s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
                                                     l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
-                                                    "ERROR_NET_INVALID_ID");
+                                                    DAP_CHAIN_CH_ERROR_NET_INVALID_ID);
                 // Who are you? I don't know you! go away!
                 a_ch->stream->esocket->flags |= DAP_SOCK_SIGNAL_CLOSE;
                 break;
             }
-            l_ch_chain->state = CHAIN_STATE_UPDATE_CHAINS_REMOTE;
+            l_ch_chain->state = DAP_CHAIN_CH_STATE_UPDATE_CHAINS_REMOTE;
             l_ch_chain->request_hdr = l_chain_pkt->hdr;
             debug_if(s_debug_more, L_INFO, "In: UPDATE_CHAINS_START pkt");
         } break;
 
         // Response with atom hashes and sizes
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS: {
+        case DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS: {
             unsigned int l_count_added=0;
             unsigned int l_count_total=0;
 
@@ -979,7 +1245,7 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                                 l_chain_pkt->hdr.cell_id.uint64);
                 s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
                                                     l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
-                                                    "ERROR_NET_INVALID_ID");
+                                                    DAP_CHAIN_CH_ERROR_NET_INVALID_ID);
                 // Who are you? I don't know you! go away!
                 a_ch->stream->esocket->flags |= DAP_SOCK_SIGNAL_CLOSE;
                 break;
@@ -1016,15 +1282,15 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                 log_it(L_INFO,"In: Added %u from %u remote atom hash  in list",l_count_added,l_count_total);
         } break;
 
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS_END: {
+        case DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS_END: {
             if(l_chain_pkt_data_size == sizeof(dap_chain_ch_sync_request_t)) {
-                if (l_ch_chain->state != CHAIN_STATE_UPDATE_CHAINS_REMOTE ||
+                if (l_ch_chain->state != DAP_CHAIN_CH_STATE_UPDATE_CHAINS_REMOTE ||
                         memcmp(&l_ch_chain->request_hdr.net_id, &l_chain_pkt->hdr.net_id,
                                sizeof(dap_chain_net_id_t) + sizeof(dap_chain_id_t) + sizeof(dap_chain_cell_id_t))) {
                     log_it(L_WARNING, "Can't process UPDATE_CHAINS_END request because its already busy with syncronization");
                     s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
                             l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
-                            "ERROR_SYNC_REQUEST_ALREADY_IN_PROCESS");
+                            DAP_CHAIN_CH_ERROR_SYNC_REQUEST_ALREADY_IN_PROCESS);
                     break;
                 }
                 dap_chain_t * l_chain = dap_chain_find_by_id(l_chain_pkt->hdr.net_id, l_chain_pkt->hdr.chain_id);
@@ -1035,7 +1301,7 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                                     l_chain_pkt->hdr.cell_id.uint64);
                     s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
                                                         l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
-                                                        "ERROR_NET_INVALID_ID");
+                                                        DAP_CHAIN_CH_ERROR_NET_INVALID_ID);
                     break;
                 }
                 debug_if(s_debug_more, L_INFO, "In: UPDATE_CHAINS_END pkt with total count %d hashes",
@@ -1045,15 +1311,15 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                 l_ch_chain->request_hdr = l_chain_pkt->hdr;
                 dap_proc_thread_callback_add(a_ch->stream_worker->worker->proc_queue_input, s_sync_out_chains_proc_callback, l_sync_request);
             } else {
-                log_it(L_WARNING, "DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS_END: Wrong chain packet size %zd when expected %zd",
+                log_it(L_WARNING, "DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS_END: Wrong chain packet size %zd when expected %zd",
                        l_chain_pkt_data_size, sizeof(l_ch_chain->request));
                 s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
                         l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
-                        "ERROR_CHAIN_PKT_DATA_SIZE" );
+                        DAP_CHAIN_CH_ERROR_CHAIN_PKT_DATA_SIZE);
             }
         } break;
         // first packet of data with source node address
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_FIRST_CHAIN: {
+        case DAP_CHAIN_CH_PKT_TYPE_FIRST_CHAIN: {
             if(l_chain_pkt_data_size == (size_t)sizeof(dap_chain_node_addr_t)){
                 l_ch_chain->request_hdr = l_chain_pkt->hdr;
                 l_ch_chain->request.node_addr = *(dap_chain_node_addr_t*)l_chain_pkt->data;
@@ -1062,53 +1328,19 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                                l_chain_pkt_data_size, l_ch_chain->request_hdr.net_id.uint64 ,
                                l_ch_chain->request_hdr.chain_id.uint64, l_ch_chain->request_hdr.cell_id.uint64);
             }else{
-                log_it(L_WARNING,"Incorrect data size %zd in packet DAP_STREAM_CH_CHAIN_PKT_TYPE_FIRST_CHAIN", l_chain_pkt_data_size);
+                log_it(L_WARNING,"Incorrect data size %zd in packet DAP_CHAIN_CH_PKT_TYPE_FIRST_CHAIN", l_chain_pkt_data_size);
                 s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
                         l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
-                        "ERROR_CHAIN_PACKET_TYPE_FIRST_CHAIN_INCORRET_DATA_SIZE");
+                        DAP_CHAIN_CH_ERROR_CHAIN_PKT_DATA_SIZE);
             }
         } break;
 
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_CHAIN: {
-            dap_chain_ch_pkt_t *l_chain_pkt = (dap_chain_ch_pkt_t *)l_ch_pkt->data;
-            if (l_chain_pkt->hdr.version >= 2 &&
-                        l_chain_pkt_data_size != l_chain_pkt->hdr.data_size) {
-                log_it(L_WARNING, "Incorrect chain packet size");
-                break;
-            }
-            dap_cluster_t *l_cluster = dap_cluster_find(dap_cluster_guuid_compose(l_chain_pkt->hdr.net_id.uint64, 0));
-            if (!l_cluster) {
-                log_it(L_WARNING, "Can't find cluster with ID 0x%" DAP_UINT64_FORMAT_X, l_chain_pkt->hdr.net_id.uint64);
-                break;
-            }
-            dap_cluster_member_t *l_check = dap_cluster_member_find_unsafe(l_cluster, &a_ch->stream->node);
-            if (!l_check) {
-                log_it(L_WARNING, "Node with addr "NODE_ADDR_FP_STR" isn't a member of cluster %s",
-                                            NODE_ADDR_FP_ARGS_S(a_ch->stream->node), l_cluster->mnemonim);
-                break;
-            }
-            dap_chain_ch_pkt_t *l_chain_pkt_copy = DAP_DUP_SIZE(l_ch_pkt->data, l_ch_pkt->hdr.data_size);
-            if (!l_chain_pkt_copy) {
-                log_it(L_CRITICAL, "Not enough memory");
-                break;
-            }
-            if (l_chain_pkt_copy->hdr.version < 2)
-                l_chain_pkt_copy->hdr.data_size = l_chain_pkt_data_size;
-            if (s_debug_more) {
-                char *l_atom_hash_str;
-                dap_get_data_hash_str_static(l_chain_pkt->data, l_chain_pkt_data_size, l_atom_hash_str);
-                log_it(L_INFO, "In: CHAIN pkt: atom hash %s (size %zd)", l_atom_hash_str, l_chain_pkt_data_size);
-            }
-            dap_proc_thread_callback_add(a_ch->stream_worker->worker->proc_queue_input, s_sync_in_chains_callback, l_chain_pkt_copy);
-        } break;
-
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_CHAINS: {
+        case DAP_CHAIN_CH_PKT_TYPE_SYNCED_CHAINS: {
             if (dap_log_level_get() <= L_INFO) {
-                dap_chain_hash_fast_t l_hash_from = l_ch_chain->request.hash_from,
-                        l_hash_to = l_ch_chain->request.hash_to;
+                dap_chain_hash_fast_t l_hash_from = l_ch_chain->request.hash_from;
                 char l_hash_from_str[DAP_CHAIN_HASH_FAST_STR_SIZE] = { '\0' }, l_hash_to_str[DAP_CHAIN_HASH_FAST_STR_SIZE] = { '\0' };
                 dap_chain_hash_fast_to_str(&l_hash_from, l_hash_from_str, DAP_CHAIN_HASH_FAST_STR_SIZE);
-                dap_chain_hash_fast_to_str(&l_hash_to, l_hash_to_str, DAP_CHAIN_HASH_FAST_STR_SIZE);
+                dap_chain_hash_fast_to_str(&c_dap_chain_addr_blank.data.hash_fast, l_hash_to_str, DAP_CHAIN_HASH_FAST_STR_SIZE);
                 log_it(L_INFO, "In:  SYNCED_CHAINS: between %s and %s",l_hash_from_str[0] ? l_hash_from_str : "(null)",
                        l_hash_to_str[0] ? l_hash_to_str: "(null)");
 
@@ -1127,47 +1359,92 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                                     l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64);
                     s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
                                                         l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
-                                                        "ERROR_NET_INVALID_ID");
+                                                        DAP_CHAIN_CH_ERROR_NET_INVALID_ID);
                     break;
                 }
                 if (s_debug_more) {
                     log_it(L_INFO, "Out: UPDATE_CHAINS_REQ pkt");
                 }
                 dap_chain_ch_sync_request_t l_request= {};
-                dap_chain_ch_pkt_write_unsafe(a_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS_REQ, l_chain_pkt->hdr.net_id.uint64,
+                dap_chain_ch_pkt_write_unsafe(a_ch, DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS_REQ, l_chain_pkt->hdr.net_id.uint64,
                                               l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64, &l_request, sizeof(l_request));
             }
         } break;
-
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_ERROR:{
-            char * l_error_str = (char*)l_chain_pkt->data;
-            if(l_chain_pkt_data_size>1)
-                l_error_str[l_chain_pkt_data_size-1]='\0'; // To be sure that nobody sends us garbage
-                                                           // without trailing zero
-            log_it(L_WARNING,"In from remote addr %s chain id 0x%016"DAP_UINT64_FORMAT_x" got error on his side: '%s'",
-                   DAP_STREAM_CH(l_ch_chain)->stream->esocket->remote_addr_str,
-                   l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt_data_size ? l_error_str : "<empty>");
-        } break;
-
-        default: {
-            s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
-                                                l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64,
-                                                "ERROR_UNKNOWN_CHAIN_PKT_TYPE");
-            }
     }
     if(l_ch_chain->callback_notify_packet_in)
         l_ch_chain->callback_notify_packet_in(l_ch_chain, l_ch_pkt->hdr.type, l_chain_pkt,
                                               l_chain_pkt_data_size, l_ch_chain->callback_notify_arg);
 }
 
+static bool s_sync_timer_callback(void *a_arg)
+{
+    dap_chain_ch_t *l_ch_chain = a_arg;
+    struct sync_context *l_context = l_ch_chain->sync_context;
+    if (l_context->last_activity + s_sync_timeout <= dap_time_now()) {
+        l_ch_chain->sync_timer = NULL;
+        s_ch_chain_go_idle(l_ch_chain);
+        return false;
+    }
+    return true;
+}
 
-/**
- * @brief s_ch_chain_go_idle_and_free_log_list
- * @param a_ch_chain
- */
-static void s_free_log_list_gdb ( dap_chain_ch_t * a_ch_chain)
+static bool s_chain_iter_callback(void *a_arg)
 {
+    assert(a_arg);
+    struct sync_context *l_context = a_arg;
+    dap_chain_atom_iter_t *l_iter = l_context->iter;
+    assert(l_iter);
+    dap_chain_t *l_chain = l_iter->chain;
+    if (atomic_exchange(&l_context->state, SYNC_STATE_BUSY) == SYNC_STATE_OVER) {
+        atomic_store(&l_context->state, SYNC_STATE_OVER);
+        return false;
+    }
+    size_t l_atom_size = l_iter->cur_size;
+    dap_chain_atom_ptr_t l_atom = l_iter->cur;
+    uint32_t l_cycles_count = 0;
+    while (l_atom && l_atom_size) {
+        if (l_iter->cur_num > atomic_load_explicit(&l_context->allowed_num, memory_order_acquire))
+            break;
+        dap_chain_ch_pkt_t *l_pkt = dap_chain_ch_pkt_new(l_context->net_id.uint64, l_context->chain_id.uint64, l_context->cell_id.uint64,
+                                                         l_atom, l_atom_size);
+        // For master format binary complience
+        l_pkt->hdr.num_lo = l_iter->cur_num & 0xFFFF;
+        l_pkt->hdr.num_hi = (l_iter->cur_num >> 16) & 0xFF;
+        dap_stream_ch_pkt_send_by_addr(&l_context->addr, DAP_CHAIN_CH_ID, DAP_CHAIN_CH_PKT_TYPE_CHAIN, l_pkt, dap_chain_ch_pkt_get_size(l_pkt));
+        DAP_DELETE(l_pkt);
+        debug_if(s_debug_more, L_DEBUG, "Out: CHAIN %s for net %s to destination " NODE_ADDR_FP_STR " with num %" DAP_UINT64_FORMAT_U
+                                            " hash %s and size %zu",
+                                l_chain ? l_chain->name : "(null)",
+                                            l_chain ? l_chain->net_name : "(null)",
+                                                            NODE_ADDR_FP_ARGS_S(l_context->addr),
+                                l_iter->cur_num, dap_hash_fast_to_str_static(l_iter->cur_hash), l_iter->cur_size);
+        l_atom = l_chain->callback_atom_iter_get(l_iter, DAP_CHAIN_ITER_OP_NEXT, &l_atom_size);
+        if (!l_atom || !l_atom_size || l_iter->cur_num > l_context->num_last)
+            break;
+        if (atomic_exchange(&l_context->state, SYNC_STATE_BUSY) == SYNC_STATE_OVER) {
+            atomic_store(&l_context->state, SYNC_STATE_OVER);
+            return false;
+        }
+        if (++l_cycles_count >= s_sync_packets_per_thread_call)
+            return true;
+    }
+    uint16_t l_state = l_atom && l_atom_size && l_iter->cur_num <= l_context->num_last
+                ? SYNC_STATE_IDLE : SYNC_STATE_OVER;
+    uint16_t l_prev_state = atomic_exchange(&l_context->state, l_state);
+    if (l_prev_state == SYNC_STATE_OVER && l_state != SYNC_STATE_OVER)
+        atomic_store(&l_context->state, SYNC_STATE_OVER);
+    if (l_prev_state == SYNC_STATE_READY)   // Allowed num was changed since last state updating
+        return true;
+    return false;
+}
 
+static bool s_chain_iter_delete_callback(void *a_arg)
+{
+    struct sync_context *l_context = a_arg;
+    assert(l_context->iter);
+    l_context->iter->chain->callback_atom_iter_delete(l_context->iter);
+    DAP_DELETE(l_context);
+    return false;
 }
 
 /**
@@ -1176,13 +1453,26 @@ static void s_free_log_list_gdb ( dap_chain_ch_t * a_ch_chain)
  */
 static void s_ch_chain_go_idle(dap_chain_ch_t *a_ch_chain)
 {
-    if (a_ch_chain->state == CHAIN_STATE_IDLE) {
+    // New protocol
+    if (a_ch_chain->sync_context) {
+        atomic_store(&((struct sync_context *)a_ch_chain->sync_context)->state, SYNC_STATE_OVER);
+        dap_proc_thread_callback_add(DAP_STREAM_CH(a_ch_chain)->stream_worker->worker->proc_queue_input,
+                                     s_chain_iter_delete_callback, a_ch_chain->sync_context);
+        a_ch_chain->sync_context = NULL;
+    }
+    if (a_ch_chain->sync_timer) {
+        dap_timerfd_delete_unsafe(a_ch_chain->sync_timer);
+        a_ch_chain->sync_timer = NULL;
+    }
+//}
+    // Legacy
+    if (a_ch_chain->state == DAP_CHAIN_CH_STATE_IDLE) {
         return;
     }
-    a_ch_chain->state = CHAIN_STATE_IDLE;
+    a_ch_chain->state = DAP_CHAIN_CH_STATE_IDLE;
 
     if(s_debug_more)
-        log_it(L_INFO, "Go in CHAIN_STATE_IDLE");
+        log_it(L_INFO, "Go in DAP_CHAIN_CH_STATE_IDLE");
 
     // Cleanup after request
     memset(&a_ch_chain->request, 0, sizeof(a_ch_chain->request));
@@ -1202,7 +1492,6 @@ static void s_ch_chain_go_idle(dap_chain_ch_t *a_ch_chain)
     }
     a_ch_chain->remote_atoms = NULL;
     a_ch_chain->sent_breaks = 0;
-    s_free_log_list_gdb(a_ch_chain);
 }
 
 struct chain_io_complete {
@@ -1233,14 +1522,14 @@ static void s_stream_ch_io_complete(dap_events_socket_t *a_es, void *a_arg)
         return;
     dap_stream_ch_t *l_ch = NULL;
     for (size_t i = 0; i < l_stream->channel_count; i++)
-        if (l_stream->channel[i]->proc->id == DAP_STREAM_CH_CHAIN_ID)
+        if (l_stream->channel[i]->proc->id == DAP_CHAIN_CH_ID)
             l_ch = l_stream->channel[i];
-    if (!l_ch || !DAP_STREAM_CH_CHAIN(l_ch))
+    if (!l_ch || !DAP_CHAIN_CH(l_ch))
         return;
     if (a_arg) {
         struct chain_io_complete *l_arg = (struct chain_io_complete *)a_arg;
-        if (DAP_STREAM_CH_CHAIN(l_ch)->state == CHAIN_STATE_WAITING)
-            DAP_STREAM_CH_CHAIN(l_ch)->state = l_arg->state;
+        if (DAP_CHAIN_CH(l_ch)->state == DAP_CHAIN_CH_STATE_WAITING)
+            DAP_CHAIN_CH(l_ch)->state = l_arg->state;
         dap_chain_ch_pkt_write_unsafe(l_ch, l_arg->type, l_arg->net_id, l_arg->chain_id,
                                              l_arg->cell_id, l_arg->data, l_arg->data_size);
         a_es->callbacks.arg = NULL;
@@ -1260,8 +1549,8 @@ static void s_stream_ch_chain_pkt_write(dap_stream_ch_t *a_ch, uint8_t a_type, u
     if (l_free_buf_size < a_data_size) {
         struct chain_io_complete *l_arg = DAP_NEW_Z_SIZE(struct chain_io_complete, sizeof(struct chain_io_complete) + a_data_size);
         l_arg->ch_uuid = a_ch->uuid;
-        l_arg->state = DAP_STREAM_CH_CHAIN(a_ch)->state;
-        DAP_STREAM_CH_CHAIN(a_ch)->state = CHAIN_STATE_WAITING;
+        l_arg->state = DAP_CHAIN_CH(a_ch)->state;
+        DAP_CHAIN_CH(a_ch)->state = DAP_CHAIN_CH_STATE_WAITING;
         l_arg->type = a_type;
         l_arg->net_id = a_net_id;
         l_arg->chain_id = a_chain_id;
@@ -1281,7 +1570,7 @@ static void s_stream_ch_chain_pkt_write(dap_stream_ch_t *a_ch, uint8_t a_type, u
  */
 static bool s_stream_ch_packet_out(dap_stream_ch_t *a_ch, void *a_arg)
 {
-    dap_chain_ch_t *l_ch_chain = DAP_STREAM_CH_CHAIN(a_ch);
+    dap_chain_ch_t *l_ch_chain = DAP_CHAIN_CH(a_ch);
     if (!l_ch_chain) {
         log_it(L_CRITICAL, "Channel without chain, dump it");
         s_ch_chain_go_idle(l_ch_chain);
@@ -1290,7 +1579,7 @@ static bool s_stream_ch_packet_out(dap_stream_ch_t *a_ch, void *a_arg)
     bool l_go_idle = false, l_was_sent_smth = false;
     switch (l_ch_chain->state) {
         // Update list of global DB records to remote
-    case CHAIN_STATE_UPDATE_GLOBAL_DB: {
+    case DAP_CHAIN_CH_STATE_UPDATE_GLOBAL_DB: {
 #if 0
         size_t i, q =
                 // s_update_pack_size;
@@ -1305,24 +1594,24 @@ static bool s_stream_ch_packet_out(dap_stream_ch_t *a_ch, void *a_arg)
         }
         if (i) {
             l_was_sent_smth = true;
-            s_stream_ch_chain_pkt_write(a_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB,
+            s_stream_ch_chain_pkt_write(a_ch, DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB,
                                         l_ch_chain->request_hdr.net_id.uint64, l_ch_chain->request_hdr.chain_id.uint64,
                                         l_ch_chain->request_hdr.cell_id.uint64,
                                         l_data, i * sizeof(dap_chain_ch_update_element_t));
             l_ch_chain->stats_request_gdb_processed += i;
             DAP_DELETE(l_data);
             DAP_DELETE(l_objs);
-            debug_if(s_debug_more, L_INFO, "Out: DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB, %zu records", i);
+            debug_if(s_debug_more, L_INFO, "Out: DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB, %zu records", i);
         } else if (!l_objs) {
             l_was_sent_smth = true;
             l_ch_chain->request.node_addr.uint64 = dap_chain_net_get_cur_addr_int(dap_chain_net_by_id(
                                                                                       l_ch_chain->request_hdr.net_id));
-            s_stream_ch_chain_pkt_write(a_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_END,
+            s_stream_ch_chain_pkt_write(a_ch, DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB_END,
                                                  l_ch_chain->request_hdr.net_id.uint64,
                                                  l_ch_chain->request_hdr.chain_id.uint64,
                                                  l_ch_chain->request_hdr.cell_id.uint64,
                                                  &l_ch_chain->request, sizeof(dap_chain_ch_sync_request_t));
-            debug_if(s_debug_more, L_INFO, "Out: DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_END");
+            debug_if(s_debug_more, L_INFO, "Out: DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB_END");
             l_go_idle = true;
         }
             dap_chain_ch_update_element_t l_data[s_update_pack_size];
@@ -1339,31 +1628,31 @@ static bool s_stream_ch_packet_out(dap_stream_ch_t *a_ch, void *a_arg)
             }
             if (i) {
                 l_was_sent_smth = true;
-                s_stream_ch_chain_pkt_write(a_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB,
+                s_stream_ch_chain_pkt_write(a_ch, DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB,
                                             l_ch_chain->request_hdr.net_id.uint64, l_ch_chain->request_hdr.chain_id.uint64,
                                             l_ch_chain->request_hdr.cell_id.uint64,
                                             l_data, i * sizeof(dap_chain_ch_update_element_t));
                 l_ch_chain->stats_request_gdb_processed += i;
                 if (s_debug_more)
-                    log_it(L_INFO, "Out: DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB");
+                    log_it(L_INFO, "Out: DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB");
             } else if (!l_obj) {
                 l_was_sent_smth = true;
                 l_ch_chain->request.node_addr.uint64 = dap_chain_net_get_cur_addr_int(dap_chain_net_by_id(
                                                                                           l_ch_chain->request_hdr.net_id));
-                s_stream_ch_chain_pkt_write(a_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_END,
+                s_stream_ch_chain_pkt_write(a_ch, DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB_END,
                                                      l_ch_chain->request_hdr.net_id.uint64,
                                                      l_ch_chain->request_hdr.chain_id.uint64,
                                                      l_ch_chain->request_hdr.cell_id.uint64,
                                                      &l_ch_chain->request, sizeof(dap_chain_ch_sync_request_t));
                 if (s_debug_more )
-                    log_it(L_INFO, "Out: DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_END");
+                    log_it(L_INFO, "Out: DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB_END");
                 l_go_idle = true;
             }
 #endif
         } break;
 
         // Synchronize GDB
-    case CHAIN_STATE_SYNC_GLOBAL_DB: {
+    case DAP_CHAIN_CH_STATE_SYNC_GLOBAL_DB: {
 #if 0
         dap_global_db_pkt_t *l_pkt = NULL;
         size_t l_pkt_size = 0, i, q = 0;
@@ -1393,7 +1682,7 @@ static bool s_stream_ch_packet_out(dap_stream_ch_t *a_ch, void *a_arg)
         if (l_pkt_size) {
             l_was_sent_smth = true;
             // If request was from defined node_addr we update its state
-            s_stream_ch_chain_pkt_write(a_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_GLOBAL_DB,
+            s_stream_ch_chain_pkt_write(a_ch, DAP_CHAIN_CH_PKT_TYPE_GLOBAL_DB,
                                         l_ch_chain->request_hdr.net_id.uint64, l_ch_chain->request_hdr.chain_id.uint64,
                                         l_ch_chain->request_hdr.cell_id.uint64, l_pkt, l_pkt_size);
             debug_if(s_debug_more, L_INFO, "Send one global_db packet, size %zu, rest %zu/%zu items", l_pkt_size,
@@ -1405,12 +1694,12 @@ static bool s_stream_ch_packet_out(dap_stream_ch_t *a_ch, void *a_arg)
             l_was_sent_smth = true;
             // last message
             dap_chain_ch_sync_request_t l_request = { };
-            s_stream_ch_chain_pkt_write(a_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB,
+            s_stream_ch_chain_pkt_write(a_ch, DAP_CHAIN_CH_PKT_TYPE_SYNCED_GLOBAL_DB,
                                         l_ch_chain->request_hdr.net_id.uint64, l_ch_chain->request_hdr.chain_id.uint64,
                                         l_ch_chain->request_hdr.cell_id.uint64, &l_request, sizeof(l_request));
             l_go_idle = true;
             if (l_ch_chain->callback_notify_packet_out)
-                l_ch_chain->callback_notify_packet_out(l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB,
+                l_ch_chain->callback_notify_packet_out(l_ch_chain, DAP_CHAIN_CH_PKT_TYPE_SYNCED_GLOBAL_DB,
                                                        NULL, 0, l_ch_chain->callback_notify_arg);
             log_it(L_INFO,"Syncronized database: items syncronyzed %"DAP_UINT64_FORMAT_U" of %zu",
                     l_ch_chain->stats_request_gdb_processed, l_ch_chain->request_db_log->items_number);
@@ -1464,7 +1753,7 @@ static bool s_stream_ch_packet_out(dap_stream_ch_t *a_ch, void *a_arg)
                     log_it(L_INFO, "Send one global_db packet len=%zu (rest=%zu/%zu items)", l_pkt_size,
                                     dap_db_log_list_get_count_rest(l_ch_chain->request_db_log),
                                     dap_db_log_list_get_count(l_ch_chain->request_db_log));
-                s_stream_ch_chain_pkt_write(a_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_GLOBAL_DB,
+                s_stream_ch_chain_pkt_write(a_ch, DAP_CHAIN_CH_PKT_TYPE_GLOBAL_DB,
                                                      l_ch_chain->request_hdr.net_id.uint64, l_ch_chain->request_hdr.chain_id.uint64,
                                                      l_ch_chain->request_hdr.cell_id.uint64, l_pkt, l_pkt_size);
                 DAP_DELETE(l_pkt);
@@ -1474,19 +1763,19 @@ static bool s_stream_ch_packet_out(dap_stream_ch_t *a_ch, void *a_arg)
                         l_ch_chain->stats_request_gdb_processed, dap_db_log_list_get_count(l_ch_chain->request_db_log));
                 // last message
                 dap_chain_ch_sync_request_t l_request = {};
-                s_stream_ch_chain_pkt_write(a_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB,
+                s_stream_ch_chain_pkt_write(a_ch, DAP_CHAIN_CH_PKT_TYPE_SYNCED_GLOBAL_DB,
                                                      l_ch_chain->request_hdr.net_id.uint64, l_ch_chain->request_hdr.chain_id.uint64,
                                                      l_ch_chain->request_hdr.cell_id.uint64, &l_request, sizeof(l_request));
                 l_go_idle = true;
                 if (l_ch_chain->callback_notify_packet_out)
-                    l_ch_chain->callback_notify_packet_out(l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB,
+                    l_ch_chain->callback_notify_packet_out(l_ch_chain, DAP_CHAIN_CH_PKT_TYPE_SYNCED_GLOBAL_DB,
                                                            NULL, 0, l_ch_chain->callback_notify_arg);
             }
 #endif
     } break;
 
         // Update list of atoms to remote
-        case CHAIN_STATE_UPDATE_CHAINS:{
+        case DAP_CHAIN_CH_STATE_UPDATE_CHAINS:{
             dap_chain_ch_update_element_t *l_data = DAP_NEW_Z_SIZE(dap_chain_ch_update_element_t,
                                                                           sizeof(dap_chain_ch_update_element_t) * s_update_pack_size);
             size_t l_data_size=0;
@@ -1495,13 +1784,13 @@ static bool s_stream_ch_packet_out(dap_stream_ch_t *a_ch, void *a_arg)
                 // Shift offset counter
                 l_data_size += sizeof(dap_chain_ch_update_element_t);
                 // Then get next atom
-                l_ch_chain->request_atom_iter->chain->callback_atom_iter_get_next(l_ch_chain->request_atom_iter, NULL);
+                l_ch_chain->request_atom_iter->chain->callback_atom_iter_get(l_ch_chain->request_atom_iter, DAP_CHAIN_ITER_OP_NEXT, NULL);
             }
             if (l_data_size){
                 l_was_sent_smth = true;
                 if(s_debug_more)
                     log_it(L_DEBUG,"Out: UPDATE_CHAINS with %zu hashes sent", l_data_size / sizeof(dap_chain_ch_update_element_t));
-                s_stream_ch_chain_pkt_write(a_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS,
+                s_stream_ch_chain_pkt_write(a_ch, DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS,
                                                      l_ch_chain->request_hdr.net_id.uint64,
                                                      l_ch_chain->request_hdr.chain_id.uint64,
                                                      l_ch_chain->request_hdr.cell_id.uint64,
@@ -1512,7 +1801,7 @@ static bool s_stream_ch_packet_out(dap_stream_ch_t *a_ch, void *a_arg)
                 if(s_debug_more)
                     log_it(L_INFO,"Out: UPDATE_CHAINS_END sent ");
                 dap_chain_ch_sync_request_t l_request = {};
-                s_stream_ch_chain_pkt_write(a_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS_END,
+                s_stream_ch_chain_pkt_write(a_ch, DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS_END,
                                                      l_ch_chain->request_hdr.net_id.uint64,
                                                      l_ch_chain->request_hdr.chain_id.uint64,
                                                      l_ch_chain->request_hdr.cell_id.uint64,
@@ -1524,7 +1813,7 @@ static bool s_stream_ch_packet_out(dap_stream_ch_t *a_ch, void *a_arg)
         }break;
 
         // Synchronize chains
-        case CHAIN_STATE_SYNC_CHAINS: {
+        case DAP_CHAIN_CH_STATE_SYNC_CHAINS: {
             // Process one chain from l_ch_chain->request_atom_iter
             // Pack loop to skip quicker
             for(uint_fast16_t k=0; k<s_skip_in_reactor_count     &&
@@ -1555,7 +1844,7 @@ static bool s_stream_ch_packet_out(dap_stream_ch_t *a_ch, void *a_arg)
                         dap_chain_hash_fast_to_str(&l_hash_item->hash, l_atom_hash_str, sizeof(l_atom_hash_str));
                         log_it(L_INFO, "Out CHAIN pkt: atom hash %s (size %zd) ", l_atom_hash_str, l_ch_chain->request_atom_iter->cur_size);
                     }
-                    s_stream_ch_chain_pkt_write(a_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_CHAIN, l_ch_chain->request_hdr.net_id.uint64,
+                    s_stream_ch_chain_pkt_write(a_ch, DAP_CHAIN_CH_PKT_TYPE_CHAIN, l_ch_chain->request_hdr.net_id.uint64,
                                                          l_ch_chain->request_hdr.chain_id.uint64, l_ch_chain->request_hdr.cell_id.uint64,
                                                          l_ch_chain->request_atom_iter->cur, l_ch_chain->request_atom_iter->cur_size);
                     l_was_sent_smth = true;
@@ -1567,7 +1856,7 @@ static bool s_stream_ch_packet_out(dap_stream_ch_t *a_ch, void *a_arg)
                                          l_hash_item);
                 }
                 // Then get next atom and populate new last
-                l_ch_chain->request_atom_iter->chain->callback_atom_iter_get_next(l_ch_chain->request_atom_iter, NULL);
+                l_ch_chain->request_atom_iter->chain->callback_atom_iter_get(l_ch_chain->request_atom_iter, DAP_CHAIN_ITER_OP_NEXT, NULL);
                 if (l_was_sent_smth)
                     break;
             }
@@ -1575,13 +1864,13 @@ static bool s_stream_ch_packet_out(dap_stream_ch_t *a_ch, void *a_arg)
                 dap_chain_ch_sync_request_t l_request = {};
                 // last message
                 l_was_sent_smth = true;
-                s_stream_ch_chain_pkt_write(a_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_CHAINS,
+                s_stream_ch_chain_pkt_write(a_ch, DAP_CHAIN_CH_PKT_TYPE_SYNCED_CHAINS,
                                                      l_ch_chain->request_hdr.net_id.uint64, l_ch_chain->request_hdr.chain_id.uint64,
                                                      l_ch_chain->request_hdr.cell_id.uint64, &l_request, sizeof(l_request));
                 log_it( L_INFO,"Synced: %"DAP_UINT64_FORMAT_U" atoms processed", l_ch_chain->stats_request_atoms_processed);
                 l_go_idle = true;
                 if (l_ch_chain->callback_notify_packet_out)
-                    l_ch_chain->callback_notify_packet_out(l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_CHAINS, NULL,
+                    l_ch_chain->callback_notify_packet_out(l_ch_chain, DAP_CHAIN_CH_PKT_TYPE_SYNCED_CHAINS, NULL,
                                                            0, l_ch_chain->callback_notify_arg);
             }
         } break;
diff --git a/modules/chain/dap_chain_ch_pkt.c b/modules/chain/dap_chain_ch_pkt.c
index a42b35d439..9149522a97 100644
--- a/modules/chain/dap_chain_ch_pkt.c
+++ b/modules/chain/dap_chain_ch_pkt.c
@@ -35,7 +35,7 @@ size_t dap_chain_ch_pkt_write_unsafe(dap_stream_ch_t *a_ch, uint8_t a_type, uint
                                             const void * a_data, size_t a_data_size)
 {
     dap_chain_ch_pkt_t *l_chain_pkt = dap_chain_ch_pkt_new(a_net_id, a_chain_id, a_cell_id, a_data, a_data_size);
-    size_t l_ret = dap_stream_ch_pkt_write_unsafe(a_ch, a_type, l_chain_pkt, sizeof(dap_chain_ch_pkt_hdr_t) + a_data_size);
+    size_t l_ret = dap_stream_ch_pkt_write_unsafe(a_ch, a_type, l_chain_pkt, dap_chain_ch_pkt_get_size(l_chain_pkt));
     DAP_DELETE(l_chain_pkt);
     return l_ret;
 }
@@ -47,10 +47,11 @@ dap_chain_ch_pkt_t *dap_chain_ch_pkt_new(uint64_t a_net_id, uint64_t a_chain_id,
     dap_chain_ch_pkt_t *l_chain_pkt = DAP_NEW_Z_SIZE(dap_chain_ch_pkt_t, l_chain_pkt_size);
     if (l_chain_pkt) {
         *l_chain_pkt = (dap_chain_ch_pkt_t) {
-                .hdr = { .version = DAP_STREAM_CH_CHAIN_PKT_VERSION,
+                .hdr = { .version = DAP_CHAIN_CH_PKT_VERSION,
                          .data_size = a_data_size,
                          .net_id.uint64 = a_net_id,
-                         .cell_id.uint64 = a_cell_id, .chain_id.uint64 = a_chain_id }
+                         .cell_id.uint64 = a_cell_id,
+                         .chain_id.uint64 = a_chain_id }
         };
         if (a_data_size && a_data)
             memcpy(l_chain_pkt->data, a_data, a_data_size);
@@ -79,7 +80,7 @@ size_t dap_chain_ch_pkt_write_mt(dap_stream_worker_t *a_worker, dap_stream_ch_uu
             ? DAP_NEW_Z_SIZE(dap_chain_ch_pkt_t, l_chain_pkt_size)
             : DAP_NEW_STACK_SIZE(dap_chain_ch_pkt_t, l_chain_pkt_size);
     *l_chain_pkt = (dap_chain_ch_pkt_t){
-            .hdr = { .version = DAP_STREAM_CH_CHAIN_PKT_VERSION, .net_id.uint64 = a_net_id, .cell_id.uint64 = a_cell_id, .chain_id.uint64 = a_chain_id }
+            .hdr = { .version = DAP_CHAIN_CH_PKT_VERSION, .net_id.uint64 = a_net_id, .cell_id.uint64 = a_cell_id, .chain_id.uint64 = a_chain_id }
     };
 
     if (a_data_size && a_data)
@@ -100,7 +101,7 @@ size_t dap_chain_ch_pkt_write_multi_mt(dap_stream_ch_cachet_t *a_links, size_t a
             ? DAP_NEW_Z_SIZE(dap_chain_ch_pkt_t, l_chain_pkt_size)
             : DAP_NEW_STACK_SIZE(dap_chain_ch_pkt_t, l_chain_pkt_size);
     *l_chain_pkt = (dap_chain_ch_pkt_t){
-            .hdr = { .version = DAP_STREAM_CH_CHAIN_PKT_VERSION, .net_id.uint64 = a_net_id, .cell_id.uint64 = a_cell_id, .chain_id.uint64 = a_chain_id }
+            .hdr = { .version = DAP_CHAIN_CH_PKT_VERSION, .net_id.uint64 = a_net_id, .cell_id.uint64 = a_cell_id, .chain_id.uint64 = a_chain_id }
     };
 
     if (a_data_size && a_data)
@@ -141,7 +142,7 @@ size_t dap_chain_ch_pkt_write_inter(dap_events_socket_t * a_es_input, dap_stream
     size_t l_chain_pkt_size = sizeof(dap_chain_ch_pkt_hdr_t) + a_data_size;
     dap_chain_ch_pkt_t *l_chain_pkt = DAP_NEW_Z_SIZE(dap_chain_ch_pkt_t, l_chain_pkt_size );
     *l_chain_pkt = (dap_chain_ch_pkt_t){
-            .hdr = { .version = DAP_STREAM_CH_CHAIN_PKT_VERSION, .net_id.uint64 = a_net_id, .cell_id.uint64 = a_cell_id, .chain_id.uint64 = a_chain_id }
+            .hdr = { .version = DAP_CHAIN_CH_PKT_VERSION, .net_id.uint64 = a_net_id, .cell_id.uint64 = a_cell_id, .chain_id.uint64 = a_chain_id }
     };
 
     if (a_data_size && a_data)
diff --git a/modules/chain/include/dap_chain.h b/modules/chain/include/dap_chain.h
index 5b0ad6af2c..59237d9605 100644
--- a/modules/chain/include/dap_chain.h
+++ b/modules/chain/include/dap_chain.h
@@ -45,13 +45,12 @@ typedef const void * dap_chain_atom_ptr_t;
 // Atomic element iterator
 typedef struct dap_chain_atom_iter {
     dap_chain_t *chain;
+    dap_chain_cell_id_t cell_id;
+     void *cur_item;
     dap_chain_atom_ptr_t cur;
     size_t cur_size;
     dap_chain_hash_fast_t *cur_hash;
-    dap_chain_cell_id_t cell_id;
-    bool with_treshold;
-    bool found_in_treshold;
-    void *cur_item;
+    uint64_t cur_num;
 } dap_chain_atom_iter_t;
 
 typedef struct dap_chain_datum_iter {
@@ -75,6 +74,13 @@ static const char* const dap_chain_atom_verify_res_str[] = {
     [ATOM_MOVE_TO_THRESHOLD] = "thresholded"
 };
 
+typedef enum dap_chain_iter_op {
+    DAP_CHAIN_ITER_OP_FIRST,
+    DAP_CHAIN_ITER_OP_LAST,
+    DAP_CHAIN_ITER_OP_NEXT,
+    DAP_CHAIN_ITER_OP_PREV
+} dap_chain_iter_op_t;
+
 typedef dap_chain_t* (*dap_chain_callback_new_t)(void);
 
 typedef void (*dap_chain_callback_t)(dap_chain_t *);
@@ -86,9 +92,10 @@ typedef dap_chain_atom_ptr_t (*dap_chain_callback_atom_form_treshold_t)(dap_chai
 typedef dap_chain_atom_verify_res_t (*dap_chain_callback_atom_verify_t)(dap_chain_t *, dap_chain_atom_ptr_t , size_t);
 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 *, dap_chain_cell_id_t, bool);
-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_atom_iter_t * (*dap_chain_callback_atom_iter_create_t)(dap_chain_t *a_chain, dap_chain_cell_id_t a_cell_id, dap_hash_fast_t *a_hash_from);
+typedef dap_chain_atom_ptr_t (*dap_chain_callback_atom_iter_get_t)(dap_chain_atom_iter_t *a_iter, dap_chain_iter_op_t a_operation, size_t *a_atom_size);
+typedef dap_chain_atom_ptr_t (*dap_chain_callback_atom_iter_find_by_hash_t)(dap_chain_atom_iter_t *a_iter, dap_hash_fast_t *a_atom_hash, size_t *a_atom_size);
+typedef void (*dap_chain_callback_atom_iter_delete_t)(dap_chain_atom_iter_t *);
 
 typedef dap_chain_datum_iter_t * (*dap_chain_datum_callback_iter_create_t)(dap_chain_t *);
 typedef dap_chain_datum_t * (*dap_chain_datum_callback_iter_get_first_t)(dap_chain_datum_iter_t *);
@@ -98,7 +105,6 @@ typedef void (*dap_chain_datum_callback_iter_delete_t)(dap_chain_datum_iter_t *)
 typedef dap_chain_datum_t** (*dap_chain_callback_atom_get_datum_t)(dap_chain_atom_ptr_t, size_t, size_t * );
 typedef dap_time_t (*dap_chain_callback_atom_get_timestamp_t)(dap_chain_atom_ptr_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_t * (*dap_chain_callback_datum_find_by_hash_t)(dap_chain_t *, dap_chain_hash_fast_t *, dap_chain_hash_fast_t *, int *);
 
 typedef dap_chain_atom_ptr_t (*dap_chain_callback_block_find_by_hash_t)(dap_chain_t * ,dap_chain_hash_fast_t *, size_t *);
@@ -106,9 +112,6 @@ typedef dap_chain_atom_ptr_t (*dap_chain_callback_block_find_by_hash_t)(dap_chai
 typedef dap_chain_atom_ptr_t * (*dap_chain_callback_atom_iter_get_atoms_t)(dap_chain_atom_iter_t * ,size_t* ,size_t**);
 typedef size_t (*dap_chain_callback_add_datums_t)(dap_chain_t * , dap_chain_datum_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 void (*dap_chain_callback_atom_iter_delete_t)(dap_chain_atom_iter_t *);
-
 typedef void (*dap_chain_callback_notify_t)(void *a_arg, dap_chain_t *a_chain, dap_chain_cell_id_t a_id, void *a_atom, size_t a_atom_size); //change in chain happened
 
 typedef uint64_t (*dap_chain_callback_get_count)(dap_chain_t *a_chain);
@@ -171,10 +174,6 @@ typedef struct dap_chain {
     dap_chain_callback_add_datums_t callback_add_datums;
     dap_chain_callback_atom_get_hdr_size_t callback_atom_get_hdr_static_size; // Get atom header's size
 
-    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_t callback_atom_get_datums;
     dap_chain_callback_atom_get_timestamp_t callback_atom_get_timestamp;
 
@@ -183,10 +182,11 @@ typedef struct dap_chain {
 
     dap_chain_callback_block_find_by_hash_t callback_block_find_by_tx_hash;
 
-    dap_chain_callback_atom_iter_get_next_t callback_atom_iter_get_next;
-    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_create_t callback_atom_iter_create;
+    dap_chain_callback_atom_iter_get_t callback_atom_iter_get;
     dap_chain_callback_atom_iter_delete_t callback_atom_iter_delete;
+    // WRN: No iterator used or changed with it
+    dap_chain_callback_atom_iter_get_atoms_t callback_atom_iter_get_links;
 
     dap_chain_callback_get_count callback_count_tx;
     dap_chain_callback_get_list callback_get_txs;
diff --git a/modules/chain/include/dap_chain_ch.h b/modules/chain/include/dap_chain_ch.h
index 225689bb72..010d112b36 100644
--- a/modules/chain/include/dap_chain_ch.h
+++ b/modules/chain/include/dap_chain_ch.h
@@ -33,20 +33,35 @@
 #include "uthash.h"
 #include "dap_global_db_cluster.h"
 
-#define DAP_CHAIN_NODE_SYNC_TIMEOUT 60  // sec
-#define DAP_SYNC_TICKS_PER_SECOND   10
+#define DAP_SYNC_TICKS_PER_SECOND           10
+
+typedef enum dap_chain_ch_state {
+    DAP_CHAIN_CH_STATE_IDLE = 0,
+    DAP_CHAIN_CH_STATE_WAITING,
+    DAP_CHAIN_CH_STATE_UPDATE_GLOBAL_DB_REMOTE, // Downloadn GDB hashtable from remote
+    DAP_CHAIN_CH_STATE_UPDATE_GLOBAL_DB, // Update GDB hashtable to remote
+    DAP_CHAIN_CH_STATE_SYNC_GLOBAL_DB,
+    DAP_CHAIN_CH_STATE_UPDATE_CHAINS_REMOTE, // Update chains hashtable from remote
+    DAP_CHAIN_CH_STATE_UPDATE_CHAINS, // Update chains hashtable to remote
+    DAP_CHAIN_CH_STATE_SYNC_CHAINS,
+    DAP_CHAIN_CH_STATE_ERROR
+} dap_chain_ch_state_t;
+
+typedef enum dap_chain_ch_error_type {
+    DAP_CHAIN_CH_ERROR_SYNC_REQUEST_ALREADY_IN_PROCESS,
+    DAP_CHAIN_CH_ERROR_INCORRECT_SYNC_SEQUENCE,
+    DAP_CHAIN_CH_ERROR_CHAIN_PKT_DATA_SIZE,
+    DAP_CHAIN_CH_ERROR_NET_INVALID_ID,
+    DAP_CHAIN_CH_ERROR_CHAIN_NOT_FOUND,
+    DAP_CHAIN_CH_ERROR_ATOM_NOT_FOUND,
+    DAP_CHAIN_CH_ERROR_UNKNOWN_CHAIN_PKT_TYPE,
+    DAP_CHAIN_CH_ERROR_GLOBAL_DB_INTERNAL_NOT_SAVED
+} dap_chain_ch_error_type_t;
 
 typedef struct dap_chain_ch dap_chain_ch_t;
 typedef void (*dap_chain_ch_callback_packet_t)(dap_chain_ch_t*, uint8_t a_pkt_type,
                                                       dap_chain_ch_pkt_t *a_pkt, size_t a_pkt_data_size,
                                                       void * a_arg);
-typedef struct dap_chain_atom_item{
-    dap_chain_hash_fast_t atom_hash;
-    dap_chain_atom_ptr_t atom;
-    size_t atom_size;
-    UT_hash_handle hh;
-} dap_chain_atom_item_t;
-
 typedef struct dap_chain_pkt_item {
     uint64_t pkt_data_size;
     byte_t *pkt_data;
@@ -61,8 +76,12 @@ typedef struct dap_chain_ch_hash_item{
 
 typedef struct dap_chain_ch {
     void *_inheritor;
+    dap_timerfd_t *sync_timer;
+    void *sync_context;
+
+    // Legacy section //
+    int state;
 
-    dap_chain_ch_state_t state;
     uint64_t stats_request_atoms_processed;
     uint64_t stats_request_gdb_processed;
 
@@ -76,7 +95,7 @@ typedef struct dap_chain_ch {
     dap_chain_ch_pkt_hdr_t request_hdr;
     dap_list_t *request_db_iter;
 
-    int timer_shots;
+    uint32_t timer_shots;
     dap_timerfd_t *activity_timer;
     int sent_breaks;
 
@@ -85,10 +104,10 @@ typedef struct dap_chain_ch {
     void *callback_notify_arg;
 } dap_chain_ch_t;
 
-#define DAP_STREAM_CH_CHAIN(a) ((dap_chain_ch_t *) ((a)->internal) )
+#define DAP_CHAIN_CH(a) ((dap_chain_ch_t *) ((a)->internal) )
 #define DAP_STREAM_CH(a) ((dap_stream_ch_t *)((a)->_inheritor))
 #define DAP_CHAIN_PKT_EXPECT_SIZE 7168
-#define DAP_STREAM_CH_CHAIN_ID 'C'
+#define DAP_CHAIN_CH_ID 'C'
 
 int dap_chain_ch_init(void);
 void dap_chain_ch_deinit(void);
diff --git a/modules/chain/include/dap_chain_ch_pkt.h b/modules/chain/include/dap_chain_ch_pkt.h
index cd200f6fa1..68046ededd 100644
--- a/modules/chain/include/dap_chain_ch_pkt.h
+++ b/modules/chain/include/dap_chain_ch_pkt.h
@@ -36,50 +36,44 @@
 
 #include "dap_stream_ch.h"
 
-#define DAP_STREAM_CH_CHAIN_PKT_VERSION                        0x02
-
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS_REQ         0x05
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS_START       0x25
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS             0x35
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS_END         0x45
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_FIRST_CHAIN               0x20
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_CHAIN                     0x01
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_CHAINS             0x03
-
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_REQ      0x06
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_START    0x26
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB          0x36
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_END      0x46
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_FIRST_GLOBAL_DB           0x21
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_GLOBAL_DB                 0x11
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB          0x13
-
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_DELETE                    0xda
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_TIMEOUT                   0xfe
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_ERROR                     0xff
+#define DAP_CHAIN_CH_PKT_VERSION                        0x02
+
+//Legacy
+#define DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS_REQ         0x05
+#define DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS_START       0x25
+#define DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS             0x35
+#define DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS_END         0x45
+#define DAP_CHAIN_CH_PKT_TYPE_FIRST_CHAIN               0x20
+#define DAP_CHAIN_CH_PKT_TYPE_SYNCED_CHAINS             0x03
+
+#define DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB_REQ      0x06
+#define DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB_START    0x26
+#define DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB          0x36
+#define DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB_END      0x46
+#define DAP_CHAIN_CH_PKT_TYPE_FIRST_GLOBAL_DB           0x21
+#define DAP_CHAIN_CH_PKT_TYPE_GLOBAL_DB                 0x11
+#define DAP_CHAIN_CH_PKT_TYPE_SYNCED_GLOBAL_DB          0x13
+
+#define DAP_CHAIN_CH_PKT_TYPE_DELETE                    0xda
+#define DAP_CHAIN_CH_PKT_TYPE_TIMEOUT                   0xfe
+#define DAP_CHAIN_CH_PKT_TYPE_ERROR                     0xff
+
+// Stable
+#define DAP_CHAIN_CH_PKT_TYPE_CHAIN_REQ                 0x80
+#define DAP_CHAIN_CH_PKT_TYPE_CHAIN                     0x01
+#define DAP_CHAIN_CH_PKT_TYPE_CHAIN_SUMMARY             0x81
+#define DAP_CHAIN_CH_PKT_TYPE_CHAIN_ACK                 0x82
+#define DAP_CHAIN_CH_PKT_TYPE_SYNCED_CHAIN              0x88
 
 // TSD sections
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS_TSD         0x15
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_TSD      0x16
-
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_TSD_PROTO        0x0001   // Protocol version
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_TSD_COUNT        0x0002   // Items count
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_TSD_HASH_LAST    0x0003   // Hash of last(s) item
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_TSD_HASH_FIRST   0x0004   // Hash of first(s) item
-#define DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_TSD_LAST_ID      0x0100   // Last ID of GDB synced group
-
-typedef enum dap_chain_ch_state{
-    CHAIN_STATE_IDLE=0,
-    CHAIN_STATE_WAITING,
-    CHAIN_STATE_UPDATE_GLOBAL_DB_REMOTE, // Downloadn GDB hashtable from remote
-    CHAIN_STATE_UPDATE_GLOBAL_DB, // Update GDB hashtable to remote
-    CHAIN_STATE_SYNC_GLOBAL_DB,
-    CHAIN_STATE_UPDATE_CHAINS_REMOTE, // Update chains hashtable from remote
-    CHAIN_STATE_UPDATE_CHAINS, // Update chains hashtable to remote
-    CHAIN_STATE_SYNC_CHAINS,
-    CHAIN_STATE_SYNC_ALL
-} dap_chain_ch_state_t;
+#define DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS_TSD         0x15
+#define DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB_TSD      0x16
 
+#define DAP_CHAIN_CH_PKT_TYPE_UPDATE_TSD_PROTO        0x0001   // Protocol version
+#define DAP_CHAIN_CH_PKT_TYPE_UPDATE_TSD_COUNT        0x0002   // Items count
+#define DAP_CHAIN_CH_PKT_TYPE_UPDATE_TSD_HASH_LAST    0x0003   // Hash of last(s) item
+#define DAP_CHAIN_CH_PKT_TYPE_UPDATE_TSD_HASH_FIRST   0x0004   // Hash of first(s) item
+#define DAP_CHAIN_CH_PKT_TYPE_UPDATE_TSD_LAST_ID      0x0100   // Last ID of GDB synced group
 
 typedef struct dap_chain_ch_update_element{
     dap_hash_fast_t hash;
@@ -89,15 +83,19 @@ typedef struct dap_chain_ch_update_element{
 typedef struct dap_chain_ch_sync_request{
     dap_chain_node_addr_t node_addr; // Requesting node's address
     dap_chain_hash_fast_t hash_from;
-    dap_chain_hash_fast_t hash_to;   // unused
-    uint64_t id_start;
-    uint64_t id_end;                 // unused
+    byte_t unused[96];
 } DAP_ALIGN_PACKED dap_chain_ch_sync_request_t;
 
+typedef struct dap_chain_ch_summary {
+    uint64_t num_cur;
+    uint64_t num_last;
+    byte_t reserved[128];
+} DAP_ALIGN_PACKED dap_chain_ch_summary_t;
 
 typedef struct dap_chain_ch_pkt_hdr {
     uint8_t version;
-    uint8_t padding[3];
+    uint8_t num_hi;
+    uint16_t num_lo;
     uint32_t data_size;
     dap_chain_net_id_t net_id;
     dap_chain_id_t chain_id;
@@ -110,14 +108,16 @@ typedef struct dap_chain_ch_pkt {
 } DAP_ALIGN_PACKED dap_chain_ch_pkt_t;
 
 static const char* c_dap_chain_ch_pkt_type_str[]={
-    [DAP_STREAM_CH_CHAIN_PKT_TYPE_CHAIN] = "DAP_STREAM_CH_CHAIN_PKT_TYPE_CHAIN",
-    [DAP_STREAM_CH_CHAIN_PKT_TYPE_GLOBAL_DB] = "DAP_STREAM_CH_CHAIN_PKT_TYPE_GLOBAL_DB",
-    [DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_CHAINS] = "DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_CHAINS",
-    [DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB] = "DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB",
-    [DAP_STREAM_CH_CHAIN_PKT_TYPE_ERROR] = "DAP_STREAM_CH_CHAIN_PKT_TYPE_ERROR"
+    [DAP_CHAIN_CH_PKT_TYPE_CHAIN] = "DAP_CHAIN_CH_PKT_TYPE_CHAIN",
+    [DAP_CHAIN_CH_PKT_TYPE_GLOBAL_DB] = "DAP_CHAIN_CH_PKT_TYPE_GLOBAL_DB",
+    [DAP_CHAIN_CH_PKT_TYPE_SYNCED_CHAINS] = "DAP_CHAIN_CH_PKT_TYPE_SYNCED_CHAINS",
+    [DAP_CHAIN_CH_PKT_TYPE_SYNCED_GLOBAL_DB] = "DAP_CHAIN_CH_PKT_TYPE_SYNCED_GLOBAL_DB",
+    [DAP_CHAIN_CH_PKT_TYPE_ERROR] = "DAP_CHAIN_CH_PKT_TYPE_ERROR"
 
 };
 
+DAP_STATIC_INLINE size_t dap_chain_ch_pkt_get_size(dap_chain_ch_pkt_t *a_pkt) { return sizeof(dap_chain_ch_pkt_hdr_t) + a_pkt->hdr.data_size; }
+
 dap_chain_ch_pkt_t *dap_chain_ch_pkt_new(uint64_t a_net_id, uint64_t a_chain_id, uint64_t a_cell_id,
                                          const void *a_data, size_t a_data_size);
 
@@ -160,7 +160,7 @@ inline static DAP_PRINTF_ATTR(5, 6) size_t dap_chain_ch_pkt_write_error_unsafe(d
         va_start(l_va, a_err_string_format);
         vsnprintf(l_str, l_size,a_err_string_format, l_va);
         va_end(l_va);
-        return dap_chain_ch_pkt_write_unsafe(a_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_ERROR,
+        return dap_chain_ch_pkt_write_unsafe(a_ch, DAP_CHAIN_CH_PKT_TYPE_ERROR,
                                                     a_net_id, a_chain_id, a_cell_id, l_str, l_size);
     }
     return 0;
@@ -190,7 +190,7 @@ static inline size_t dap_chain_ch_pkt_write_error_inter(dap_events_socket_t *a_e
         va_start(l_va, a_err_string_format);
         vsnprintf(l_str, l_size, a_err_string_format, l_va);
         va_end(l_va);
-        return dap_chain_ch_pkt_write_inter(a_es_input, a_ch_uuid, DAP_STREAM_CH_CHAIN_PKT_TYPE_ERROR,
+        return dap_chain_ch_pkt_write_inter(a_es_input, a_ch_uuid, DAP_CHAIN_CH_PKT_TYPE_ERROR,
                                                     a_net_id, a_chain_id, a_cell_id, l_str, l_size);
     }
     return 0;
diff --git a/modules/channel/chain-net/dap_stream_ch_chain_net.c b/modules/channel/chain-net/dap_stream_ch_chain_net.c
index 831f04a10e..056a573ce2 100644
--- a/modules/channel/chain-net/dap_stream_ch_chain_net.c
+++ b/modules/channel/chain-net/dap_stream_ch_chain_net.c
@@ -169,14 +169,14 @@ void s_stream_ch_packet_in(dap_stream_ch_t *a_ch, void* a_arg)
             break;
             // received ping request - > send pong request
         case DAP_STREAM_CH_CHAIN_NET_PKT_TYPE_PING:
-            //log_it(L_INFO, "Get STREAM_CH_CHAIN_NET_PKT_TYPE_PING");
+            //log_it(L_INFO, "Get CHAIN_CH_NET_PKT_TYPE_PING");
             dap_stream_ch_chain_net_pkt_write(a_ch, DAP_STREAM_CH_CHAIN_NET_PKT_TYPE_PONG,
                                               l_ch_chain_net_pkt->hdr.net_id,NULL, 0);
             dap_stream_ch_set_ready_to_write_unsafe(a_ch, true);
             break;
             // receive pong request -> send nothing
         case DAP_STREAM_CH_CHAIN_NET_PKT_TYPE_PONG:
-            //log_it(L_INFO, "Get STREAM_CH_CHAIN_NET_PKT_TYPE_PONG");
+            //log_it(L_INFO, "Get CHAIN_CH_NET_PKT_TYPE_PONG");
             dap_stream_ch_set_ready_to_write_unsafe(a_ch, false);
             break;
 
diff --git a/modules/net/dap_chain_net.c b/modules/net/dap_chain_net.c
index 05e8dc7b92..1ff166f62e 100644
--- a/modules/net/dap_chain_net.c
+++ b/modules/net/dap_chain_net.c
@@ -144,6 +144,23 @@ struct block_reward {
     struct block_reward *prev, *next;
 };
 
+enum sync_state {
+    SYNC_STATE_IDLE,
+    SYNC_STATE_WAITING,
+    SYNC_STATE_SYNCED,
+    SYNC_STATE_ERROR
+};
+
+struct chain_sync_context {
+    enum sync_state state,
+                    last_state;
+    dap_time_t      stage_last_activity,
+                    sync_idle_time;
+    dap_stream_node_addr_t current_link;
+    dap_chain_t *cur_chain;
+    dap_chain_cell_t *cur_cell;
+};
+
 /**
   * @struct dap_chain_net_pvt
   * @details Private part of chain_net dap object
@@ -170,6 +187,8 @@ typedef struct dap_chain_net_pvt{
     uint16_t seed_nodes_count;
     dap_chain_node_info_t **seed_nodes_info;
 
+    struct chain_sync_context sync_context;
+
     _Atomic(dap_chain_net_state_t) state, state_target;
     uint16_t acl_idx;
 
@@ -228,21 +247,17 @@ static const dap_link_manager_callbacks_t s_link_manager_callbacks = {
 
 // State machine switchs here
 static bool s_net_states_proc(void *a_arg);
-
 struct json_object *s_net_states_json_collect(dap_chain_net_t * l_net);
-
 static void s_net_states_notify(dap_chain_net_t * l_net);
 static void s_nodelist_change_notify(dap_store_obj_t *a_obj, void *a_arg);
 //static void s_net_proc_kill( dap_chain_net_t * a_net );
 static int s_net_init(const char * a_net_name, uint16_t a_acl_idx);
-
 static int s_net_load(dap_chain_net_t *a_net);
 static int s_net_try_online(dap_chain_net_t *a_net);
-
 static int s_cli_net(int argc, char ** argv, void **a_str_reply);
 static uint8_t *s_net_set_acl(dap_chain_hash_fast_t *a_pkey_hash);
 static bool s_new_balancer_link_request(dap_chain_net_t *a_net);
-
+static void s_sync_timer_callback(void *a_arg);
 
 /**
  * @brief
@@ -1976,7 +1991,6 @@ void dap_chain_net_delete(dap_chain_net_t *a_net)
         HASH_DELETE(hh2, s_net_ids, l_net_item);
         DAP_DELETE(l_net_item);
     }
-
     DAP_DEL_Z(PVT(a_net)->poa_nodes_addrs);
     DAP_DEL_Z(PVT(a_net)->node_info);
     if (a_net->pub.ledger) {
@@ -2120,6 +2134,7 @@ int s_net_init(const char *a_net_name, uint16_t a_acl_idx)
             = dap_strncpy(l_net_pvt->seed_nodes_info[i]->ext_host, l_host, l_hostlen) - l_net_pvt->seed_nodes_info[i]->ext_host;
         if (g_node_addr.uint64 == l_addr.uint64) {
             // We're in PoA seed list, predefine node info regardless of host set in [server] config section
+            l_net_pvt->node_info = DAP_NEW_Z_SIZE(dap_chain_node_info_t, sizeof(dap_chain_node_info_t) + DAP_HOSTADDR_STRLEN + 1);
             dap_mempcpy(l_net_pvt->node_info, l_net_pvt->seed_nodes_info[i], dap_chain_node_info_get_size(l_net_pvt->seed_nodes_info[i]));
         }
     }
@@ -2486,6 +2501,9 @@ int s_net_load(dap_chain_net_t *a_net)
     } else if (dap_strcmp(l_node_addr_type, "auto"))
         log_it(L_WARNING, "Unknown node address type will be defalted to 'auto'");
 
+    l_net_pvt->sync_context.sync_idle_time = dap_config_get_item_uint32_default(g_config, "chain", "sync_idle_time", 60);
+    dap_proc_thread_timer_add(NULL, s_sync_timer_callback, l_net, 1000);
+
     if(dap_link_manager_add_net(l_net->pub.id.uint64, l_net_pvt->nodes_cluster->links_cluster)) {
         log_it(L_WARNING, "Can't add net %s to link manager", l_net->pub.name);
     }
@@ -2495,6 +2513,107 @@ int s_net_load(dap_chain_net_t *a_net)
     return 0;
 }
 
+static void s_ch_in_pkt_callback(dap_stream_ch_t *a_ch, uint8_t a_type, const void *a_data, size_t a_data_size, void *a_arg)
+{
+    debug_if(s_debug_more, L_DEBUG, "Got IN sync packet type %hhu size %zu from addr " NODE_ADDR_FP_STR,
+                                                           a_type, a_data_size, NODE_ADDR_FP_ARGS_S(a_ch->stream->node));
+    dap_chain_net_t *l_net = a_arg;
+    dap_chain_net_pvt_t *l_net_pvt = PVT(l_net);
+    switch (a_type) {
+    case DAP_CHAIN_CH_PKT_TYPE_ERROR:
+        l_net_pvt->sync_context.state = SYNC_STATE_ERROR;
+        break;
+    case DAP_CHAIN_CH_PKT_TYPE_SYNCED_CHAIN:
+        l_net_pvt->sync_context.state = SYNC_STATE_SYNCED;
+        break;
+    default:
+        break;
+    }
+    l_net_pvt->sync_context.stage_last_activity = dap_time_now();
+}
+
+static void s_ch_out_pkt_callback(dap_stream_ch_t *a_ch, uint8_t a_type, const void *a_data, size_t a_data_size, void *a_arg)
+{
+    debug_if(s_debug_more, L_DEBUG, "Sent OUT sync packet type %hhu size %zu to addr " NODE_ADDR_FP_STR,
+                                                           a_type, a_data_size, NODE_ADDR_FP_ARGS_S(a_ch->stream->node));
+    dap_chain_net_t *l_net = a_arg;
+    dap_chain_net_pvt_t *l_net_pvt = PVT(l_net);
+    switch (a_type) {
+    case DAP_CHAIN_CH_PKT_TYPE_ERROR:
+        l_net_pvt->sync_context.state = SYNC_STATE_ERROR;
+        break;
+    default:
+        break;
+    }
+    l_net_pvt->sync_context.stage_last_activity = dap_time_now();
+}
+
+static void s_sync_timer_callback(void *a_arg)
+{
+    dap_chain_net_t *l_net = a_arg;
+    dap_chain_net_pvt_t *l_net_pvt = PVT(l_net);
+    if (l_net_pvt->state_target == NET_STATE_OFFLINE)
+        return;
+    if (l_net_pvt->sync_context.last_state == SYNC_STATE_SYNCED || l_net_pvt->sync_context.last_state == SYNC_STATE_ERROR ||
+            (l_net_pvt->sync_context.last_state == SYNC_STATE_IDLE && l_net_pvt->state != NET_STATE_ONLINE) ||
+            dap_time_now() - l_net_pvt->sync_context.stage_last_activity > l_net_pvt->sync_context.sync_idle_time) {
+        if (!l_net_pvt->sync_context.cur_chain || l_net_pvt->sync_context.last_state == SYNC_STATE_ERROR) {
+            // Go no next link
+            dap_cluster_t *l_cluster = dap_cluster_by_mnemonim(l_net->pub.name);
+            if (!dap_stream_node_addr_is_blank(&l_net_pvt->sync_context.current_link)) {
+                dap_stream_ch_del_notifier(&l_net_pvt->sync_context.current_link, DAP_CHAIN_CH_ID,
+                                           DAP_STREAM_PKT_DIR_IN, s_ch_in_pkt_callback, l_net);
+                dap_stream_ch_del_notifier(&l_net_pvt->sync_context.current_link, DAP_CHAIN_CH_ID,
+                                           DAP_STREAM_PKT_DIR_OUT, s_ch_out_pkt_callback, l_net);
+            }
+            l_net_pvt->sync_context.current_link = dap_cluster_get_random_link(l_cluster);
+            if (dap_stream_node_addr_is_blank(&l_net_pvt->sync_context.current_link))
+                return;     // No links in cluster
+            l_net_pvt->sync_context.cur_chain = l_net->pub.chains;
+            if (!l_net_pvt->sync_context.cur_chain) {
+                log_it(L_ERROR, "No chains in net %s", l_net->pub.name);
+                return;
+            }
+            dap_stream_ch_add_notifier(&l_net_pvt->sync_context.current_link, DAP_CHAIN_CH_ID,
+                                       DAP_STREAM_PKT_DIR_IN, s_ch_in_pkt_callback, l_net);
+            dap_stream_ch_add_notifier(&l_net_pvt->sync_context.current_link, DAP_CHAIN_CH_ID,
+                                       DAP_STREAM_PKT_DIR_OUT, s_ch_out_pkt_callback, l_net);
+            l_net_pvt->sync_context.state = l_net_pvt->sync_context.last_state = SYNC_STATE_WAITING;
+        } else {
+            l_net_pvt->sync_context.cur_chain = l_net_pvt->sync_context.cur_chain->next;
+            if (!l_net_pvt->sync_context.cur_chain) {
+                if (l_net_pvt->sync_context.last_state == SYNC_STATE_SYNCED) {
+                    l_net_pvt->state = NET_STATE_ONLINE;
+                    l_net_pvt->sync_context.state = l_net_pvt->sync_context.last_state = SYNC_STATE_IDLE;
+                    s_net_states_proc(l_net);
+                } else
+                    l_net_pvt->sync_context.state = l_net_pvt->sync_context.last_state = SYNC_STATE_WAITING;
+                return;
+            }
+        }
+        // TODO make correct working with cells
+        assert(l_net_pvt->sync_context.cur_chain);
+        l_net_pvt->sync_context.cur_cell = l_net_pvt->sync_context.cur_chain->cells;
+        log_it(L_INFO, "Start synchronization process with " NODE_ADDR_FP_STR " for net %s and chain %s",
+                                                        NODE_ADDR_FP_ARGS_S(l_net_pvt->sync_context.current_link),
+                                                        l_net->pub.name, l_net_pvt->sync_context.cur_chain->name);
+        l_net_pvt->sync_context.state = l_net_pvt->sync_context.last_state = SYNC_STATE_WAITING;
+        dap_hash_fast_t l_last_hash;
+        dap_chain_get_atom_last_hash(l_net_pvt->sync_context.cur_chain, &l_last_hash, l_net_pvt->sync_context.cur_cell
+                                                                                        ? l_net_pvt->sync_context.cur_cell->id : c_dap_chain_cell_id_null);
+        dap_chain_ch_pkt_t *l_chain_pkt = dap_chain_ch_pkt_new(l_net->pub.id.uint64, l_net_pvt->sync_context.cur_chain->id.uint64,
+                                                               l_net_pvt->sync_context.cur_cell ? l_net_pvt->sync_context.cur_cell->id.uint64 : 0,
+                                                               &l_last_hash, sizeof(l_last_hash));
+        dap_stream_ch_pkt_send_by_addr(&l_net_pvt->sync_context.current_link, DAP_CHAIN_CH_ID,
+                                       DAP_CHAIN_CH_PKT_TYPE_CHAIN_REQ, l_chain_pkt,
+                                       dap_chain_ch_pkt_get_size(l_chain_pkt));
+        DAP_DELETE(l_chain_pkt);
+    }
+    if (l_net_pvt->sync_context.last_state != SYNC_STATE_IDLE &&
+            l_net_pvt->sync_context.last_state != l_net_pvt->sync_context.state)
+        l_net_pvt->sync_context.last_state = l_net_pvt->sync_context.state;
+}
+
 /**
  * @brief try net to go online
  *
@@ -3059,8 +3178,8 @@ dap_list_t* dap_chain_datum_list(dap_chain_net_t *a_net, dap_chain_t *a_chain, d
         {
             dap_chain_cell_t *l_cell = l_chain_cur->cells;
             size_t l_atom_size = 0;
-            dap_chain_atom_iter_t *l_atom_iter = l_chain_cur->callback_atom_iter_create(l_chain_cur, l_cell->id, 0);
-            dap_chain_atom_ptr_t l_atom = l_chain_cur->callback_atom_iter_get_first(l_atom_iter, &l_atom_size);
+            dap_chain_atom_iter_t *l_atom_iter = l_chain_cur->callback_atom_iter_create(l_chain_cur, l_cell->id, NULL);
+            dap_chain_atom_ptr_t l_atom = l_chain_cur->callback_atom_iter_get(l_atom_iter, DAP_CHAIN_ITER_OP_FIRST, &l_atom_size);
             while(l_atom && l_atom_size)
             {
                 size_t l_datums_count = 0;
@@ -3092,7 +3211,7 @@ dap_list_t* dap_chain_datum_list(dap_chain_net_t *a_net, dap_chain_t *a_chain, d
                 }
                 DAP_DEL_Z(l_datums);
                 // go to next transaction
-                l_atom = l_chain_cur->callback_atom_iter_get_next(l_atom_iter, &l_atom_size);
+                l_atom = l_chain_cur->callback_atom_iter_get(l_atom_iter, DAP_CHAIN_ITER_OP_NEXT, &l_atom_size);
             }
             l_chain_cur->callback_atom_iter_delete(l_atom_iter);
         }
diff --git a/modules/net/dap_chain_node_cli_cmd.c b/modules/net/dap_chain_node_cli_cmd.c
index 785eb810a5..a85fe65d9a 100644
--- a/modules/net/dap_chain_node_cli_cmd.c
+++ b/modules/net/dap_chain_node_cli_cmd.c
@@ -1159,7 +1159,7 @@ int com_node(int a_argc, char ** a_argv, void **a_str_reply)
         log_it(L_NOTICE, "Stream connection established");
 
         dap_chain_ch_sync_request_t l_sync_request = {};
-        dap_stream_ch_t *l_ch_chain = dap_client_get_stream_ch_unsafe(l_node_client->client, DAP_STREAM_CH_CHAIN_ID);
+        dap_stream_ch_t *l_ch_chain = dap_client_get_stream_ch_unsafe(l_node_client->client, DAP_CHAIN_CH_ID);
         // fill begin id
         l_sync_request.id_start = 1;
         // fill current node address
@@ -1167,7 +1167,7 @@ int com_node(int a_argc, char ** a_argv, void **a_str_reply)
 
         log_it(L_INFO, "Requested GLOBAL_DB syncronizatoin, %"DAP_UINT64_FORMAT_U":%"DAP_UINT64_FORMAT_U" period",
                                                         l_sync_request.id_start, l_sync_request.id_end);
-        if(0 == dap_chain_ch_pkt_write_unsafe(l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNC_GLOBAL_DB,
+        if(0 == dap_chain_ch_pkt_write_unsafe(l_ch_chain, DAP_CHAIN_CH_PKT_TYPE_SYNC_GLOBAL_DB,
                 l_net->pub.id.uint64, 0, 0, &l_sync_request,
                 sizeof(l_sync_request))) {
             dap_cli_server_cmd_set_reply_text(a_str_reply, "Error: Can't send sync chains request");
@@ -1202,7 +1202,7 @@ int com_node(int a_argc, char ** a_argv, void **a_str_reply)
             dap_chain_node_client_reset(l_node_client);
             // send request
             dap_chain_ch_sync_request_t l_sync_request = {};
-            if(0 == dap_chain_ch_pkt_write_unsafe(l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNC_CHAINS,
+            if(0 == dap_chain_ch_pkt_write_unsafe(l_ch_chain, DAP_CHAIN_CH_PKT_TYPE_SYNC_CHAINS,
                     l_net->pub.id.uint64, l_chain->id.uint64, l_remote_node_info->hdr.cell_id.uint64, &l_sync_request,
                     sizeof(l_sync_request))) {
                 dap_cli_server_cmd_set_reply_text(a_str_reply, "Error: Can't send sync chains request");
@@ -7702,7 +7702,7 @@ static int s_check_cmd(int a_arg_index, int a_argc, char **a_argv, void **a_str_
     size_t l_atom_size = 0, l_datums_count = 0;
 
     HASH_ITER(hh, l_chain->cells, l_cell, l_cell_tmp) {
-        l_iter = l_cell->chain->callback_atom_iter_create(l_cell->chain, l_cell->id, 0);
+        l_iter = l_cell->chain->callback_atom_iter_create(l_cell->chain, l_cell->id, NULL);
         dap_chain_atom_ptr_t l_atom = l_cell->chain->callback_atom_find_by_hash(l_iter, &l_hash_tmp, &l_atom_size);
         dap_chain_datum_t **l_datums = l_cell->chain->callback_atom_get_datums(l_atom, l_atom_size, &l_datums_count);
         for (size_t i = 0; i < l_datums_count; i++) {
diff --git a/modules/net/dap_chain_node_cli_cmd_tx.c b/modules/net/dap_chain_node_cli_cmd_tx.c
index 5c00beb02b..db41574ba2 100644
--- a/modules/net/dap_chain_node_cli_cmd_tx.c
+++ b/modules/net/dap_chain_node_cli_cmd_tx.c
@@ -631,9 +631,9 @@ json_object *dap_db_history_tx_all(dap_chain_t *l_chain, dap_chain_net_t *l_net,
         }
         size_t i_tmp = 1;
         HASH_ITER(hh, l_chain->cells, l_cell, l_cell_tmp) {
-            l_iter = l_chain->callback_atom_iter_create(l_chain, l_cell->id, 0);
+            l_iter = l_chain->callback_atom_iter_create(l_chain, l_cell->id, NULL);
             size_t l_atom_size = 0;
-            dap_chain_atom_ptr_t l_ptr = l_chain->callback_atom_iter_get_first(l_iter, &l_atom_size);
+            dap_chain_atom_ptr_t l_ptr = l_chain->callback_atom_iter_get(l_iter, DAP_CHAIN_ITER_OP_FIRST, &l_atom_size);
             while (l_ptr && l_atom_size) {
                 size_t l_datums_count = 0;
                 dap_chain_datum_t **l_datums = l_cell->chain->callback_atom_get_datums(l_ptr, l_atom_size, &l_datums_count);
@@ -663,7 +663,7 @@ json_object *dap_db_history_tx_all(dap_chain_t *l_chain, dap_chain_net_t *l_net,
                     }
                 }
                 DAP_DEL_Z(l_datums);
-                l_ptr = l_chain->callback_atom_iter_get_next(l_iter, &l_atom_size);
+                l_ptr = l_chain->callback_atom_iter_get(l_iter, DAP_CHAIN_ITER_OP_NEXT, &l_atom_size);
             }
             l_cell->chain->callback_atom_iter_delete(l_iter);
         }
@@ -700,13 +700,13 @@ static char* dap_db_chain_history_token_list(dap_chain_t * a_chain, const char *
     size_t l_atom_size = 0;
     dap_chain_cell_t *l_cell = a_chain->cells;
     do {
-        dap_chain_atom_iter_t *l_atom_iter = a_chain->callback_atom_iter_create(a_chain, l_cell->id, 0);
+        dap_chain_atom_iter_t *l_atom_iter = a_chain->callback_atom_iter_create(a_chain, l_cell->id, NULL);
         if(!a_chain->callback_atom_get_datums) {
             log_it(L_DEBUG, "Not defined callback_atom_get_datums for chain \"%s\"", a_chain->name);
             return NULL ;
         }
-        for (dap_chain_atom_ptr_t l_atom = a_chain->callback_atom_iter_get_first(l_atom_iter, &l_atom_size);
-                l_atom && l_atom_size; l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter, &l_atom_size)) {
+        for (dap_chain_atom_ptr_t l_atom = a_chain->callback_atom_iter_get(l_atom_iter, DAP_CHAIN_ITER_OP_FIRST, &l_atom_size);
+                l_atom && l_atom_size; l_atom = a_chain->callback_atom_iter_get(l_atom_iter, DAP_CHAIN_ITER_OP_NEXT, &l_atom_size)) {
             size_t l_datums_count = 0;
             dap_chain_datum_t **l_datums = a_chain->callback_atom_get_datums(l_atom, l_atom_size, &l_datums_count);
             for(size_t l_datum_n = 0; l_datum_n < l_datums_count; l_datum_n++) {
@@ -790,11 +790,11 @@ static char* dap_db_history_filter(dap_chain_t * a_chain, dap_ledger_t *a_ledger
     do {
         // load transactions
         size_t l_atom_size = 0;
-        dap_chain_atom_iter_t *l_atom_iter = a_chain->callback_atom_iter_create(a_chain, l_cell->id, 0);
+        dap_chain_atom_iter_t *l_atom_iter = a_chain->callback_atom_iter_create(a_chain, l_cell->id, NULL);
         size_t l_datum_num = 0, l_token_num = 0, l_emission_num = 0, l_tx_num = 0;
         size_t l_datum_num_global = a_total_datums ? *a_total_datums : 0;
-        for (dap_chain_atom_ptr_t l_atom = a_chain->callback_atom_iter_get_first(l_atom_iter, &l_atom_size);
-                l_atom && l_atom_size; l_atom = a_chain->callback_atom_iter_get_next(l_atom_iter, &l_atom_size)) {
+        for (dap_chain_atom_ptr_t l_atom = a_chain->callback_atom_iter_get(l_atom_iter, DAP_CHAIN_ITER_OP_FIRST, &l_atom_size);
+                l_atom && l_atom_size; l_atom = a_chain->callback_atom_iter_get(l_atom_iter, DAP_CHAIN_ITER_OP_NEXT, &l_atom_size)) {
             size_t l_datums_count = 0;
             dap_chain_datum_t **l_datums = a_chain->callback_atom_get_datums(l_atom, l_atom_size, &l_datums_count);
             if (!l_datums || !l_datums_count)
diff --git a/modules/net/dap_chain_node_client.c b/modules/net/dap_chain_node_client.c
index 14f7bdeddb..9caed1f5bc 100644
--- a/modules/net/dap_chain_node_client.c
+++ b/modules/net/dap_chain_node_client.c
@@ -181,13 +181,13 @@ dap_chain_node_sync_status_t dap_chain_node_client_start_sync(dap_chain_node_cli
     // check if esocket still in worker
     dap_stream_ch_t *l_ch = dap_stream_ch_find_by_uuid_unsafe(a_node_client->stream_worker, a_node_client->ch_chain_uuid);
     if (l_ch) {
-        dap_chain_ch_t *l_ch_chain = DAP_STREAM_CH_CHAIN(l_ch);
+        dap_chain_ch_t *l_ch_chain = DAP_CHAIN_CH(l_ch);
         assert(l_ch_chain);
         dap_chain_net_t * l_net = a_node_client->net;
         assert(l_net);
         // If we do nothing - init sync process
 
-        if (l_ch_chain->state == CHAIN_STATE_IDLE) {
+        if (l_ch_chain->state == DAP_CHAIN_CH_STATE_IDLE) {
             // bool l_trylocked = dap_chain_net_sync_trylock(l_net, a_node_client);
             bool l_trylocked = true;
             if (l_trylocked) {
@@ -197,7 +197,7 @@ dap_chain_node_sync_status_t dap_chain_node_client_start_sync(dap_chain_node_cli
                 dap_chain_ch_sync_request_t l_sync_chain = {};
                 l_sync_chain.node_addr.uint64 = dap_chain_net_get_cur_addr_int(l_net);
                 dap_chain_ch_pkt_write_unsafe(a_node_client->ch_chain,
-                                                     DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS_REQ,
+                                                     DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS_REQ,
                                                      l_net->pub.id.uint64, l_net->pub.chains->id.uint64, 0,
                                                      &l_sync_chain, sizeof(l_sync_chain));
                 if (!l_ch_chain->activity_timer)
@@ -362,35 +362,35 @@ static void s_ch_chain_callback_notify_packet_in(dap_chain_ch_t* a_ch_chain, uin
                 l_finished = true;
             }
         break;
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_REQ:{
+        case DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB_REQ:{
             l_node_client->state = NODE_CLIENT_STATE_SYNC_GDB_UPDATES;
         }break;
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_START:{
+        case DAP_CHAIN_CH_PKT_TYPE_UPDATE_GLOBAL_DB_START:{
             l_node_client->state = NODE_CLIENT_STATE_SYNC_GDB_RVRS;
             dap_chain_net_t * l_net = l_node_client->net;
             assert(l_net);
             dap_chain_net_set_state(l_net, NET_STATE_SYNC_GDB);
         }break;
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_FIRST_GLOBAL_DB:{
+        case DAP_CHAIN_CH_PKT_TYPE_FIRST_GLOBAL_DB:{
             l_node_client->state = NODE_CLIENT_STATE_SYNC_GDB;
         }break;
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS_REQ:{
+        case DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS_REQ:{
             l_node_client->state = NODE_CLIENT_STATE_SYNC_CHAINS_UPDATES;
         }break;
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS_START:{
+        case DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS_START:{
             l_node_client->state = NODE_CLIENT_STATE_SYNC_CHAINS_RVRS;
             dap_chain_net_t * l_net = l_node_client->net;
             assert(l_net);
             dap_chain_net_set_state(l_net, NET_STATE_SYNC_CHAINS);
         }break;
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_FIRST_CHAIN:{
+        case DAP_CHAIN_CH_PKT_TYPE_FIRST_CHAIN:{
             l_node_client->state = NODE_CLIENT_STATE_SYNC_CHAINS;
         }break;
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB:
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_CHAINS: {
+        case DAP_CHAIN_CH_PKT_TYPE_SYNCED_GLOBAL_DB:
+        case DAP_CHAIN_CH_PKT_TYPE_SYNCED_CHAINS: {
             dap_chain_id_t  l_chain_id = {};
             dap_chain_cell_id_t l_cell_id = {};
-            if (a_pkt_type == DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB) {
+            if (a_pkt_type == DAP_CHAIN_CH_PKT_TYPE_SYNCED_GLOBAL_DB) {
                 if (dap_chain_net_get_target_state(l_net) != NET_STATE_SYNC_GDB) {
                     if(s_stream_ch_chain_debug_more)
                         log_it(L_INFO,"In: Link %s."NODE_ADDR_FP_STR" synced GDB. Going to update chains", l_net->pub.name, NODE_ADDR_FP_ARGS_S(l_node_client->remote_node_addr ));
@@ -434,7 +434,7 @@ static void s_ch_chain_callback_notify_packet_in(dap_chain_ch_t* a_ch_chain, uin
                     log_it(L_INFO,"In: Link %s."NODE_ADDR_FP_STR" started to sync %s chain",l_net->pub.name,
                            NODE_ADDR_FP_ARGS_S(l_node_addr), l_node_client->cur_chain->name );
                 }
-                dap_chain_ch_pkt_write_unsafe(l_node_client->ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_CHAINS_REQ,
+                dap_chain_ch_pkt_write_unsafe(l_node_client->ch_chain, DAP_CHAIN_CH_PKT_TYPE_UPDATE_CHAINS_REQ,
                                                      l_net->pub.id.uint64 ,
                                                      l_chain_id.uint64,l_cell_id.uint64,NULL,0);
             } else { // If no - over with sync process
@@ -483,20 +483,20 @@ static void s_ch_chain_callback_notify_packet_out(dap_chain_ch_t* a_ch_chain, ui
     dap_chain_node_client_t * l_node_client = (dap_chain_node_client_t *) a_arg;
     assert(a_arg);
     switch (a_pkt_type) {
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB: {
+        case DAP_CHAIN_CH_PKT_TYPE_SYNCED_GLOBAL_DB: {
             if(s_stream_ch_chain_debug_more)
                 log_it(L_INFO,"Out: global database sent to uplink "NODE_ADDR_FP_STR, NODE_ADDR_FP_ARGS_S(l_node_client->remote_node_addr));
         } break;
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_CHAINS: {
+        case DAP_CHAIN_CH_PKT_TYPE_SYNCED_CHAINS: {
             if(s_stream_ch_chain_debug_more)
                 log_it(L_INFO,"Out: chain %"DAP_UINT64_FORMAT_x" sent to uplink "NODE_ADDR_FP_STR,l_node_client->cur_chain ? l_node_client->cur_chain->id.uint64 : 0, NODE_ADDR_FP_ARGS_S(l_node_client->remote_node_addr));
         } break;
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_TIMEOUT:
-        case DAP_STREAM_CH_CHAIN_PKT_TYPE_DELETE: {
+        case DAP_CHAIN_CH_PKT_TYPE_TIMEOUT:
+        case DAP_CHAIN_CH_PKT_TYPE_DELETE: {
             dap_chain_net_t *l_net = l_node_client->net;
             assert(l_net);
             log_it(L_DEBUG, "In: State node %s."NODE_ADDR_FP_STR" %s", l_net->pub.name, NODE_ADDR_FP_ARGS_S(g_node_addr),
-                            a_pkt_type == DAP_STREAM_CH_CHAIN_PKT_TYPE_TIMEOUT ? "is timeout for sync" : "stream closed");
+                            a_pkt_type == DAP_CHAIN_CH_PKT_TYPE_TIMEOUT ? "is timeout for sync" : "stream closed");
             l_node_client->state = NODE_CLIENT_STATE_ERROR;
             if (l_node_client->sync_timer)
                 dap_timerfd_reset_unsafe(l_node_client->sync_timer);
@@ -771,7 +771,7 @@ void dap_chain_node_client_close_unsafe(dap_chain_node_client_t *a_node_client)
     if (a_node_client->stream_worker) {
         dap_stream_ch_t *l_ch = dap_stream_ch_find_by_uuid_unsafe(a_node_client->stream_worker, a_node_client->ch_chain_uuid);
         if (l_ch) {
-            dap_chain_ch_t *l_ch_chain = DAP_STREAM_CH_CHAIN(l_ch);
+            dap_chain_ch_t *l_ch_chain = DAP_CHAIN_CH(l_ch);
             l_ch_chain->callback_notify_packet_in = NULL;
             l_ch_chain->callback_notify_packet_out = NULL;
         }
@@ -930,8 +930,8 @@ static int s_node_client_set_notify_callbacks(dap_client_t *a_client, uint8_t a_
             l_ret = 0;
             switch (a_ch_id) {
                 //  'C'
-            case DAP_STREAM_CH_CHAIN_ID: {
-                dap_chain_ch_t *l_ch_chain       = DAP_STREAM_CH_CHAIN(l_ch);
+            case DAP_CHAIN_CH_ID: {
+                dap_chain_ch_t *l_ch_chain       = DAP_CHAIN_CH(l_ch);
                 l_ch_chain->callback_notify_packet_out  = s_ch_chain_callback_notify_packet_out;
                 l_ch_chain->callback_notify_packet_in   = s_ch_chain_callback_notify_packet_in;
                 l_ch_chain->callback_notify_arg         = l_node_client;
diff --git a/modules/type/blocks/dap_chain_cs_blocks.c b/modules/type/blocks/dap_chain_cs_blocks.c
index 96ec6b4786..4ddb1393bf 100644
--- a/modules/type/blocks/dap_chain_cs_blocks.c
+++ b/modules/type/blocks/dap_chain_cs_blocks.c
@@ -99,8 +99,7 @@ static dap_chain_atom_verify_res_t s_callback_atom_verify(dap_chain_t * a_chain,
 //    Get block header size
 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_cell_id_t a_cell_id, bool a_with_treshold);
-static dap_chain_atom_iter_t* s_callback_atom_iter_create_from(dap_chain_t *a_chain, dap_chain_atom_ptr_t a_atom, size_t a_atom_size);
+static dap_chain_atom_iter_t *s_callback_atom_iter_create(dap_chain_t *a_chain, dap_chain_cell_id_t a_cell_id, dap_hash_fast_t *a_hash_from);
 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_t *s_callback_datum_find_by_hash(dap_chain_t *a_chain, dap_chain_hash_fast_t *a_datum_hash,
@@ -112,12 +111,9 @@ static dap_chain_datum_t** s_callback_atom_get_datums(dap_chain_atom_ptr_t a_ato
 static dap_time_t s_chain_callback_atom_get_timestamp(dap_chain_atom_ptr_t a_atom) { return ((dap_chain_block_t *)a_atom)->hdr.ts_created; }
 static uint256_t s_callback_calc_reward(dap_chain_t *a_chain, dap_hash_fast_t *a_block_hash, dap_pkey_t *a_block_sign_pkey);
 //    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
+static dap_chain_atom_ptr_t s_callback_atom_iter_get(dap_chain_atom_iter_t *a_atom_iter, dap_chain_iter_op_t a_operation, size_t *a_atom_size);
 static dap_chain_atom_ptr_t *s_callback_atom_iter_get_links( dap_chain_atom_iter_t * a_atom_iter , size_t *a_links_size,
                                                                   size_t ** a_links_size_ptr );  //    Get list of linked blocks
-static dap_chain_atom_ptr_t *s_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 blocks
 //Get list of hashes
 static dap_list_t *s_block_parse_str_list(char *a_hash_str, size_t * a_hash_size, dap_chain_t * a_chain);
 
@@ -242,13 +238,10 @@ static int s_chain_cs_blocks_new(dap_chain_t * a_chain, dap_config_t * a_chain_c
     a_chain->callback_atom_get_hdr_static_size = s_callback_atom_get_static_hdr_size; // Get block hdr size
 
     a_chain->callback_atom_iter_create = s_callback_atom_iter_create;
-    a_chain->callback_atom_iter_create_from = s_callback_atom_iter_create_from;
     a_chain->callback_atom_iter_delete = s_callback_atom_iter_delete;
-    // 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_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;
+    a_chain->callback_atom_iter_get = s_callback_atom_iter_get; // Linear pass through
+
+    a_chain->callback_atom_iter_get_links = s_callback_atom_iter_get_links;
 
     // Datum operations callbacks
     a_chain->callback_datum_iter_create = s_chain_callback_datum_iter_create; // Datum iterator create
@@ -1446,52 +1439,6 @@ static size_t s_callback_atom_get_static_hdr_size(void)
     return sizeof (dap_chain_block_hdr_t);
 }
 
-/**
- * @brief s_callback_atom_iter_create
- * @param a_chain
- * @return
- */
-static dap_chain_atom_iter_t *s_callback_atom_iter_create(dap_chain_t *a_chain, dap_chain_cell_id_t a_cell_id, bool a_with_treshold)
-{
-    dap_chain_atom_iter_t * l_atom_iter = DAP_NEW_Z(dap_chain_atom_iter_t);
-    if (!l_atom_iter) {
-        log_it(L_CRITICAL, "Memory allocation error");
-        return NULL;
-    }
-    l_atom_iter->chain = a_chain;
-    l_atom_iter->cell_id = a_cell_id;
-    l_atom_iter->with_treshold = a_with_treshold;
-#ifdef WIN32
-    log_it(L_DEBUG, "! %p create caller id %lu", l_atom_iter, GetThreadId(GetCurrentThread()));
-#endif
-    return l_atom_iter;
-}
-
-/**
- * @brief s_callback_atom_iter_create_from
- * @param a_chain
- * @param a_atom
- * @param a_atom_size
- * @return
- */
-static dap_chain_atom_iter_t* s_callback_atom_iter_create_from(dap_chain_t * a_chain, dap_chain_atom_ptr_t a_atom, size_t a_atom_size)
-{
-    if (a_atom && a_atom_size){
-        dap_chain_hash_fast_t l_atom_hash;
-        dap_hash_fast(a_atom, a_atom_size, &l_atom_hash);
-        dap_chain_atom_iter_t * l_atom_iter = s_callback_atom_iter_create(a_chain, a_chain->cells->id, 0);
-        if (l_atom_iter){
-            dap_chain_cs_blocks_t *l_blocks = DAP_CHAIN_CS_BLOCKS(a_chain);
-            l_atom_iter->cur_item = dap_chain_block_cache_get_by_hash(l_blocks, &l_atom_hash);
-            l_atom_iter->cur = a_atom;
-            l_atom_iter->cur_size = a_atom_size;
-            return l_atom_iter;
-        }else
-            return NULL;
-    }else
-        return NULL;
-}
-
 /**
  * @brief s_callback_atom_iter_find_by_hash
  * @param a_atom_iter
@@ -1499,23 +1446,21 @@ static dap_chain_atom_iter_t* s_callback_atom_iter_create_from(dap_chain_t * a_c
  * @param a_atom_size
  * @return
  */
-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,
+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)
 {
     assert(a_atom_iter);
-    dap_chain_cs_blocks_pvt_t *l_blocks_pvt = PVT(DAP_CHAIN_CS_BLOCKS(a_atom_iter->chain));
-    dap_chain_block_cache_t * l_block_cache = NULL;
-    pthread_rwlock_rdlock(&l_blocks_pvt->rwlock);
-    HASH_FIND(hh, l_blocks_pvt->blocks, a_atom_hash, sizeof(*a_atom_hash), l_block_cache);
-    pthread_rwlock_unlock(&l_blocks_pvt->rwlock);
+    dap_chain_cs_blocks_t *l_blocks = DAP_CHAIN_CS_BLOCKS(a_atom_iter->chain);
+    dap_chain_block_cache_t *l_block_cache = dap_chain_block_cache_get_by_hash(l_blocks, a_atom_hash);
     a_atom_iter->cur_item = l_block_cache;
     if (l_block_cache) {
-        a_atom_iter->cur = l_block_cache->block;
-        a_atom_iter->cur_size = l_block_cache->block_size;
-    } else {
-        a_atom_iter->cur = NULL;
-        a_atom_iter->cur_size = 0;
-    }
+        a_atom_iter->cur        = l_block_cache->block;
+        a_atom_iter->cur_size   = l_block_cache->block_size;
+        a_atom_iter->cur_hash   = &l_block_cache->block_hash;
+        a_atom_iter->cur_num    = l_block_cache->block_number;
+    } else
+        *a_atom_iter = (dap_chain_atom_iter_t) { .chain = a_atom_iter->chain,
+                                                 .cell_id = a_atom_iter->cell_id };
     if (a_atom_size)
         *a_atom_size = a_atom_iter->cur_size;
     return a_atom_iter->cur;
@@ -1578,32 +1523,64 @@ static dap_chain_datum_t** s_callback_atom_get_datums(dap_chain_atom_ptr_t a_ato
 }
 
 /**
- * @brief s_callback_atom_iter_get_first
- * @param a_atom_iter
- * @param a_atom_size
+ * @brief s_callback_atom_iter_create
+ * @param a_chain
  * @return
  */
-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 )
+static dap_chain_atom_iter_t *s_callback_atom_iter_create(dap_chain_t *a_chain, dap_chain_cell_id_t a_cell_id, dap_hash_fast_t *a_hash_from)
 {
-    if(!a_atom_iter) {
-        log_it(L_CRITICAL, "Invalid argument");
+    dap_chain_atom_iter_t * l_atom_iter = DAP_NEW_Z(dap_chain_atom_iter_t);
+    if (!l_atom_iter) {
+        log_it(L_CRITICAL, "Memory allocation error");
         return NULL;
     }
+    l_atom_iter->chain = a_chain;
+    l_atom_iter->cell_id = a_cell_id;
+    if (a_hash_from)
+        s_callback_atom_iter_find_by_hash(l_atom_iter, a_hash_from, NULL);
+    return l_atom_iter;
+}
+
+/**
+ * @brief s_callback_atom_iter_get
+ * @param a_atom_iter
+ * @param a_operation
+ * @param a_atom_size
+ * @return
+ */
+static dap_chain_atom_ptr_t s_callback_atom_iter_get(dap_chain_atom_iter_t *a_atom_iter, dap_chain_iter_op_t a_operation, size_t *a_atom_size)
+{
+    dap_return_val_if_fail(a_atom_iter, 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);
-    //pthread_rwlock_rdlock(&l_blocks_pvt->rwlock);
-    a_atom_iter->cur_item       = l_blocks_pvt->blocks;
-    if (a_atom_iter->cur_item) {
-        a_atom_iter->cur        = l_blocks_pvt->blocks->block;
-        a_atom_iter->cur_size   = l_blocks_pvt->blocks->block_size;
-        a_atom_iter->cur_hash   = &l_blocks_pvt->blocks->block_hash;
-    } else {
-        a_atom_iter->cur        = NULL;
-        a_atom_iter->cur_size   = 0;
-        a_atom_iter->cur_hash   = NULL;
+    pthread_rwlock_rdlock(&l_blocks_pvt->rwlock);
+    switch (a_operation) {
+    case DAP_CHAIN_ITER_OP_FIRST:
+        a_atom_iter->cur_item = l_blocks_pvt->blocks;
+        break;
+    case DAP_CHAIN_ITER_OP_LAST:
+        HASH_LAST(l_blocks_pvt->blocks, a_atom_iter->cur_item);
+        break;
+    case DAP_CHAIN_ITER_OP_NEXT:
+        if (a_atom_iter->cur_item)
+            a_atom_iter->cur_item = ((dap_chain_block_cache_t *)a_atom_iter->cur_item)->hh.next;
+        break;
+    case DAP_CHAIN_ITER_OP_PREV:
+        if (a_atom_iter->cur_item)
+            a_atom_iter->cur_item = ((dap_chain_block_cache_t *)a_atom_iter->cur_item)->hh.prev;
+        break;
     }
-    //pthread_rwlock_unlock(&l_blocks_pvt->rwlock);
+    if (a_atom_iter->cur_item) {
+        dap_chain_block_cache_t *l_item = a_atom_iter->cur_item;
+        a_atom_iter->cur        = l_item->block;
+        a_atom_iter->cur_size   = l_item->block_size;
+        a_atom_iter->cur_hash   = &l_item->block_hash;
+        a_atom_iter->cur_num    = l_item->block_number;
+    } else
+        *a_atom_iter = (dap_chain_atom_iter_t) { .chain = a_atom_iter->chain,
+                                                 .cell_id = a_atom_iter->cell_id };
+    pthread_rwlock_unlock(&l_blocks_pvt->rwlock);
     if (a_atom_size)
         *a_atom_size = a_atom_iter->cur_size;
 
@@ -1611,31 +1588,12 @@ static dap_chain_atom_ptr_t s_callback_atom_iter_get_first( dap_chain_atom_iter_
 }
 
 /**
- * @brief s_callback_atom_iter_get_next
+ * @brief s_callback_atom_iter_delete
  * @param a_atom_iter
- * @param a_atom_size
- * @return
  */
-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 )
+static void s_callback_atom_iter_delete(dap_chain_atom_iter_t * a_atom_iter)
 {
-    assert(a_atom_iter);
-    assert(a_atom_iter->cur_item);
-
-    dap_chain_block_cache_t *l_next_item = ((dap_chain_block_cache_t*)a_atom_iter->cur_item)->hh.next;
-    a_atom_iter->cur_item = l_next_item;
-    if (a_atom_iter->cur_item) {
-        a_atom_iter->cur        = l_next_item->block;
-        a_atom_iter->cur_size   = l_next_item->block_size;
-        a_atom_iter->cur_hash   = &l_next_item->block_hash;
-    } else {
-        a_atom_iter->cur        = NULL;
-        a_atom_iter->cur_size   = 0;
-        a_atom_iter->cur_hash   = NULL;
-    }
-    if(a_atom_size)
-        *a_atom_size = a_atom_iter->cur_size;
-
-    return a_atom_iter->cur;
+    DAP_DELETE(a_atom_iter);
 }
 
 /**
@@ -1670,70 +1628,6 @@ static dap_chain_atom_ptr_t *s_callback_atom_iter_get_links(dap_chain_atom_iter_
     return l_ret;
 }
 
-/**
- * @brief s_callback_atom_iter_get_lasts
- * @param a_atom_iter
- * @param a_links_size
- * @param a_lasts_size_ptr
- * @return
- */
-static dap_chain_atom_ptr_t *s_callback_atom_iter_get_lasts( dap_chain_atom_iter_t *a_atom_iter, size_t *a_links_size, size_t **a_lasts_size_ptr)
-{
-    if(!a_atom_iter) {
-        log_it(L_CRITICAL, "Invalid argument");
-        return NULL;
-    }
-    dap_chain_block_cache_t *l_blocks = PVT(DAP_CHAIN_CS_BLOCKS(a_atom_iter->chain))->blocks;
-    dap_chain_block_cache_t *l_block_cache_last = l_blocks ? l_blocks->hh.tbl->tail->prev : NULL;
-    l_block_cache_last = l_block_cache_last ? l_block_cache_last->hh.next : l_blocks;
-
-    if (l_block_cache_last) {
-        a_atom_iter->cur = l_block_cache_last->block;
-        a_atom_iter->cur_size = l_block_cache_last->block_size;
-        a_atom_iter->cur_hash = &l_block_cache_last->block_hash;
-        if (a_lasts_size_ptr) {
-            *a_lasts_size_ptr = DAP_NEW_Z(size_t);
-            if (!a_lasts_size_ptr) {
-                log_it(L_CRITICAL, "Memory allocation error");
-                return NULL;
-            }
-            (*a_lasts_size_ptr)[0] = l_block_cache_last->block_size;
-        }
-        if (a_links_size)
-            *a_links_size = 1;
-        dap_chain_atom_ptr_t *l_ret = DAP_NEW_Z(dap_chain_atom_ptr_t);
-        if (!l_ret) {
-            log_it(L_CRITICAL, "Memory allocation error");
-            if (a_lasts_size_ptr)
-                DAP_DEL_Z(*a_lasts_size_ptr);
-            return NULL;
-        }
-        l_ret[0] = l_block_cache_last->block;
-        return l_ret;
-    } else {
-        a_atom_iter->cur = NULL;
-        a_atom_iter->cur_size = 0;
-        a_atom_iter->cur_hash = NULL;
-        if (a_links_size)
-            *a_links_size = 0;
-        if (a_lasts_size_ptr)
-            *a_lasts_size_ptr = NULL;
-        return NULL;
-    }
-}
-
-/**
- * @brief s_callback_atom_iter_delete
- * @param a_atom_iter
- */
-static void s_callback_atom_iter_delete(dap_chain_atom_iter_t * a_atom_iter)
-{
-#ifdef WIN32
-    log_it(L_DEBUG, "! %p delete caller id %lu", a_atom_iter, GetThreadId(GetCurrentThread()));
-#endif
-    DAP_DELETE(a_atom_iter);
-}
-
 static dap_chain_datum_iter_t *s_chain_callback_datum_iter_create(dap_chain_t *a_chain)
 {
     dap_chain_datum_iter_t *l_ret = DAP_NEW_Z(dap_chain_datum_iter_t);
diff --git a/modules/type/dag/dap_chain_cs_dag.c b/modules/type/dag/dap_chain_cs_dag.c
index 4bbd15ab3f..e7cbf18eb3 100644
--- a/modules/type/dag/dap_chain_cs_dag.c
+++ b/modules/type/dag/dap_chain_cs_dag.c
@@ -63,6 +63,7 @@ typedef struct dap_chain_cs_dag_event_item {
     dap_nanotime_t ts_added;
     dap_chain_cs_dag_event_t *event;
     size_t event_size;
+    uint64_t event_number;
     int ret_code;
     UT_hash_handle hh, hh_select, hh_datums;
 } dap_chain_cs_dag_event_item_t;
@@ -100,10 +101,7 @@ static dap_chain_atom_ptr_t s_chain_callback_atom_add_from_treshold(dap_chain_t
 static dap_chain_atom_verify_res_t s_chain_callback_atom_verify(dap_chain_t * a_chain, dap_chain_atom_ptr_t , size_t);                   //    Verify new event in dag
 static size_t s_chain_callback_atom_get_static_hdr_size(void);                               //    Get dag event header size
 
-static dap_chain_atom_iter_t* s_chain_callback_atom_iter_create(dap_chain_t * a_chain, dap_chain_cell_id_t a_cell_id, bool a_with_treshold);
-static dap_chain_atom_iter_t* s_chain_callback_atom_iter_create_from(dap_chain_t *  ,
-                                                                     dap_chain_atom_ptr_t , size_t);
-
+static dap_chain_atom_iter_t* s_chain_callback_atom_iter_create(dap_chain_t * a_chain, dap_chain_cell_id_t a_cell_id, dap_hash_fast_t *a_hash_from);
 
 static dap_chain_atom_ptr_t s_chain_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);
@@ -112,12 +110,9 @@ static dap_chain_datum_t *s_chain_callback_atom_find_by_datum_hash(dap_chain_t *
 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);
 static dap_time_t s_chain_callback_atom_get_timestamp(dap_chain_atom_ptr_t a_atom) { return ((dap_chain_cs_dag_event_t *)a_atom)->header.ts_created; }
 //    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
+static dap_chain_atom_ptr_t s_chain_callback_atom_iter_get(dap_chain_atom_iter_t *a_atom_iter, dap_chain_iter_op_t a_operation, size_t *a_atom_size);
 static dap_chain_atom_ptr_t *s_chain_callback_atom_iter_get_links( dap_chain_atom_iter_t * a_atom_iter , size_t *a_links_size,
                                                                   size_t ** a_links_size_ptr );  //    Get list of linked events
-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
 
 // Delete iterator
 static void s_chain_callback_atom_iter_delete(dap_chain_atom_iter_t * a_atom_iter );                  //    Get the fisrt event from dag
@@ -256,18 +251,14 @@ static int s_chain_cs_dag_new(dap_chain_t * a_chain, dap_config_t * a_chain_cfg)
     a_chain->callback_atom_get_hdr_static_size = s_chain_callback_atom_get_static_hdr_size; // Get dag event hdr size
 
     a_chain->callback_atom_iter_create = s_chain_callback_atom_iter_create;
-    a_chain->callback_atom_iter_create_from = s_chain_callback_atom_iter_create_from;
     a_chain->callback_atom_iter_delete = s_chain_callback_atom_iter_delete;
-    // 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_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;
+    a_chain->callback_atom_iter_get = s_chain_callback_atom_iter_get;               // Linear pass through
+    a_chain->callback_atom_find_by_hash = s_chain_callback_atom_iter_find_by_hash;  // Get element by hash
+    a_chain->callback_atom_iter_get_links = s_chain_callback_atom_iter_get_links;
 
     a_chain->callback_atom_get_datums = s_chain_callback_atom_get_datum;
     a_chain->callback_atom_get_timestamp = s_chain_callback_atom_get_timestamp;
 
-    a_chain->callback_atom_find_by_hash = s_chain_callback_atom_iter_find_by_hash;
     a_chain->callback_datum_find_by_hash = s_chain_callback_atom_find_by_datum_hash;
 
     a_chain->callback_add_datums = s_callback_add_datums;
@@ -453,6 +444,7 @@ static int s_dap_chain_add_atom_to_events_table(dap_chain_cs_dag_t *a_dag, dap_c
         PVT(a_dag)->tx_count++;
     a_event_item->datum_hash = l_datum_hash;
     a_event_item->ret_code = l_ret;
+    a_event_item->event_number = HASH_COUNT(PVT(a_dag)->events) + 1;
     unsigned l_hash_item_hashv;
     HASH_VALUE(&l_datum_hash, sizeof(l_datum_hash), l_hash_item_hashv);
     pthread_mutex_lock(&PVT(a_dag)->events_mutex);
@@ -1040,59 +1032,6 @@ static size_t s_chain_callback_atom_get_static_hdr_size()
    return sizeof (dap_chain_class_dag_event_hdr_t);
 }
 
-/**
- * @brief s_chain_callback_atom_iter_create_from
- * @param a_chain
- * @param a_atom
- * @return
- */
-static dap_chain_atom_iter_t* s_chain_callback_atom_iter_create_from(dap_chain_t * a_chain ,
-                                                                     dap_chain_atom_ptr_t a_atom, size_t a_atom_size)
-{
-    dap_chain_atom_iter_t * l_atom_iter = DAP_NEW_Z(dap_chain_atom_iter_t);
-    if (!l_atom_iter) {
-        log_it(L_CRITICAL, "Memory allocation error");
-        return NULL;
-    }
-    l_atom_iter->chain = a_chain;
-    l_atom_iter->cur = a_atom;
-    l_atom_iter->cur_size = a_atom_size;
-
-    if ( a_atom ){
-        dap_chain_hash_fast_t l_atom_hash;
-        dap_hash_fast(a_atom, a_atom_size, &l_atom_hash );
-
-        dap_chain_cs_dag_event_item_t  * l_atom_item;
-        HASH_FIND(hh, PVT(DAP_CHAIN_CS_DAG(a_chain))->events, &l_atom_hash, sizeof(l_atom_hash),l_atom_item );
-        l_atom_iter->cur_item = l_atom_item;
-        l_atom_iter->cur_hash = &l_atom_item->hash;
-    }
-    return l_atom_iter;
-
-}
-
-/**
- * @brief s_chain_callback_atom_iter_create Create atomic element iterator
- * @param a_chain
- * @return
- */
-static dap_chain_atom_iter_t *s_chain_callback_atom_iter_create(dap_chain_t *a_chain, dap_chain_cell_id_t a_cell_id, bool a_with_treshold)
-{
-    dap_chain_atom_iter_t * l_atom_iter = DAP_NEW_Z(dap_chain_atom_iter_t);
-    if (!l_atom_iter) {
-        log_it(L_CRITICAL, "Memory allocation error");
-        return NULL;
-    }
-    l_atom_iter->chain = a_chain;
-    l_atom_iter->cell_id = a_cell_id;
-    l_atom_iter->with_treshold = a_with_treshold;
-#ifdef WIN32
-    log_it(L_DEBUG, "! %p create caller id %lu", l_atom_iter, GetThreadId(GetCurrentThread()));
-#endif
-    return l_atom_iter;
-}
-
-
 /**
  * @brief s_chain_callback_atom_get_datum Get the datum from event
  * @param a_atom_iter
@@ -1119,102 +1058,6 @@ static dap_chain_datum_t **s_chain_callback_atom_get_datum(dap_chain_atom_ptr_t
     return l_datums;
 }
 
-/**
- * @brief s_chain_callback_atom_iter_get_first Get the first dag event
- * @param a_atom_iter
- * @return
- */
-static dap_chain_atom_ptr_t s_chain_callback_atom_iter_get_first(dap_chain_atom_iter_t * a_atom_iter, size_t * a_ret_size )
-{
-    if(! a_atom_iter){
-        log_it(L_ERROR, "NULL iterator on input for atom_iter_get_first function");
-        return NULL;
-    }
-    dap_chain_cs_dag_t * l_dag = DAP_CHAIN_CS_DAG(a_atom_iter->chain);
-    assert(l_dag);
-    dap_chain_cs_dag_pvt_t *l_dag_pvt = PVT(l_dag);
-    assert(l_dag_pvt);
-    a_atom_iter->cur_item = NULL;
-    dap_chain_cs_dag_event_item_t *l_item_tmp, *l_item_cur;
-    int found = 0;
-
-    pthread_mutex_lock(&PVT(l_dag)->events_mutex);
-    HASH_ITER(hh, l_dag_pvt->events, l_item_cur, l_item_tmp) {
-        if (l_item_cur->event->header.cell_id.uint64 == a_atom_iter->cell_id.uint64) {
-            a_atom_iter->cur_item = l_item_cur;
-            found = 1;
-            a_atom_iter->found_in_treshold = 0;
-            break;
-        }
-    }
-    if (!found && a_atom_iter->with_treshold) {
-        HASH_ITER(hh, l_dag_pvt->events_treshold, l_item_cur, l_item_tmp) {
-            if (l_item_cur->event->header.cell_id.uint64 == a_atom_iter->cell_id.uint64) {
-                a_atom_iter->cur_item = l_item_cur;
-                a_atom_iter->found_in_treshold = 1;
-                break;
-            }
-        }
-    }
-    pthread_mutex_unlock(&PVT(l_dag)->events_mutex);
-
-    if ( a_atom_iter->cur_item ){
-        a_atom_iter->cur = ((dap_chain_cs_dag_event_item_t*) a_atom_iter->cur_item)->event;
-        a_atom_iter->cur_size = ((dap_chain_cs_dag_event_item_t*) a_atom_iter->cur_item)->event_size;
-        a_atom_iter->cur_hash = &((dap_chain_cs_dag_event_item_t*) a_atom_iter->cur_item)->hash;
-    }else{
-        a_atom_iter->cur = NULL;
-        a_atom_iter->cur_size = 0;
-        a_atom_iter->cur_hash = NULL;
-    }
-
-    if (a_ret_size)
-        *a_ret_size = a_atom_iter->cur_size;
-    return a_atom_iter->cur;
-}
-
-
-/**
- * @brief s_chain_callback_atom_iter_get_lasts
- * @param a_atom_iter
- * @param a_lasts_size_ptr
- * @return
- */
-static dap_chain_atom_ptr_t* s_chain_callback_atom_iter_get_lasts( dap_chain_atom_iter_t * a_atom_iter ,size_t * a_lasts_size,
-                                                                  size_t ** a_lasts_size_array )
-{
-    dap_chain_cs_dag_t * l_dag = DAP_CHAIN_CS_DAG( a_atom_iter->chain );
-    dap_chain_atom_ptr_t * l_ret = NULL;
-    pthread_mutex_lock(&PVT(l_dag)->events_mutex);
-    size_t l_lasts_size = HASH_COUNT( PVT(l_dag)->events_lasts_unlinked );
-    if ( l_lasts_size > 0 ) {
-        if( a_lasts_size)
-            *a_lasts_size = l_lasts_size;
-        l_ret = DAP_NEW_Z_SIZE(dap_chain_atom_ptr_t, sizeof(dap_chain_atom_ptr_t) * l_lasts_size);
-        if (!l_ret) {
-            log_it(L_CRITICAL, "Memory allocation error");
-            pthread_mutex_unlock(&PVT(l_dag)->events_mutex);
-            return NULL;
-        }
-        dap_chain_cs_dag_event_item_t * l_event_item = NULL, *l_event_item_tmp = NULL;
-        size_t i = 0;
-        *a_lasts_size_array = DAP_NEW_Z_SIZE(size_t, sizeof(size_t) * l_lasts_size);
-        if (!*a_lasts_size_array) {
-            log_it(L_CRITICAL, "Memory allocation error");
-            pthread_mutex_unlock(&PVT(l_dag)->events_mutex);
-            DAP_DEL_Z(l_ret);
-            return NULL;
-        }
-        HASH_ITER(hh,PVT(l_dag)->events_lasts_unlinked, l_event_item,l_event_item_tmp){
-            l_ret[i] = l_event_item->event;
-            (*a_lasts_size_array)[i] = l_event_item->event_size;
-            i++;
-        }
-    }
-    pthread_mutex_unlock(&PVT(l_dag)->events_mutex);
-    return l_ret;
-}
-
 /**
  * @brief s_chain_callback_atom_iter_get_links
  * @param a_atom_iter
@@ -1278,111 +1121,102 @@ static dap_chain_atom_ptr_t* s_chain_callback_atom_iter_get_links( dap_chain_ato
 }
 
 /**
- * @brief s_chain_callback_atom_iter_find_by_hash
- * @param a_atom_iter
- * @param a_atom_hash
+ * @brief s_chain_callback_atom_iter_create Create atomic element iterator
+ * @param a_chain
  * @return
  */
-static dap_chain_atom_ptr_t s_chain_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_atom_iter_t *s_chain_callback_atom_iter_create(dap_chain_t *a_chain, dap_chain_cell_id_t a_cell_id, dap_hash_fast_t *a_hash_from)
 {
-    dap_chain_cs_dag_t * l_dag = DAP_CHAIN_CS_DAG( a_atom_iter->chain );
-    dap_chain_cs_dag_event_item_t * l_event_item = NULL;
-    pthread_mutex_lock(&PVT(l_dag)->events_mutex);
-    HASH_FIND(hh, PVT(l_dag)->events,a_atom_hash,sizeof(*a_atom_hash),l_event_item);
-    pthread_mutex_unlock(&PVT(l_dag)->events_mutex);
-    if ( l_event_item ){
-        a_atom_iter->cur_item = l_event_item;
-        a_atom_iter->cur = l_event_item->event;
-        a_atom_iter->cur_size= l_event_item->event_size;
-        a_atom_iter->cur_hash = &l_event_item->hash;
-        if(a_atom_size)
-            *a_atom_size = l_event_item->event_size;
-        return  l_event_item->event;
-    }else
+    dap_chain_atom_iter_t * l_atom_iter = DAP_NEW_Z(dap_chain_atom_iter_t);
+    if (!l_atom_iter) {
+        log_it(L_CRITICAL, "Memory allocation error");
         return NULL;
+    }
+    l_atom_iter->chain = a_chain;
+    l_atom_iter->cell_id = a_cell_id;
+    if (a_hash_from)
+        s_chain_callback_atom_iter_find_by_hash(l_atom_iter, a_hash_from, NULL);
+    return l_atom_iter;
 }
 
 /**
- * @brief s_chain_callback_atom_find_by_datum_hash
- * @param IN a_chain
- * @param IN a_datum_hash
- * @param OUT a_event_hash
- * @param OUT a_ret_code
+ * @brief s_chain_callback_atom_iter_get Get pointed dag event
+ * @param a_atom_iter
+ * @param a_opertaion
+ * @param a_atom_size
  * @return
  */
-static dap_chain_datum_t *s_chain_callback_atom_find_by_datum_hash(dap_chain_t *a_chain, dap_chain_hash_fast_t *a_datum_hash,
-                                                                   dap_chain_hash_fast_t *a_event_hash, int *a_ret_code)
+static dap_chain_atom_ptr_t s_chain_callback_atom_iter_get(dap_chain_atom_iter_t *a_atom_iter, dap_chain_iter_op_t a_operation, size_t *a_atom_size)
 {
-    dap_chain_cs_dag_t *l_dag = DAP_CHAIN_CS_DAG( a_chain );
-    dap_chain_cs_dag_event_item_t *l_event_item = NULL;
+    dap_return_val_if_fail(a_atom_iter, NULL);
+    dap_chain_cs_dag_t * l_dag = DAP_CHAIN_CS_DAG(a_atom_iter->chain);
+    assert(l_dag);
+    dap_chain_cs_dag_pvt_t *l_dag_pvt = PVT(l_dag);
+    assert(l_dag_pvt);
     pthread_mutex_lock(&PVT(l_dag)->events_mutex);
-    HASH_FIND(hh_datums, PVT(l_dag)->datums, a_datum_hash, sizeof(*a_datum_hash), l_event_item);
-    pthread_mutex_unlock(&PVT(l_dag)->events_mutex);
-    if ( l_event_item ){
-        dap_chain_datum_t *l_datum = dap_chain_cs_dag_event_get_datum(l_event_item->event, l_event_item->event_size);
-        if (l_datum && l_datum->header.data_size) {
-            if (a_event_hash)
-                *a_event_hash = l_event_item->hash;
-            if (a_ret_code)
-                *a_ret_code = l_event_item->ret_code;
-            return l_datum;
-        }
+    switch (a_operation) {
+    case DAP_CHAIN_ITER_OP_FIRST:
+        a_atom_iter->cur_item = l_dag_pvt->events;
+        break;
+    case DAP_CHAIN_ITER_OP_LAST:
+        HASH_LAST(l_dag_pvt->events, a_atom_iter->cur_item);
+        break;
+    case DAP_CHAIN_ITER_OP_NEXT:
+        if (a_atom_iter->cur_item)
+            a_atom_iter->cur_item = ((dap_chain_cs_dag_event_item_t *)a_atom_iter->cur_item)->hh.next;
+        break;
+    case DAP_CHAIN_ITER_OP_PREV:
+        if (a_atom_iter->cur_item)
+            a_atom_iter->cur_item = ((dap_chain_cs_dag_event_item_t *)a_atom_iter->cur_item)->hh.prev;
+        break;
     }
-    return NULL;
+    if (a_atom_iter->cur_item) {
+        dap_chain_cs_dag_event_item_t *l_item = a_atom_iter->cur_item;
+        a_atom_iter->cur = l_item->event;
+        a_atom_iter->cur_size = l_item->event_size;
+        a_atom_iter->cur_hash = &l_item->hash;
+        a_atom_iter->cur_num = l_item->event_number;
+    } else
+        *a_atom_iter = (dap_chain_atom_iter_t) { .chain = a_atom_iter->chain,
+                                                 .cell_id = a_atom_iter->cell_id };
+    pthread_mutex_unlock(&PVT(l_dag)->events_mutex);
+    if (a_atom_size)
+        *a_atom_size = a_atom_iter->cur_size;
+    return a_atom_iter->cur;
 }
 
 /**
- * @brief s_chain_callback_atom_iter_get_next Get the next dag event
+ * @brief s_chain_callback_atom_iter_find_by_hash
  * @param a_atom_iter
+ * @param a_atom_hash
  * @return
  */
-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 )
+static dap_chain_atom_ptr_t s_chain_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)
 {
-    dap_chain_cs_dag_event_item_t * l_event_item = (dap_chain_cs_dag_event_item_t*) a_atom_iter->cur_item;
-
-    while (l_event_item) {
-        l_event_item = (dap_chain_cs_dag_event_item_t *)l_event_item->hh.next;
-        if (l_event_item && l_event_item->event->header.cell_id.uint64 == a_atom_iter->cell_id.uint64)
-            break;
-    }
-
-    if(!l_event_item && !a_atom_iter->found_in_treshold && a_atom_iter->with_treshold) {
-        dap_chain_cs_dag_t *l_dag = DAP_CHAIN_CS_DAG(a_atom_iter->chain);
-        assert(l_dag);
-        dap_chain_cs_dag_pvt_t *l_dag_pvt = PVT(l_dag);
-        assert(l_dag_pvt);
-        l_event_item = l_dag_pvt->events_treshold;
-        pthread_mutex_lock(&PVT(l_dag)->events_mutex);
-        while (l_event_item) {
-            if (l_event_item && l_event_item->event->header.cell_id.uint64 == a_atom_iter->cell_id.uint64) {
-                a_atom_iter->found_in_treshold = 1;
-                break;
-            }
-            l_event_item = (dap_chain_cs_dag_event_item_t *)l_event_item->hh.next;
-        }
-        pthread_mutex_unlock(&PVT(l_dag)->events_mutex);
-    }
-    // if l_event_item=NULL then items are over
+    dap_chain_cs_dag_t *l_dag = DAP_CHAIN_CS_DAG(a_atom_iter->chain);
+    dap_chain_cs_dag_event_item_t *l_event_item = NULL;
+    pthread_mutex_lock(&PVT(l_dag)->events_mutex);
+    HASH_FIND(hh, PVT(l_dag)->events, a_atom_hash, sizeof(*a_atom_hash), l_event_item);
+    pthread_mutex_unlock(&PVT(l_dag)->events_mutex);
+    if (!l_event_item)
+        return NULL;
     a_atom_iter->cur_item = l_event_item;
-    a_atom_iter->cur = l_event_item ? l_event_item->event : NULL;
-    a_atom_iter->cur_size = a_atom_iter->cur ? l_event_item->event_size : 0;
-    a_atom_iter->cur_hash = l_event_item ? &l_event_item->hash : NULL;
-    if(a_atom_size)
-        *a_atom_size = a_atom_iter->cur_size;
-    return a_atom_iter->cur;
+    a_atom_iter->cur = l_event_item->event;
+    a_atom_iter->cur_size= l_event_item->event_size;
+    a_atom_iter->cur_hash = &l_event_item->hash;
+    a_atom_iter->cur_num = l_event_item->event_number;
+    if (a_atom_size)
+        *a_atom_size = l_event_item->event_size;
+    return l_event_item->event;
 }
 
-
 /**
  * @brief s_chain_callback_atom_iter_delete Delete dag event iterator
  * @param a_atom_iter
  */
-static void s_chain_callback_atom_iter_delete(dap_chain_atom_iter_t * a_atom_iter )
+static void s_chain_callback_atom_iter_delete(dap_chain_atom_iter_t *a_atom_iter)
 {
-#ifdef WIN32
-    log_it(L_DEBUG, "! Delete caller id %lu", GetThreadId(GetCurrentThread()));
-#endif
     DAP_DELETE(a_atom_iter);
 }
 
@@ -1419,6 +1253,36 @@ static void s_datum_iter_fill(dap_chain_datum_iter_t *a_datum_iter, dap_chain_cs
     }
 }
 
+/**
+ * @brief s_chain_callback_atom_find_by_datum_hash
+ * @param IN a_chain
+ * @param IN a_datum_hash
+ * @param OUT a_event_hash
+ * @param OUT a_ret_code
+ * @return
+ */
+static dap_chain_datum_t *s_chain_callback_atom_find_by_datum_hash(dap_chain_t *a_chain, dap_chain_hash_fast_t *a_datum_hash,
+                                                                   dap_chain_hash_fast_t *a_event_hash, int *a_ret_code)
+{
+    dap_chain_cs_dag_t *l_dag = DAP_CHAIN_CS_DAG( a_chain );
+    dap_chain_cs_dag_event_item_t *l_event_item = NULL;
+    pthread_mutex_lock(&PVT(l_dag)->events_mutex);
+    HASH_FIND(hh_datums, PVT(l_dag)->datums, a_datum_hash, sizeof(*a_datum_hash), l_event_item);
+    pthread_mutex_unlock(&PVT(l_dag)->events_mutex);
+    if ( l_event_item ){
+        dap_chain_datum_t *l_datum = dap_chain_cs_dag_event_get_datum(l_event_item->event, l_event_item->event_size);
+        if (l_datum && l_datum->header.data_size) {
+            if (a_event_hash)
+                *a_event_hash = l_event_item->hash;
+            if (a_ret_code)
+                *a_ret_code = l_event_item->ret_code;
+            return l_datum;
+        }
+    }
+    return NULL;
+}
+
+
 static dap_chain_datum_t *s_chain_callback_datum_iter_get_first(dap_chain_datum_iter_t *a_datum_iter)
 {
     dap_chain_cs_dag_t *l_dag = DAP_CHAIN_CS_DAG(a_datum_iter->chain);
@@ -2193,11 +2057,8 @@ static dap_list_t *s_callback_get_atoms(dap_chain_t *a_chain, size_t a_count, si
     size_t l_counter = 0;
     size_t l_end = l_offset + a_count;
 
-    dap_chain_cs_dag_event_item_t *l_ptr = l_dag_pvt->events->hh.tbl->tail->prev;
-    if (!l_ptr)
-        l_ptr = l_dag_pvt->events;
-    else
-        l_ptr = l_ptr->hh.next;
+    dap_chain_cs_dag_event_item_t *l_ptr;
+    HASH_LAST(l_dag_pvt->events, l_ptr);
     for (dap_chain_cs_dag_event_item_t *ptr = l_ptr; ptr != NULL && l_counter < l_end; ptr = ptr->hh.prev){
         if (l_counter >= l_offset){
             dap_chain_cs_dag_event_t *l_event = ptr->event;
diff --git a/modules/type/none/dap_chain_cs_none.c b/modules/type/none/dap_chain_cs_none.c
index f04e5104b6..67484a00d4 100644
--- a/modules/type/none/dap_chain_cs_none.c
+++ b/modules/type/none/dap_chain_cs_none.c
@@ -66,9 +66,7 @@ static dap_chain_atom_verify_res_t s_nonconsensus_callback_atom_add(dap_chain_t
 static dap_chain_atom_verify_res_t s_nonconsensus_callback_atom_verify(dap_chain_t * a_chain, dap_chain_atom_ptr_t, size_t); //    Verify new event in gdb
 static size_t s_nonconsensus_callback_atom_get_static_hdr_size(void); //    Get gdb event header size
 
-static dap_chain_atom_iter_t* s_nonconsensus_callback_atom_iter_create(dap_chain_t * a_chain, dap_chain_cell_id_t a_cell_id, bool a_with_treshold);
-static dap_chain_atom_iter_t* s_nonconsensus_callback_atom_iter_create_from(dap_chain_t * a_chain,
-        dap_chain_atom_ptr_t a, size_t a_atom_size);
+static dap_chain_atom_iter_t* s_nonconsensus_callback_atom_iter_create(dap_chain_t * a_chain, dap_chain_cell_id_t a_cell_id, dap_hash_fast_t *a_hash_from);
 
 // Delete iterator
 static void s_nonconsensus_callback_atom_iter_delete(dap_chain_atom_iter_t * a_atom_iter); //    Get the fisrt event from gdb
@@ -77,12 +75,8 @@ static dap_chain_atom_ptr_t s_nonconsensus_callback_atom_iter_find_by_hash(dap_c
         dap_chain_hash_fast_t * a_atom_hash, size_t * a_atom_size);
 
 // Get event(s) from gdb
-static dap_chain_atom_ptr_t s_nonconsensus_callback_atom_iter_get_first(dap_chain_atom_iter_t * a_atom_iter, size_t * a_atom_size); //    Get the fisrt event from gdb
-static dap_chain_atom_ptr_t s_nonconsensus_callback_atom_iter_get_next(dap_chain_atom_iter_t * a_atom_iter, size_t * a_atom_size); //    Get the next event from gdb
-static dap_chain_atom_ptr_t *s_nonconsensus_callback_atom_iter_get_links(dap_chain_atom_iter_t * a_atom_iter,
-        size_t * a_links_size_ptr, size_t ** a_lasts_sizes_ptr); //    Get list of linked events
-static dap_chain_atom_ptr_t *s_nonconsensus_callback_atom_iter_get_lasts(dap_chain_atom_iter_t * a_atom_iter,
-        size_t * a_lasts_size_ptr, size_t ** a_lasts_sizes_ptr); //    Get list of linked events
+static dap_chain_atom_ptr_t s_nonconsensus_callback_atom_iter_get(dap_chain_atom_iter_t *a_atom_iter, dap_chain_iter_op_t a_operation, size_t *a_atom_size);
+static dap_chain_atom_ptr_t *s_nonconsensus_callback_atom_iter_get_links(dap_chain_atom_iter_t *a_atom_iter, size_t *a_links_size_ptr, size_t **a_lasts_sizes_ptr); //    Get list of linked events
 static dap_chain_datum_t **s_nonconsensus_callback_atom_get_datum(dap_chain_atom_ptr_t a_atom, size_t a_atom_size, size_t *a_datums_count);
 static dap_time_t s_nonconsensus_callback_atom_get_timestamp(dap_chain_atom_ptr_t a_atom) { return ((dap_chain_datum_t *)a_atom)->header.ts_create; }
 static size_t s_nonconsensus_callback_datums_pool_proc(dap_chain_t * a_chain, dap_chain_datum_t ** a_datums,
@@ -191,15 +185,12 @@ static int s_cs_callback_new(dap_chain_t *a_chain, dap_config_t UNUSED_ARG *a_ch
     a_chain->callback_atom_get_hdr_static_size = s_nonconsensus_callback_atom_get_static_hdr_size; // Get dag event hdr size
 
     a_chain->callback_atom_iter_create = s_nonconsensus_callback_atom_iter_create;
-    a_chain->callback_atom_iter_create_from = s_nonconsensus_callback_atom_iter_create_from;
     a_chain->callback_atom_iter_delete = s_nonconsensus_callback_atom_iter_delete;
-    // Linear pass through
-    a_chain->callback_atom_iter_get_first = s_nonconsensus_callback_atom_iter_get_first; // Get the fisrt element from chain
-    a_chain->callback_atom_iter_get_next = s_nonconsensus_callback_atom_iter_get_next; // Get the next element from chain from the current one
+    a_chain->callback_atom_iter_get = s_nonconsensus_callback_atom_iter_get; // Linear pass through
     a_chain->callback_atom_find_by_hash = s_nonconsensus_callback_atom_iter_find_by_hash;
 
     a_chain->callback_atom_iter_get_links = s_nonconsensus_callback_atom_iter_get_links; // Get the next element from chain from the current one
-    a_chain->callback_atom_iter_get_lasts = s_nonconsensus_callback_atom_iter_get_lasts;
+
     a_chain->callback_atom_get_datums = s_nonconsensus_callback_atom_get_datum;
     a_chain->callback_atom_get_timestamp = s_nonconsensus_callback_atom_get_timestamp;
 
@@ -251,24 +242,6 @@ const char* dap_nonconsensus_get_group(dap_chain_t * a_chain)
 }
 
 
-/**
- * @brief compare_datum_items
- * @param l_a
- * @param l_b
- * @return
- */
-
-/*static int compare_datum_items(const void * l_a, const void * l_b)
-{
-    const dap_chain_datum_t *l_item_a = (const dap_chain_datum_t*) l_a;
-    const dap_chain_datum_t *l_item_b = (const dap_chain_datum_t*) l_b;
-    if(l_item_a->header.ts_create == l_item_b->header.ts_create)
-        return 0;
-    if(l_item_a->header.ts_create < l_item_b->header.ts_create)
-        return -1;
-    return 1;
-}*/
-
 /**
  * @brief s_ledger_load_callback
  * @param a_global_db_context
@@ -432,7 +405,7 @@ static size_t s_nonconsensus_callback_atom_get_static_hdr_size()
  * @param a_chain dap_chain_t a_chain
  * @return dap_chain_atom_iter_t*
  */
-static dap_chain_atom_iter_t* s_nonconsensus_callback_atom_iter_create(dap_chain_t * a_chain, dap_chain_cell_id_t a_cell_id, bool a_with_treshold)
+static dap_chain_atom_iter_t* s_nonconsensus_callback_atom_iter_create(dap_chain_t * a_chain, dap_chain_cell_id_t a_cell_id, dap_hash_fast_t *a_hash_from)
 {
     dap_chain_atom_iter_t * l_iter = DAP_NEW_Z(dap_chain_atom_iter_t);
     if (!l_iter) {
@@ -441,34 +414,11 @@ static dap_chain_atom_iter_t* s_nonconsensus_callback_atom_iter_create(dap_chain
     }
     l_iter->chain = a_chain;
     l_iter->cell_id = a_cell_id;
-    l_iter->with_treshold = a_with_treshold;
-    return l_iter;
-}
-
-/**
- * @brief create atom object (dap_chain_atom_iter_t)
- *
- * @param a_chain chain object
- * @param a_atom pointer to atom
- * @param a_atom_size size of atom
- * @return dap_chain_atom_iter_t*
- */
-static dap_chain_atom_iter_t* s_nonconsensus_callback_atom_iter_create_from(dap_chain_t * a_chain,
-        dap_chain_atom_ptr_t a_atom, size_t a_atom_size)
-{
-    dap_chain_atom_iter_t * l_iter = DAP_NEW_Z(dap_chain_atom_iter_t);
-    if (!l_iter) {
-        log_it(L_CRITICAL, "Memory allocation error");
-        return NULL;
-    }
-    l_iter->chain = a_chain;
-    l_iter->cur = a_atom;
-    l_iter->cur_size = a_atom_size;
-    dap_hash_fast(a_atom, a_atom_size, l_iter->cur_hash);
+    if (a_hash_from)
+        s_nonconsensus_callback_atom_iter_find_by_hash(l_iter, a_hash_from, NULL);
     return l_iter;
 }
 
-
 /**
  * @brief Delete dag event iterator
  * execute DAP_DELETE(a_atom_iter)
@@ -502,6 +452,7 @@ static dap_chain_atom_ptr_t s_nonconsensus_callback_atom_iter_find_by_hash(dap_c
         l_ret = dap_global_db_get_sync(PVT(l_nochain)->group_datums, l_key, &l_ret_size, NULL, NULL);
         *a_atom_size = l_ret_size;
     }
+    //TODO set a_atom_iter item field
     return l_ret;
 }
 
@@ -512,68 +463,45 @@ static dap_chain_atom_ptr_t s_nonconsensus_callback_atom_iter_find_by_hash(dap_c
  * @param a_atom_size a_atom_size atom size
  * @return dap_chain_atom_ptr_t
  */
-static dap_chain_atom_ptr_t s_nonconsensus_callback_atom_iter_get_first(dap_chain_atom_iter_t * a_atom_iter, size_t *a_atom_size)
+static dap_chain_atom_ptr_t s_nonconsensus_callback_atom_iter_get(dap_chain_atom_iter_t *a_atom_iter, dap_chain_iter_op_t a_operation, size_t *a_atom_size)
 {
-    if (!a_atom_iter)
-        return NULL;
+    dap_return_val_if_fail(a_atom_iter, NULL);
     if (a_atom_iter->cur_item) { /* Iterator creates copies, free them at delete routine! */
         DAP_DEL_Z(a_atom_iter->cur);
         DAP_DEL_Z(a_atom_iter->cur_hash);
     }
-    dap_chain_datum_t * l_datum = NULL;
-    dap_nonconsensus_datum_hash_item_t *l_item = PVT(DAP_NONCONSENSUS(a_atom_iter->chain))->hash_items;
-    a_atom_iter->cur_item = l_item;
-    if (a_atom_iter->cur_item) {
-        size_t l_datum_size = 0;
-        l_datum = (dap_chain_datum_t*)dap_global_db_get_sync(PVT(DAP_NONCONSENSUS(a_atom_iter->chain))->group_datums,
-                                                             l_item->key, &l_datum_size, NULL, NULL);
-        a_atom_iter->cur = l_datum;
-        a_atom_iter->cur_size = l_datum_size;
-        a_atom_iter->cur_hash = DAP_NEW_Z(dap_hash_fast_t);
-        dap_chain_hash_fast_from_str(l_item->key, a_atom_iter->cur_hash);
-        if (a_atom_size)
-            *a_atom_size = l_datum_size;
-    } else {
-        a_atom_iter->cur_size = 0;
-        if (a_atom_size)
-            *a_atom_size = 0;
+    dap_nonconsensus_datum_hash_item_t *l_head = PVT(DAP_NONCONSENSUS(a_atom_iter->chain))->hash_items;
+    switch (a_operation) {
+    case DAP_CHAIN_ITER_OP_FIRST:
+        a_atom_iter->cur_item = l_head;
+        break;
+    case DAP_CHAIN_ITER_OP_LAST:
+        a_atom_iter->cur_item = l_head ? l_head->prev : NULL;
+        break;
+    case DAP_CHAIN_ITER_OP_NEXT:
+        if (a_atom_iter->cur_item)
+            a_atom_iter->cur_item = ((dap_nonconsensus_datum_hash_item_t *)a_atom_iter->cur_item)->next;
+        break;
+    case DAP_CHAIN_ITER_OP_PREV:
+        if (a_atom_iter->cur_item)
+            a_atom_iter->cur_item = ((dap_nonconsensus_datum_hash_item_t *)a_atom_iter->cur_item)->prev->next
+                    ? ((dap_nonconsensus_datum_hash_item_t *)a_atom_iter->cur_item)->prev
+                    : NULL;
+        break;
     }
-    return l_datum;
-}
-
+    if (a_atom_iter->cur_item) {
+        dap_nonconsensus_datum_hash_item_t *l_item = a_atom_iter->cur_item;
 
-/**
- * @brief Get the next dag event from database
- *
- * @param a_atom_iter dap_chain_atom_iter_t
- * @param a_atom_size size_t a_atom_size
- * @return dap_chain_atom_ptr_t
- */
-static dap_chain_atom_ptr_t s_nonconsensus_callback_atom_iter_get_next(dap_chain_atom_iter_t *a_atom_iter, size_t *a_atom_size)
-{
-    dap_chain_datum_t * l_datum = NULL;
-    dap_nonconsensus_datum_hash_item_t *l_item = (dap_nonconsensus_datum_hash_item_t*)a_atom_iter->cur_item;
-    if (l_item)
-        l_item = l_item->next;
-    a_atom_iter->cur_item = l_item;
-    if (a_atom_iter->cur_item ){
-        size_t l_datum_size =0;
-        l_datum = (dap_chain_datum_t *)dap_global_db_get_sync(PVT(DAP_NONCONSENSUS(a_atom_iter->chain))->group_datums, l_item->key, &l_datum_size, NULL, NULL);
-        if (a_atom_iter->cur) // This iterator should clean up data for it because its allocate it
-            DAP_DELETE(a_atom_iter->cur);
-        a_atom_iter->cur = l_datum;
-        a_atom_iter->cur_size = l_datum_size;
+        a_atom_iter->cur = dap_global_db_get_sync(PVT(DAP_NONCONSENSUS(a_atom_iter->chain))->group_datums,
+                                                  l_item->key, &a_atom_iter->cur_size, NULL, NULL);
+        a_atom_iter->cur_hash = DAP_NEW_Z(dap_hash_fast_t);
         dap_chain_hash_fast_from_str(l_item->key, a_atom_iter->cur_hash);
-        if (a_atom_size)
-            *a_atom_size = l_datum_size;
-    } else {
-        DAP_DEL_Z(a_atom_iter->cur_hash);
-        DAP_DEL_Z(a_atom_iter->cur);
-        a_atom_iter->cur_size = 0;
-        if (a_atom_size)
-            *a_atom_size = 0;
-    }
-    return l_datum;
+    } else
+        *a_atom_iter = (dap_chain_atom_iter_t) { .chain = a_atom_iter->chain,
+                                                 .cell_id = a_atom_iter->cell_id };
+    if (a_atom_size)
+        *a_atom_size = a_atom_iter->cur_size;
+    return a_atom_iter->cur;
 }
 
 /**
@@ -593,23 +521,6 @@ static dap_chain_atom_ptr_t* s_nonconsensus_callback_atom_iter_get_links(dap_cha
     return NULL;
 }
 
-/**
- * @brief return null in current implementation
- *
- * @param a_atom_iter
- * @param a_lasts_size_ptr
- * @param a_links_sizes_ptr
- * @return dap_chain_atom_ptr_t*
- */
-static dap_chain_atom_ptr_t* s_nonconsensus_callback_atom_iter_get_lasts(dap_chain_atom_iter_t * a_atom_iter,
-        size_t * a_lasts_size_ptr,  size_t **a_links_sizes_ptr)
-{
-    (void) a_atom_iter;
-    (void) a_lasts_size_ptr;
-    (void) a_links_sizes_ptr;
-    return NULL;
-}
-
 /**
  * @brief get new datum object from atom
  *
-- 
GitLab