From 168c74b58c5e8dbf6abc696d8c98459c56227353 Mon Sep 17 00:00:00 2001
From: Roman Khlopkov <roman.khlopkov@demlabs.net>
Date: Wed, 8 Sep 2021 12:46:13 +0000
Subject: [PATCH] bugs-4779

---
 CMakeLists.txt                                |    2 +-
 modules/channel/chain/dap_stream_ch_chain.c   |  191 ++-
 modules/consensus/none/dap_chain_cs_none.c    |   10 +-
 modules/global-db/dap_chain_global_db.c       |  390 ++---
 .../global-db/dap_chain_global_db_driver.c    |  125 --
 .../dap_chain_global_db_driver_cdb.c          |   40 +-
 .../dap_chain_global_db_driver_sqlite.c       |  121 +-
 modules/global-db/dap_chain_global_db_hist.c  | 1254 ++---------------
 .../global-db/dap_chain_global_db_remote.c    |  200 ++-
 .../global-db/include/dap_chain_global_db.h   |   48 +-
 .../include/dap_chain_global_db_driver.h      |    6 -
 .../dap_chain_global_db_driver_sqlite.h       |    3 +
 .../include/dap_chain_global_db_hist.h        |   40 +-
 .../include/dap_chain_global_db_remote.h      |   12 +-
 modules/net/dap_chain_net.c                   |   76 +-
 modules/net/dap_chain_node.c                  |    3 +-
 modules/net/dap_chain_node_cli_cmd.c          |    2 +-
 modules/net/dap_chain_node_client.c           |    4 +-
 modules/net/include/dap_chain_net.h           |    4 +-
 modules/net/include/dap_chain_node.h          |    2 +-
 modules/net/srv/dap_chain_net_srv_order.c     |    5 +-
 21 files changed, 670 insertions(+), 1868 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7876c45e76..135a9c3e32 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,7 +2,7 @@ project(cellframe-sdk C)
 cmake_minimum_required(VERSION 3.0)
 
 set(CMAKE_C_STANDARD 11)
-set(CELLFRAME_SDK_NATIVE_VERSION "3.0-3")
+set(CELLFRAME_SDK_NATIVE_VERSION "3.0-4")
 add_definitions ("-DCELLFRAME_SDK_VERSION=\"${CELLFRAME_SDK_NATIVE_VERSION}\"")
 set(DAPSDK_MODULES "")
 message("Cellframe modules: ${CELLFRAME_MODULES}")
diff --git a/modules/channel/chain/dap_stream_ch_chain.c b/modules/channel/chain/dap_stream_ch_chain.c
index 7c8392f5b5..830f391af1 100644
--- a/modules/channel/chain/dap_stream_ch_chain.c
+++ b/modules/channel/chain/dap_stream_ch_chain.c
@@ -365,12 +365,11 @@ static void s_sync_out_gdb_last_worker_callback(dap_worker_t *a_worker, void *a_
     dap_stream_ch_chain_t *l_ch_chain = DAP_STREAM_CH_CHAIN( l_ch );
     s_sync_out_gdb_first_worker_callback(NULL,a_arg); // NULL to say callback not to delete request
 
-    dap_stream_ch_chain_sync_request_t l_request = {0};
     if (s_debug_more )
         log_it(L_INFO,"Out: DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB");
     dap_stream_ch_chain_pkt_write_unsafe(l_ch_chain->ch, DAP_STREAM_CH_CHAIN_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_ch_chain->request_hdr.cell_id.uint64, NULL, 0);
     l_ch_chain->state = CHAIN_STATE_IDLE;
     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,
@@ -386,27 +385,23 @@ static void s_sync_out_gdb_last_worker_callback(dap_worker_t *a_worker, void *a_
  */
 static bool s_sync_out_gdb_proc_callback(dap_proc_thread_t *a_thread, void *a_arg)
 {
-    struct sync_request *l_sync_request = (struct sync_request *) a_arg;
-
-    // Get log diff
-    uint64_t l_local_last_id = dap_db_log_get_last_id();
-    uint64_t l_start_item = l_sync_request->request.id_start ? l_sync_request->request.id_start + 1 : 0;
-    // If the current global_db has been truncated, but the remote node has not known this
-    if (l_sync_request->request.id_start > l_local_last_id) {
-        l_start_item = 0;
-    }
-    if (s_debug_more)
-        log_it(L_DEBUG, "Sync out gdb proc, requested transactions %"DAP_UINT64_FORMAT_u":%"DAP_UINT64_FORMAT_u" from address "NODE_ADDR_FP_STR,
-                            l_start_item, l_local_last_id, NODE_ADDR_FP_ARGS_S(l_sync_request->request.node_addr));
+    struct sync_request *l_sync_request = (struct sync_request *)a_arg;
     dap_chain_net_t *l_net = dap_chain_net_by_id(l_sync_request->request_hdr.net_id);
-    dap_list_t *l_add_groups = dap_chain_net_get_add_gdb_group(l_net, l_sync_request->request.node_addr);
-    dap_db_log_list_t *l_db_log = dap_db_log_list_start(1/*l_start_item*/, l_add_groups);
+    int l_flags = 0;
+    if (dap_chain_net_get_add_gdb_group(l_net, l_sync_request->request.node_addr))
+        l_flags |= F_DB_LOG_ADD_EXTRA_GROUPS;
+    if (!l_sync_request->request.id_start)
+        l_flags |= F_DB_LOG_SYNC_FROM_ZERO;
+    dap_db_log_list_t *l_db_log = dap_db_log_list_start(l_sync_request->request.node_addr, l_flags);
 
     if(l_db_log) {
+        if (s_debug_more)
+            log_it(L_DEBUG, "Sync out gdb proc, requested %"DAP_UINT64_FORMAT_u" transactions from address "NODE_ADDR_FP_STR,
+                             l_db_log->items_number, NODE_ADDR_FP_ARGS_S(l_sync_request->request.node_addr));
         l_sync_request->gdb.db_log = l_db_log;
-        dap_proc_thread_worker_exec_callback(a_thread, l_sync_request->worker->id, s_sync_out_gdb_first_worker_callback,l_sync_request );
+        dap_proc_thread_worker_exec_callback(a_thread, l_sync_request->worker->id, s_sync_out_gdb_first_worker_callback, l_sync_request );
     } else {
-        dap_proc_thread_worker_exec_callback(a_thread, l_sync_request->worker->id, s_sync_out_gdb_last_worker_callback,l_sync_request );
+        dap_proc_thread_worker_exec_callback(a_thread, l_sync_request->worker->id, s_sync_out_gdb_last_worker_callback, l_sync_request );
     }
     return true;
 }
@@ -421,10 +416,9 @@ static void s_sync_update_gdb_start_worker_callback(dap_worker_t *a_worker, void
         s_sync_request_delete(l_sync_request);
         return;
     }
-
     dap_stream_ch_chain_pkt_write_unsafe(l_ch, DAP_STREAM_CH_CHAIN_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, &l_sync_request->request, sizeof(dap_stream_ch_chain_sync_request_t));
+                                         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" "
                        "chain_id 0x%016"DAP_UINT64_FORMAT_x" cell_id 0x%016"DAP_UINT64_FORMAT_x"",
@@ -435,20 +429,19 @@ static void s_sync_update_gdb_start_worker_callback(dap_worker_t *a_worker, void
 static bool s_sync_update_gdb_proc_callback(dap_proc_thread_t *a_thread, void *a_arg)
 {
     struct sync_request *l_sync_request = (struct sync_request *)a_arg;
-    char l_buf[64];
-    if ( l_sync_request->request.id_end)
-        dap_sprintf(l_buf, "%"DAP_UINT64_FORMAT_u"",  l_sync_request->request.id_end);
-    else
-        strcpy(l_buf, "infinity");
-    log_it(L_DEBUG, "Prepare request to gdb sync from %"DAP_UINT64_FORMAT_u" to %s",
-                      l_sync_request->request.id_start + 1, l_buf);
+    log_it(L_DEBUG, "Prepare request to gdb sync from %s", l_sync_request->request.id_start ? "last sync" : "zero");
     dap_chain_net_t *l_net = dap_chain_net_by_id(l_sync_request->request_hdr.net_id);
-    l_sync_request->request.node_addr.uint64 = dap_chain_net_get_cur_addr_int(l_net);
     dap_stream_ch_t *l_ch = dap_stream_ch_find_by_uuid_unsafe(DAP_STREAM_WORKER(l_sync_request->worker), l_sync_request->ch_uuid);
     dap_stream_ch_chain_t *l_ch_chain = DAP_STREAM_CH_CHAIN(l_ch);
-    dap_list_t *l_add_groups = dap_chain_net_get_add_gdb_group(l_net, l_sync_request->request.node_addr);
-    dap_db_log_list_t *l_db_log = dap_db_log_list_start(l_sync_request->request.id_start + 1, l_add_groups);
+    int l_flags = 0;
+    if (dap_chain_net_get_add_gdb_group(l_net, l_sync_request->request.node_addr))
+        l_flags |= F_DB_LOG_ADD_EXTRA_GROUPS;
+    if (!l_sync_request->request.id_start)
+        l_flags |= F_DB_LOG_SYNC_FROM_ZERO;
+    dap_db_log_list_t *l_db_log = dap_db_log_list_start(l_sync_request->request.node_addr, l_flags);
     l_ch_chain->request_db_log = l_db_log;
+    l_ch_chain->state = CHAIN_STATE_UPDATE_GLOBAL_DB;
+    l_sync_request->request.node_addr.uint64 = dap_chain_net_get_cur_addr_int(l_net);
     dap_proc_thread_worker_exec_callback(a_thread, l_sync_request->worker->id, s_sync_update_gdb_start_worker_callback, l_sync_request);
     return true;
 }
@@ -607,7 +600,7 @@ static bool s_gdb_in_pkt_proc_callback(dap_proc_thread_t *a_thread, void *a_arg)
     if (l_pkt_item->pkt_data_size) {
         size_t l_data_obj_count = 0;
         // deserialize data & Parse data from dap_db_log_pack()
-        dap_store_obj_t *l_store_obj = dap_db_log_unpack(l_pkt_item->pkt_data, l_pkt_item->pkt_data_size, &l_data_obj_count);
+        dap_store_obj_t *l_store_obj = dap_store_unpacket_multiple((dap_store_obj_pkt_t *)l_pkt_item->pkt_data, &l_data_obj_count);
         if (s_debug_more){
             if (l_data_obj_count)
                 log_it(L_INFO, "In: GLOBAL_DB parse: pkt_data_size=%zd, l_data_obj_count = %d",l_pkt_item->pkt_data_size, l_data_obj_count );
@@ -617,46 +610,25 @@ static bool s_gdb_in_pkt_proc_callback(dap_proc_thread_t *a_thread, void *a_arg)
                  log_it(L_WARNING, "In: GLOBAL_DB parse: packet in list with NULL data(pkt_data_size:%zd)", l_pkt_item->pkt_data_size);
         }
 
-        for(size_t i = 0; i < l_data_obj_count; i++) {
-            // timestamp for exist obj
-            time_t l_timestamp_cur = 0;
+        for (size_t i = 0; i < l_data_obj_count; i++) {
             // obj to add
-            dap_store_obj_t* l_obj = l_store_obj + i;
-            // read item from base;
-            size_t l_count_read = 0;
-            dap_store_obj_t *l_read_obj = dap_chain_global_db_driver_read(l_obj->group,
-                    l_obj->key, &l_count_read);
-            // get timestamp for the exist entry
-            if(l_read_obj)
-                l_timestamp_cur = l_read_obj->timestamp;
-            // get timestamp for the deleted entry
-            else
-            {
-                l_timestamp_cur = global_db_gr_del_get_timestamp(l_obj->group, l_obj->key);
-            }
-
+            dap_store_obj_t *l_obj = l_store_obj + i;
             //check whether to apply the received data into the database
-            bool l_apply = true;
-            if(l_obj->timestamp < l_timestamp_cur)
-                l_apply = false;
-            else if(l_obj->type == 'd') {
-                // already deleted
-                if(!l_read_obj)
-                    l_apply = false;
+            bool l_apply = false;
+            // timestamp for exist obj
+            time_t l_timestamp_cur = 0;
+            if (dap_chain_global_db_driver_is(l_obj->group, l_obj->key)) {
+                dap_store_obj_t *l_read_obj = dap_chain_global_db_driver_read(l_obj->group, l_obj->key, NULL);
+                if (l_read_obj) {
+                    l_timestamp_cur = l_read_obj->timestamp;
+                    dap_store_obj_free(l_read_obj, 1);
+                }
             }
-            else if(l_obj->type == 'a') {
-                bool l_is_the_same_present = false;
-                if(l_read_obj &&
-                        l_read_obj->value_len == l_obj->value_len &&
-                        !memcmp(l_read_obj->value, l_obj->value, l_obj->value_len))
-                    l_is_the_same_present = true;
-                // this data already present in global_db and not obsolete (out of date)
-                if(l_read_obj && (l_is_the_same_present || l_read_obj->timestamp >= l_store_obj->timestamp))
-                    l_apply = false;
+            // check the applied object newer that we have stored or erased
+            if (l_obj->timestamp > global_db_gr_del_get_timestamp(l_obj->group, l_obj->key) &&
+                    l_obj->timestamp > l_timestamp_cur) {
+                l_apply = true;
             }
-            if(l_read_obj)
-                dap_store_obj_free(l_read_obj, l_count_read);
-
             if (s_debug_more){
                 char l_ts_str[50];
                 dap_time_to_str_rfc822(l_ts_str, sizeof(l_ts_str), l_store_obj[i].timestamp);
@@ -665,14 +637,7 @@ static bool s_gdb_in_pkt_proc_callback(dap_proc_thread_t *a_thread, void *a_arg)
                         (char ) l_store_obj[i].type, l_store_obj[i].type, l_store_obj[i].group,
                         l_store_obj[i].key, l_ts_str, l_store_obj[i].value_len);
             }
-
-            if(!l_apply) {
-                // Object is already in database, but haven't track in history, why?
-                dap_global_db_obj_track_history(l_obj, 1);
-                // If request was from defined node_addr we update its state
-                if(l_sync_request->request.node_addr.uint64) {
-                    dap_db_set_last_id_remote(l_sync_request->request.node_addr.uint64, l_obj->id);
-                }
+            if (!l_apply) {
                 continue;
             }
 
@@ -680,7 +645,7 @@ static bool s_gdb_in_pkt_proc_callback(dap_proc_thread_t *a_thread, 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);
             if(l_chain) {
                 if(l_chain->callback_add_datums_with_group){
-                    void * restrict l_store_obj_value = l_store_obj->value;
+                    void * restrict l_store_obj_value = l_store_obj[i].value;
                     l_chain->callback_add_datums_with_group(l_chain,
                             (dap_chain_datum_t** restrict) l_store_obj_value, 1,
                             l_store_obj[i].group);
@@ -688,17 +653,11 @@ static bool s_gdb_in_pkt_proc_callback(dap_proc_thread_t *a_thread, void *a_arg)
             }
             // save data to global_db
             if(!dap_chain_global_db_obj_save(l_obj, 1)) {
-                if(l_store_obj)
-                    dap_store_obj_free(l_store_obj, l_data_obj_count);
-                dap_proc_thread_worker_exec_callback(a_thread, l_sync_request->worker->id, s_gdb_in_pkt_error_worker_callback, l_sync_request);
-                return true;
+                dap_proc_thread_worker_exec_callback(a_thread, l_sync_request->worker->id,
+                                                     s_gdb_in_pkt_error_worker_callback, l_sync_request);
             } else {
-                // If request was from defined node_addr we update its state
-                if(l_sync_request->request.node_addr.uint64) {
-                    dap_db_set_last_id_remote(l_sync_request->request.node_addr.uint64, l_obj->id);
-                }
                 if (s_debug_more)
-                    log_it(L_DEBUG, "Added new GLOBAL_DB history pack");
+                    log_it(L_DEBUG, "Added new GLOBAL_DB synchronization record");
             }
         }
         if(l_store_obj)
@@ -808,21 +767,22 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
             if(s_debug_more)
                 log_it(L_INFO, "In:  UPDATE_GLOBAL_DB_REQ pkt: net 0x%016x chain 0x%016x cell 0x%016x", l_chain_pkt->hdr.net_id.uint64 ,
                                     l_chain_pkt->hdr.chain_id.uint64, l_chain_pkt->hdr.cell_id.uint64);
-            l_ch_chain->state = CHAIN_STATE_UPDATE_GLOBAL_DB;
             if (l_chain_pkt_data_size == sizeof(dap_stream_ch_chain_sync_request_t))
                 memcpy(&l_ch_chain->request, l_chain_pkt->data, sizeof(dap_stream_ch_chain_sync_request_t));
-            struct sync_request *l_sync_request = dap_stream_ch_chain_create_sync_request(l_chain_pkt, a_ch);
-            l_sync_request->request.id_start = dap_db_get_last_id_remote(l_sync_request->request.node_addr.uint64);
             dap_chain_node_client_t *l_client = (dap_chain_node_client_t *)l_ch_chain->callback_notify_arg;
             if (l_client && l_client->resync_gdb)
-                l_sync_request->request.id_start = 0;
+                l_ch_chain->request.id_start = 0;
+            else
+                l_ch_chain->request.id_start = 1;   // incremental sync by default
+            struct sync_request *l_sync_request = dap_stream_ch_chain_create_sync_request(l_chain_pkt, a_ch);
             l_ch_chain->stats_request_gdb_processed = 0;
             dap_proc_queue_add_callback_inter(a_ch->stream_worker->worker->proc_queue_input, s_sync_update_gdb_proc_callback, l_sync_request);
         } break;
 
         // Response with metadata organized in TSD
         case DAP_STREAM_CH_CHAIN_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
@@ -838,8 +798,6 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                 break;
             }
             memcpy(&l_ch_chain->request_hdr, &l_chain_pkt->hdr, sizeof(dap_stream_ch_chain_pkt_t));
-            if (l_chain_pkt_data_size == sizeof(dap_stream_ch_chain_sync_request_t))
-                memcpy(&l_ch_chain->request, l_chain_pkt->data, sizeof(dap_stream_ch_chain_sync_request_t));
             l_ch_chain->state = CHAIN_STATE_UPDATE_GLOBAL_DB_REMOTE;
         } break;
         // Response with gdb element hashes and sizes
@@ -868,7 +826,7 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                         char l_hash_str[72]={ [0]='\0'};
                         dap_chain_hash_fast_to_str(&l_hash_item->hash,l_hash_str,sizeof (l_hash_str));
                         log_it(L_DEBUG,"In: Updated remote hash gdb list with %s ", l_hash_str);
-                    } */
+                    }*/
                 }
             }
         } break;
@@ -899,8 +857,9 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                     else
                         log_it(L_INFO, "In: SYNC_GLOBAL_DB pkt");
                 }
+                if (l_chain_pkt_data_size == sizeof(dap_stream_ch_chain_sync_request_t))
+                    memcpy(&l_ch_chain->request, l_chain_pkt->data, sizeof(dap_stream_ch_chain_sync_request_t));
                 struct sync_request *l_sync_request = dap_stream_ch_chain_create_sync_request(l_chain_pkt, a_ch);
-                l_ch_chain->state = CHAIN_STATE_SYNC_GLOBAL_DB;
                 dap_proc_queue_add_callback_inter(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_SYNC_GLOBAL_DB: Wrong chain packet size %zd when expected %zd", l_chain_pkt_data_size, sizeof(l_ch_chain->request));
@@ -958,7 +917,7 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
 
         case DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNC_GLOBAL_DB_RVRS: {
             dap_stream_ch_chain_sync_request_t l_sync_gdb = {};
-            l_sync_gdb.id_start = dap_db_get_last_id_remote(l_sync_gdb.node_addr.uint64);
+            l_sync_gdb.id_start = 1;
             dap_chain_net_t *l_net = dap_chain_net_by_id(l_chain_pkt->hdr.net_id);
             l_sync_gdb.node_addr.uint64 = dap_chain_net_get_cur_addr_int(l_net);
             log_it(L_INFO, "In:  SYNC_GLOBAL_DB_RVRS pkt: net 0x%016x chain 0x%016x cell 0x%016x, request gdb sync from %u", l_chain_pkt->hdr.net_id.uint64 ,
@@ -1007,7 +966,8 @@ 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_CHAINS_TSD :{
-            log_it(L_DEBUG, "Chain TSD packet detected");
+            if (s_debug_more)
+                log_it(L_DEBUG, "Chain TSD packet detected");
         } break;
 
         // If requested - begin to send atom hashes
@@ -1326,7 +1286,7 @@ void s_stream_ch_packet_out(dap_stream_ch_t* a_ch, void* a_arg)
                 if (!l_obj)
                     break;
                 memcpy(&l_data[i].hash, &l_obj->hash, sizeof(dap_chain_hash_fast_t));
-                l_data[i].size = l_obj->obj->value_len;
+                l_data[i].size = l_obj->pkt->data_size;
             }
             if (i) {
                 dap_stream_ch_chain_pkt_write_unsafe(l_ch_chain->ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB,
@@ -1337,12 +1297,13 @@ void s_stream_ch_packet_out(dap_stream_ch_t* a_ch, void* a_arg)
                 if (s_debug_more)
                     log_it(L_INFO, "Out: DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB");
             } else {
-                dap_stream_ch_chain_sync_request_t l_request = {};
+                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));
                 dap_stream_ch_chain_pkt_write_unsafe(a_ch, DAP_STREAM_CH_CHAIN_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_request, sizeof(dap_stream_ch_chain_sync_request_t));
+                                                     &l_ch_chain->request, sizeof(dap_stream_ch_chain_sync_request_t));
                 if (s_debug_more )
                     log_it(L_INFO, "Out: DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_END");
                 dap_stream_ch_chain_go_idle(l_ch_chain);
@@ -1353,34 +1314,41 @@ void s_stream_ch_packet_out(dap_stream_ch_t* a_ch, void* a_arg)
         case CHAIN_STATE_SYNC_GLOBAL_DB: {
             // Get global DB record
             dap_store_obj_pkt_t *l_pkt = NULL;
-            dap_db_log_list_obj_t *l_obj = dap_db_log_list_get(l_ch_chain->request_db_log);
+            dap_db_log_list_obj_t *l_obj = NULL;
             size_t l_pkt_size = 0;
-            uint_fast16_t l_skip_count = 0;
-            while (l_obj && l_skip_count < s_skip_in_reactor_count) {
+            for (uint_fast16_t l_skip_count = 0; l_skip_count < s_skip_in_reactor_count; ) {
+                l_obj = dap_db_log_list_get(l_ch_chain->request_db_log);
+                if (!l_obj)
+                    break;
                 dap_stream_ch_chain_hash_item_t *l_hash_item = NULL;
                 HASH_FIND(hh, l_ch_chain->remote_gdbs, &l_obj->hash, sizeof(dap_hash_fast_t), l_hash_item);
                 if (l_hash_item) { // If found - skip it
-                    if (s_debug_more) {
+                    /*if (s_debug_more) {
                         char l_request_atom_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE];
                         dap_chain_hash_fast_to_str(&l_obj->hash, l_request_atom_hash_str, DAP_CHAIN_HASH_FAST_STR_SIZE);
                         log_it(L_DEBUG, "Out CHAIN: skip GDB hash %s because its already present in remote GDB hash table",
                                         l_request_atom_hash_str);
-                    }
+                    }*/
                     l_skip_count++;
                 } else {
+                    if (l_ch_chain->request.node_addr.uint64) {
+                        dap_db_set_last_id_remote(l_ch_chain->request.node_addr.uint64,
+                                                  dap_store_packet_get_id(l_obj->pkt),
+                                                  dap_store_packet_get_group(l_obj->pkt));
+                    }
                     l_hash_item = DAP_NEW(dap_stream_ch_chain_hash_item_t);
                     memcpy(&l_hash_item->hash, &l_obj->hash, sizeof(dap_chain_hash_fast_t));
-                    l_hash_item->size = l_obj->obj->value_len;
+                    l_hash_item->size = l_obj->pkt->data_size;
                     HASH_ADD(hh, l_ch_chain->remote_gdbs, hash, sizeof(dap_chain_hash_fast_t), l_hash_item);
-                    l_pkt = dap_store_packet_multiple(l_obj->obj, 0, l_pkt);
+                    l_pkt = dap_store_packet_multiple(l_pkt, l_obj->pkt);
                     l_ch_chain->stats_request_gdb_processed++;
                     l_pkt_size = sizeof(dap_store_obj_pkt_t) + l_pkt->data_size;
                     if (l_pkt_size >= DAP_CHAIN_PKT_EXPECT_SIZE)
                         break;
                 }
-                l_obj = dap_db_log_list_get(l_ch_chain->request_db_log);
             }
             if (l_pkt_size) {
+                // If request was from defined node_addr we update its state
                 if( s_debug_more)
                     log_it(L_INFO, "Send one global_db packet len=%d (rest=%d/%d items)", l_pkt_size,
                                     dap_db_log_list_get_count_rest(l_ch_chain->request_db_log),
@@ -1389,9 +1357,14 @@ void s_stream_ch_packet_out(dap_stream_ch_t* a_ch, void* a_arg)
                                                      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);
+            } else if (l_obj) {
+                // Sending dumb packet with nothing to inform remote thats we're just skiping GDBs, nothing freezed
+                dap_stream_ch_chain_pkt_write_unsafe(a_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_UPDATE_GLOBAL_DB_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);
             } else {
-                log_it( L_INFO,"Syncronized database:  last id %"DAP_UINT64_FORMAT_u", items syncronyzed %"DAP_UINT64_FORMAT_u"", dap_db_log_get_last_id(),
-                                    l_ch_chain->stats_request_gdb_processed );
+                log_it( L_INFO,"Syncronized database: items syncronyzed %"DAP_UINT64_FORMAT_u" from %"DAP_UINT64_FORMAT_u"",
+                        l_ch_chain->stats_request_gdb_processed, dap_db_log_list_get_count(l_ch_chain->request_db_log));
                 // last message
                 dap_stream_ch_chain_sync_request_t l_request = {};
                 dap_stream_ch_chain_pkt_write_unsafe(a_ch, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_GLOBAL_DB,
diff --git a/modules/consensus/none/dap_chain_cs_none.c b/modules/consensus/none/dap_chain_cs_none.c
index 707465f288..b3bae67c29 100644
--- a/modules/consensus/none/dap_chain_cs_none.c
+++ b/modules/consensus/none/dap_chain_cs_none.c
@@ -128,7 +128,7 @@ int dap_chain_gdb_init(void)
  * @param a_value
  * @param a_value_len
  */
-static void s_history_callback_notify(void * a_arg, const char a_op_code, const char * a_prefix, const char * a_group,
+static void s_history_callback_notify(void * a_arg, const char a_op_code, const char * a_group,
         const char * a_key, const void * a_value, const size_t a_value_size)
 {
     if (a_arg){
@@ -136,8 +136,8 @@ static void s_history_callback_notify(void * a_arg, const char a_op_code, const
         dap_chain_net_t *l_net = dap_chain_net_by_id( l_gdb->chain->net_id);
         log_it(L_DEBUG,"%s.%s: op_code='%c' group=\"%s\" key=\"%s\" value_size=%u",l_net->pub.name,
                l_gdb->chain->name, a_op_code, a_group, a_key, a_value_size);
-        dap_chain_node_mempool_autoproc_notify((void *)l_net, a_op_code, a_prefix, a_group, a_key, a_value, a_value_size);
-        dap_chain_net_sync_gdb_broadcast((void *)l_net, a_op_code, a_prefix, a_group, a_key, a_value, a_value_size);
+        dap_chain_node_mempool_autoproc_notify((void *)l_net, a_op_code, a_group, a_key, a_value, a_value_size);
+        dap_chain_net_sync_gdb_broadcast((void *)l_net, a_op_code, a_group, a_key, a_value, a_value_size);
     }
 }
 
@@ -174,9 +174,7 @@ int dap_chain_gdb_new(dap_chain_t * a_chain, dap_config_t * a_chain_cfg)
     }
 
     // Add group prefix that will be tracking all changes
-    dap_chain_global_db_add_history_group_prefix("chain-gdb", GROUP_LOCAL_HISTORY);
-
-    dap_chain_global_db_add_history_callback_notify("chain-gdb", s_history_callback_notify,l_gdb);
+    dap_chain_global_db_add_sync_group("chain-gdb", s_history_callback_notify, l_gdb);
 
     // load ledger
     l_gdb_priv->is_load_mode = true;
diff --git a/modules/global-db/dap_chain_global_db.c b/modules/global-db/dap_chain_global_db.c
index b844ddca94..829c573e04 100644
--- a/modules/global-db/dap_chain_global_db.c
+++ b/modules/global-db/dap_chain_global_db.c
@@ -62,137 +62,68 @@ static inline void unlock()
 }
 
 // Callback table item
-typedef struct history_group_item
+typedef struct sync_group_item
 {
-    char prefix[32];
-    uint8_t padding[7];
-    bool auto_track; // Track history actions automaticly
-    dap_global_db_obj_callback_notify_t callback_notify;
-    void * callback_arg;
-    char *group_name_for_history;
-    UT_hash_handle hh;
-} history_group_item_t;
-
-// Callback table item
-typedef struct history_extra_group_item
-{
-    char *group_name;
+    char *group_mask;
     char *group_name_for_history;
     dap_global_db_obj_callback_notify_t callback_notify;
     void * callback_arg;
     UT_hash_handle hh;
-} history_extra_group_item_t;
+} sync_group_item_t;
 
 // Tacked group callbacks
-static history_group_item_t * s_history_group_items = NULL;
-static history_extra_group_item_t * s_history_extra_group_items = NULL;
-char * extract_group_prefix(const char * a_group);
+static sync_group_item_t *s_sync_group_items = NULL;
+static sync_group_item_t *s_sync_group_extra_items = NULL;
+static bool s_track_history = false;
 
 /**
- * @brief extract_group_prefix
- * @param a_group
- * @return
- */
-char * extract_group_prefix(const char* a_group)
-{
-    char * l_group_prefix = NULL, *l_delimeter;
-    size_t l_group_prefix_size;
-
-//    l_delimeter = index(a_group, '.');
-    l_delimeter = strchr(a_group, '.');
-
-    if(l_delimeter == NULL) {
-        l_group_prefix = dap_strdup(a_group);
-        l_group_prefix_size = dap_strlen(l_group_prefix) + 1;
-    } else {
-        l_group_prefix_size = (size_t) l_delimeter - (size_t) a_group;
-        if(l_group_prefix_size > 1)
-            l_group_prefix = strndup(a_group, l_group_prefix_size);
-    }
-    return l_group_prefix;
-}
-
-
-/*
- * Get history group by group name
+ * @brief dap_chain_global_db_add_sync_group
+ * @details Add group name for synchronization
+ * @param a_group_prefix
  */
-char* dap_chain_global_db_get_history_group_by_group_name(const char * a_group_name)
+void dap_chain_global_db_add_sync_group(const char *a_group_prefix, dap_global_db_obj_callback_notify_t a_callback, void *a_arg)
 {
-    if(!s_history_extra_group_items || !a_group_name)
-        return NULL;
-    history_extra_group_item_t * l_history_extra_group_item = NULL;
-    HASH_FIND_STR(s_history_extra_group_items, a_group_name, l_history_extra_group_item);
-    if(l_history_extra_group_item) {
-        return dap_strdup(l_history_extra_group_item->group_name_for_history);
-    }else
-        return NULL;
+    sync_group_item_t * l_item = DAP_NEW_Z(sync_group_item_t);
+    l_item->group_mask = dap_strdup_printf("%s.*", a_group_prefix);
+    l_item->group_name_for_history = dap_strdup(GROUP_LOCAL_HISTORY);
+    l_item->callback_notify = a_callback;
+    l_item->callback_arg = a_arg;
+    HASH_ADD_STR(s_sync_group_items, group_mask, l_item);
 }
 
 /**
- * @brief dap_chain_global_db_add_history_group_prefix
- * @details Add group prefix that will be tracking all changes
+ * @brief dap_chain_global_db_add_sync_extra_group
+ * @details Add group name for synchronization with especially node addresses
  * @param a_group_prefix
- * @param a_group_name_for_history
  */
-void dap_chain_global_db_add_history_group_prefix(const char * a_group_prefix, const char * a_group_name_for_history)
+void dap_chain_global_db_add_sync_extra_group(const char *a_group_mask, dap_global_db_obj_callback_notify_t a_callback, void *a_arg)
 {
-    history_group_item_t * l_item = DAP_NEW_Z(history_group_item_t);
-    snprintf(l_item->prefix, sizeof(l_item->prefix), "%s", a_group_prefix);
-    l_item->group_name_for_history = dap_strdup(a_group_name_for_history);//GROUP_LOCAL_HISTORY
-    l_item->auto_track = true;
-    HASH_ADD_STR(s_history_group_items, prefix, l_item);
+    sync_group_item_t* l_item = DAP_NEW_Z(sync_group_item_t);
+    l_item->group_mask = dap_strdup(a_group_mask);
+    l_item->group_name_for_history = dap_strdup(GROUP_LOCAL_HISTORY".extra");
+    l_item->callback_notify = a_callback;
+    l_item->callback_arg = a_arg;
+    HASH_ADD_STR(s_sync_group_extra_items, group_mask, l_item);
 }
 
-/**
- * @brief dap_chain_global_db_add_history_callback_notify
- * @param a_group_prefix
- * @param a_callback
- */
-void dap_chain_global_db_add_history_callback_notify(const char * a_group_prefix,
-        dap_global_db_obj_callback_notify_t a_callback, void * a_arg)
+dap_list_t *dap_chain_db_get_sync_groups_internal(sync_group_item_t *a_table)
 {
-    history_group_item_t * l_item = NULL;
-    HASH_FIND_STR(s_history_group_items, a_group_prefix, l_item);
-    if(l_item) {
-        l_item->callback_notify = a_callback;
-        l_item->callback_arg = a_arg;
-    } else
-        log_it(L_WARNING, "Can't setup notify callback for groups with prefix %s. Possible not in history track state",
-                a_group_prefix);
+    dap_list_t *l_ret = NULL;
+    sync_group_item_t *l_item = NULL, *l_item_tmp = NULL;
+    HASH_ITER(hh, a_table, l_item, l_item_tmp) {
+        l_ret = dap_list_append(l_ret, l_item->group_mask);
+    }
+    return l_ret;
 }
 
-/**
- * @brief dap_chain_global_db_add_history_extra_group
- * @details Add group prefix that will be tracking all changes
- * @param a_group_prefix
- */
-const char* dap_chain_global_db_add_history_extra_group(const char * a_group_name, dap_chain_node_addr_t *a_nodes, uint16_t *a_nodes_count)
+dap_list_t *dap_chain_db_get_sync_groups()
 {
-    UNUSED(a_nodes);
-    UNUSED(a_nodes_count);
-    history_extra_group_item_t* l_item = DAP_NEW_Z(history_extra_group_item_t);
-    l_item->group_name = dap_strdup(a_group_name);
-    l_item->group_name_for_history = dap_strdup_printf(GROUP_LOCAL_HISTORY".%s", a_group_name);
-    HASH_ADD_STR(s_history_extra_group_items, group_name, l_item);
-    return (const char*)l_item->group_name_for_history;
+    return dap_chain_db_get_sync_groups_internal(s_sync_group_items);
 }
 
-/**
- * @brief dap_chain_global_db_add_history_extra_group_callback_notify
- * @param a_group_prefix
- * @param a_callback
- */
-void dap_chain_global_db_add_history_extra_group_callback_notify(const char * a_group_prefix,
-        dap_global_db_obj_callback_notify_t a_callback, void * a_arg)
+dap_list_t *dap_chain_db_get_sync_extra_groups()
 {
-    history_extra_group_item_t * l_item = NULL;
-    HASH_FIND_STR(s_history_extra_group_items, a_group_prefix, l_item);
-    if(l_item) {
-        l_item->callback_notify = a_callback;
-        l_item->callback_arg = a_arg;
-    } else
-        log_it(L_WARNING, "Can't setup notify callback for extra groups with prefix %s. Possible not in history track state",
-                a_group_prefix);
+    return dap_chain_db_get_sync_groups_internal(s_sync_group_extra_items);
 }
 
 /**
@@ -222,13 +153,8 @@ void dap_chain_global_db_obj_delete(dap_global_db_obj_t *obj)
  */
 void dap_chain_global_db_objs_delete(dap_global_db_obj_t *objs, size_t a_count)
 {
-    //int i = 0;
-    //while(objs) {
     for(size_t i = 0; i < a_count; i++) {
-        //if(!(objs[i]))
-        //    break;
         dap_chain_global_db_obj_clean(objs + i);
-        //i++;
     }
     DAP_DELETE(objs);
 }
@@ -241,8 +167,9 @@ void dap_chain_global_db_objs_delete(dap_global_db_obj_t *objs, size_t a_count)
 int dap_chain_global_db_init(dap_config_t * g_config)
 {
     const char *l_storage_path = dap_config_get_item_str(g_config, "resources", "dap_global_db_path");
-    //const char *l_driver_name = dap_config_get_item_str_default(g_config, "resources", "dap_global_db_driver", "sqlite");
-    const char *l_driver_name = dap_config_get_item_str_default(g_config, "resources", "dap_global_db_driver", "cdb");
+    const char *l_driver_name = dap_config_get_item_str_default(g_config, "resources", "dap_global_db_driver", "sqlite");
+    //const char *l_driver_name = dap_config_get_item_str_default(g_config, "resources", "dap_global_db_driver", "cdb");
+    s_track_history = dap_config_get_item_bool_default(g_config, "resources", "dap_global_db_track_history", s_track_history);
     lock();
     int res = dap_db_driver_init(l_driver_name, l_storage_path);
     unlock();
@@ -262,20 +189,20 @@ void dap_chain_global_db_deinit(void)
     dap_db_driver_deinit();
     //dap_db_deinit();
     unlock();
-    history_group_item_t * l_item = NULL, *l_item_tmp = NULL;
-    HASH_ITER(hh, s_history_group_items, l_item, l_item_tmp)
+    sync_group_item_t * l_item = NULL, *l_item_tmp = NULL;
+    HASH_ITER(hh, s_sync_group_items, l_item, l_item_tmp)
     {
         DAP_DELETE(l_item->group_name_for_history);
         DAP_DELETE(l_item);
     }
-    history_extra_group_item_t * l_add_item = NULL, *l_add_item_tmp = NULL;
-    HASH_ITER(hh, s_history_extra_group_items, l_add_item, l_add_item_tmp)
+    sync_group_item_t * l_add_item = NULL, *l_add_item_tmp = NULL;
+    HASH_ITER(hh, s_sync_group_extra_items, l_add_item, l_add_item_tmp)
     {
-        DAP_DELETE(l_add_item->group_name);
+        DAP_DELETE(l_add_item->group_mask);
         DAP_DELETE(l_add_item->group_name_for_history);
         DAP_DELETE(l_add_item);
     }
-    s_history_group_items = NULL;
+    s_sync_group_items = NULL;
 
 }
 
@@ -301,19 +228,6 @@ void* dap_chain_global_db_obj_get(const char *a_key, const char *a_group)
     // read one item
     dap_store_obj_t *l_store_data = dap_chain_global_db_driver_read(a_group, a_key, &l_count);
     return l_store_data;
-
-    /*    size_t count = 0;
-     if(!a_key)
-     return NULL;
-     size_t query_len = (size_t) snprintf(NULL, 0, "(&(cn=%s)(objectClass=%s))", a_key, a_group);
-     char *query = DAP_NEW_Z_SIZE(char, query_len + 1); //char query[32 + strlen(a_key)];
-     snprintf(query, query_len + 1, "(&(cn=%s)(objectClass=%s))", a_key, a_group); // objectClass != ou
-     lock();
-     dap_store_obj_t *store_data = dap_db_read_data(query, &count);
-     unlock();
-     assert(count <= 1);
-     DAP_DELETE(query);
-     return store_data;*/
 }
 
 /**
@@ -325,18 +239,14 @@ void* dap_chain_global_db_obj_get(const char *a_key, const char *a_group)
  */
 dap_store_obj_t* dap_chain_global_db_obj_gr_get(const char *a_key, size_t *a_data_len_out, const char *a_group)
 {
-    //uint8_t *l_ret_value = NULL;
     // read several items, 0 - no limits
     size_t l_data_len_out = 0;
     if(a_data_len_out)
         l_data_len_out = *a_data_len_out;
     dap_store_obj_t *l_store_data = dap_chain_global_db_driver_read(a_group, a_key, &l_data_len_out);
     if(l_store_data) {
-        //l_ret_value = (l_store_data->value) ? DAP_NEW_SIZE(uint8_t, l_store_data->value_len) : NULL; //ret_value = (store_data->value) ? strdup(store_data->value) : NULL;
-        //memcpy(l_ret_value, l_store_data->value, l_store_data->value_len);
         if(a_data_len_out)
-            *a_data_len_out = l_data_len_out;//l_store_data->value_len;
-        //dap_store_obj_free(l_store_data, l_data_len_out);
+            *a_data_len_out = l_data_len_out;
     }
     return l_store_data;
 }
@@ -365,28 +275,6 @@ uint8_t * dap_chain_global_db_gr_get(const char *a_key, size_t *a_data_len_out,
         dap_store_obj_free(l_store_data, l_data_len_out);
     }
     return l_ret_value;
-
-    /*ldb
-     *     uint8_t *l_ret_value = NULL;
-     size_t l_count = 0;
-     if(!a_key)
-     return NULL;
-     size_t l_query_len =(size_t) snprintf(NULL, 0, "(&(cn=%s)(objectClass=%s))", a_key, a_group);
-
-     char *l_query = DAP_NEW_Z_SIZE(char, l_query_len + 1); //char query[32 + strlen(a_key)];
-     snprintf(l_query, l_query_len + 1, "(&(cn=%s)(objectClass=%s))", a_key, a_group); // objectClass != ou
-     lock();
-     pdap_store_obj_t store_data = dap_db_read_data(l_query, &l_count);
-     unlock();
-     if(l_count == 1 && store_data && !strcmp(store_data->key, a_key)) {
-     l_ret_value = (store_data->value) ? DAP_NEW_SIZE(uint8_t, store_data->value_len) : NULL; //ret_value = (store_data->value) ? strdup(store_data->value) : NULL;
-     memcpy(l_ret_value, store_data->value, store_data->value_len);
-     if(a_data_out)
-     *a_data_out = store_data->value_len;
-     }
-     dap_store_obj_free(store_data, l_count);
-     DAP_DELETE(l_query);
-     return l_ret_value;*/
 }
 
 uint8_t * dap_chain_global_db_get(const char *a_key, size_t *a_data_out)
@@ -424,7 +312,7 @@ static bool global_db_gr_del_add(char *a_key,const char *a_group, time_t a_times
 /**
  * Delete info about the deleted entry from the base
  */
-static bool global_db_gr_del_del(char *a_key,const char *a_group)
+static bool global_db_gr_del_del(char *a_key, const char *a_group)
 {
     if(!a_key)
         return NULL;
@@ -460,7 +348,7 @@ time_t global_db_gr_del_get_timestamp(const char *a_group, char *a_key)
     store_data.group = dap_strdup_printf("%s.del", a_group);
     //store_data->c_group = a_group;
     lock();
-    if(dap_chain_global_db_driver_is(store_data.group, store_data.key)) {
+    if (dap_chain_global_db_driver_is(store_data.group, store_data.key)) {
         size_t l_count_out = 0;
         dap_store_obj_t *l_obj = dap_chain_global_db_driver_read(store_data.group, store_data.key, &l_count_out);
         assert(l_count_out <= 1);
@@ -544,60 +432,51 @@ dap_global_db_obj_t* dap_chain_global_db_load(size_t *a_data_size_out)
     return dap_chain_global_db_gr_load(GROUP_LOCAL_GENERAL, a_data_size_out);
 }
 
-void dap_global_db_obj_track_history(void* a_store_data, size_t a_objs_count)
+/**
+ * @brief extract_group_mask
+ * @param a_group
+ * @return
+ */
+static sync_group_item_t *find_item_by_mask(sync_group_item_t *a_items, const char *a_group)
 {
-    for(size_t i = 0; i < a_objs_count; i++) {
-
-        dap_store_obj_t *a_store_obj = a_store_data + i;
-        if(a_store_obj->type == 'a')
-            // delete info about the deleted entry from the base if one present
-            global_db_gr_del_del(a_store_obj->key, a_store_obj->group);
-        else if(a_store_obj->type == 'd')
-            // add to Del group
-            global_db_gr_del_add(a_store_obj->key, a_store_obj->group, a_store_obj->timestamp);
-
+    sync_group_item_t * l_item = NULL, *l_item_tmp = NULL;
+    HASH_ITER(hh, a_items, l_item, l_item_tmp) {
+        if (!dap_fnmatch(l_item->group_mask, a_group, 0))
+            return l_item;
+    }
+    return NULL;
+}
 
-        history_group_item_t * l_history_group_item = NULL;
-        dap_store_obj_t* l_obj = (dap_store_obj_t*)a_store_data + i;
-        char * l_group_prefix = extract_group_prefix(l_obj->group);
-        if(l_group_prefix)
-            HASH_FIND_STR(s_history_group_items, l_group_prefix, l_history_group_item);
 
-        if(l_history_group_item) {
-            if(l_history_group_item->auto_track) {
-                lock();
-                dap_db_history_add((char)l_obj->type, l_obj, 1, l_history_group_item->group_name_for_history);
-                unlock();
-            }
-            if(l_history_group_item->callback_notify) {
-                if(l_obj) {
-                    l_history_group_item->callback_notify(l_history_group_item->callback_arg,
-                            (const char)l_obj->type,
-                            l_group_prefix, l_obj->group, l_obj->key,
-                            l_obj->value, l_obj->value_len);
-                } else {
-                    break;
-                }
+void dap_global_db_obj_track_history(void* a_store_data)
+{
+    if (!s_track_history)
+        return;
+    dap_store_obj_t *l_obj = (dap_store_obj_t *)a_store_data;
+    sync_group_item_t *l_sync_group_item = find_item_by_mask(s_sync_group_items, l_obj->group);
+    if(l_sync_group_item) {
+        lock();
+        dap_db_history_add((char)l_obj->type, l_obj, 1, l_sync_group_item->group_name_for_history);
+        unlock();
+        if(l_sync_group_item->callback_notify) {
+            if(l_obj) {
+                l_sync_group_item->callback_notify(l_sync_group_item->callback_arg,
+                        (const char)l_obj->type,
+                        l_obj->group, l_obj->key,
+                        l_obj->value, l_obj->value_len);
             }
         }
-        // looking for extra group
-        else {
-            history_extra_group_item_t * l_history_extra_group_item = NULL;
-            HASH_FIND_STR(s_history_extra_group_items, l_obj->group, l_history_extra_group_item);
-
-            if(l_history_extra_group_item) {
-                lock();
-                dap_db_history_add((char)l_obj->type, l_obj, 1, l_history_extra_group_item->group_name_for_history);
-                unlock();
-                if(l_history_extra_group_item->callback_notify)
-                    l_history_extra_group_item->callback_notify(l_history_extra_group_item->callback_arg,
-                            (const char)l_obj->type,
-                            l_group_prefix, l_obj->group, l_obj->key,
-                            l_obj->value, l_obj->value_len);
-            }
+    } else { // looking for extra group
+        sync_group_item_t *l_sync_extra_group_item = find_item_by_mask(s_sync_group_extra_items, l_obj->group);
+        if(l_sync_extra_group_item) {
+            lock();
+            dap_db_history_add((char)l_obj->type, l_obj, 1, l_sync_extra_group_item->group_name_for_history);
+            unlock();
+            if(l_sync_extra_group_item->callback_notify)
+                l_sync_extra_group_item->callback_notify(l_sync_extra_group_item->callback_arg,
+                        (const char)l_obj->type, l_obj->group, l_obj->key,
+                        l_obj->value, l_obj->value_len);
         }
-
-        DAP_DELETE(l_group_prefix);
     }
 }
 
@@ -615,7 +494,6 @@ bool dap_chain_global_db_gr_set(char *a_key, void *a_value, size_t a_value_len,
 {
     dap_store_obj_t store_data;
     memset(&store_data, 0, sizeof(dap_store_obj_t));
-    store_data.type = 'a';
     store_data.key = a_key;
     store_data.value = a_value;
     store_data.value_len = (a_value_len == (size_t) -1) ? dap_strlen((const char*) a_value) : a_value_len;
@@ -627,7 +505,9 @@ bool dap_chain_global_db_gr_set(char *a_key, void *a_value, size_t a_value_len,
 
     // Extract prefix if added successfuly, add history log and call notify callback if present
     if(!l_res) {
-        dap_global_db_obj_track_history(&store_data, 1);
+        // delete info about the deleted entry from the base if one present
+        global_db_gr_del_del(store_data.key, store_data.group);
+        dap_global_db_obj_track_history(&store_data);
     } else {
         log_it(L_ERROR, "Save error: %d", l_res);
     }
@@ -654,9 +534,13 @@ bool dap_chain_global_db_gr_del(char *a_key,const char *a_group)
     lock();
     int l_res = dap_chain_global_db_driver_delete(&store_data, 1);
     unlock();
+    if(l_res >= 0) {
+        // add to Del group
+        global_db_gr_del_add(store_data.key, store_data.group, store_data.timestamp);
+    }
     // do not add to history if l_res=1 (already deleted)
-    if(!l_res) {
-        dap_global_db_obj_track_history(&store_data, 1);
+    if (!l_res) {
+        dap_global_db_obj_track_history(&store_data);
     }
     return !l_res;
 }
@@ -676,9 +560,17 @@ bool dap_chain_global_db_obj_save(void* a_store_data, size_t a_objs_count)
     int l_res = dap_chain_global_db_driver_appy(a_store_data, a_objs_count);
     unlock();
 
-    // Extract prefix if added successfuly, add history log and call notify callback if present
-    if(!l_res) {
-        dap_global_db_obj_track_history(a_store_data, a_objs_count);
+    for(size_t i = 0; i < a_objs_count; i++) {
+        dap_store_obj_t *a_store_obj = (dap_store_obj_t *)a_store_data + i;
+        if (a_store_obj->type == 'a' && !l_res)
+            // delete info about the deleted entry from the base if one present
+            global_db_gr_del_del(a_store_obj->key, a_store_obj->group);
+        else if (a_store_obj->type == 'd' && l_res >= 0)
+            // add to Del group
+            global_db_gr_del_add(a_store_obj->key, a_store_obj->group, a_store_obj->timestamp);
+        if (!l_res)
+        // Extract prefix if added successfuly, add history log and call notify callback if present
+        dap_global_db_obj_track_history(a_store_obj);
     }
     return !l_res;
 }
@@ -701,8 +593,10 @@ bool dap_chain_global_db_gr_save(dap_global_db_obj_t* a_objs, size_t a_objs_coun
         //log_it(L_DEBUG,"Added %u objects", a_objs_count);
         int l_res = dap_chain_global_db_driver_add(l_store_data, a_objs_count);
         unlock();
-        if(!l_res) {
-            dap_global_db_obj_track_history(l_store_data, a_objs_count);
+        if(!l_res) {    
+            for(size_t i = 0; i < a_objs_count; i++) {
+                dap_global_db_obj_track_history(l_store_data + i);
+            }
         }
         DAP_DELETE(l_store_data);
         return !l_res;
@@ -724,65 +618,3 @@ char* dap_chain_global_db_hash(const uint8_t *data, size_t data_size)
 {
     return dap_chain_global_db_driver_hash(data, data_size);
 }
-
-/**
- * Parse data from dap_db_log_pack()
- *
- * return dap_store_obj_t*
- */
-void* dap_db_log_unpack(const void *a_data, size_t a_data_size, size_t *a_store_obj_count)
-{
-    const dap_store_obj_pkt_t *l_pkt = (const dap_store_obj_pkt_t*) a_data;
-    if (! l_pkt || ! a_data_size)
-        return NULL;
-    if( (l_pkt->data_size+ sizeof(dap_store_obj_pkt_t)) != ((size_t) a_data_size ))
-        return NULL;
-
-    size_t l_store_obj_count = 0;
-    dap_store_obj_t *l_obj = dap_store_unpacket_multiple(l_pkt, &l_store_obj_count);
-    if(a_store_obj_count)
-        *a_store_obj_count = l_store_obj_count;
-
-    return l_obj;
-}
-
-/**
- * Get timestamp from dap_db_log_pack()
- */
-time_t dap_db_log_unpack_get_timestamp(uint8_t *a_data, size_t a_data_size)
-{
-    dap_store_obj_pkt_t *l_pkt = (dap_store_obj_pkt_t*) a_data;
-    if(!l_pkt || l_pkt->data_size != (a_data_size - sizeof(dap_store_obj_pkt_t)))
-        return 0;
-    return l_pkt->timestamp;
-}
-
-/**
- * Get log diff as string
- */
-char* dap_db_log_get_diff(size_t *a_data_size_out)
-{
-    size_t l_data_size_out = 0;
-    dap_global_db_obj_t *l_objs = dap_chain_global_db_gr_load(GROUP_LOCAL_HISTORY, &l_data_size_out);
-    // make keys & val vector
-    char **l_keys_vals0 = DAP_NEW_SIZE(char*, sizeof(char*) * (l_data_size_out * 2 + 2));
-    char **l_keys_vals = l_keys_vals0 + 1;
-    size_t i;
-    // first element - number of records
-    l_keys_vals0[0] = dap_strdup_printf("%d", l_data_size_out);
-    for(i = 0; i < l_data_size_out; i++) {
-        dap_global_db_obj_t *l_obj_cur = l_objs + i;
-        l_keys_vals[i] = l_obj_cur->key;
-        l_keys_vals[i + l_data_size_out] = (char*) l_obj_cur->value;
-    }
-    if(a_data_size_out)
-        *a_data_size_out = l_data_size_out;
-    // last element - NULL (marker)
-    l_keys_vals[l_data_size_out * 2] = NULL;
-    char *l_keys_vals_flat = dap_strjoinv(GLOBAL_DB_HIST_KEY_SEPARATOR, l_keys_vals0);
-    DAP_DELETE(l_keys_vals0[0]);
-    DAP_DELETE(l_keys_vals0);
-    //dap_strfreev(l_keys_vals0);
-    dap_chain_global_db_objs_delete(l_objs, l_data_size_out);
-    return l_keys_vals_flat;
-}
diff --git a/modules/global-db/dap_chain_global_db_driver.c b/modules/global-db/dap_chain_global_db_driver.c
index a9a997ab20..ed6511d71d 100644
--- a/modules/global-db/dap_chain_global_db_driver.c
+++ b/modules/global-db/dap_chain_global_db_driver.c
@@ -198,131 +198,6 @@ void dap_store_obj_free(dap_store_obj_t *a_store_obj, size_t a_store_count)
     DAP_DELETE(a_store_obj);
 }
 
-static size_t dap_db_get_size_pdap_store_obj_t(pdap_store_obj_t store_obj)
-{
-    size_t size = sizeof(uint32_t) + 2 * sizeof(uint16_t) + sizeof(time_t)
-            + 2 * sizeof(uint64_t) + dap_strlen(store_obj->group) +
-            dap_strlen(store_obj->key) + store_obj->value_len;
-    return size;
-}
-
-/**
- * serialization
- * @param a_store_obj_count count of structures store_obj
- * @param a_timestamp create data time
- * @param a_size_out[out] size of output structure
- * @return NULL in case of an error
- */
-dap_store_obj_pkt_t *dap_store_packet_multiple(pdap_store_obj_t a_store_obj, time_t a_timestamp,
-        dap_store_obj_pkt_t *a_old_pkt)
-{
-    UNUSED(a_timestamp);
-
-    if (!a_store_obj)
-        return a_old_pkt;
-
-    dap_store_obj_pkt_t *l_pkt = a_old_pkt;
-    uint32_t l_data_size_out = 0;
-    l_data_size_out = dap_db_get_size_pdap_store_obj_t(a_store_obj);
-    if (l_pkt)
-        l_pkt = (dap_store_obj_pkt_t *)DAP_REALLOC((void *)l_pkt,
-                                                   sizeof(dap_store_obj_pkt_t) + l_pkt->data_size + l_data_size_out);
-    else
-        l_pkt = DAP_NEW_Z_SIZE(dap_store_obj_pkt_t, sizeof(dap_store_obj_pkt_t) + l_data_size_out);
-    uint64_t l_offset = l_pkt->data_size;
-    l_pkt->data_size += l_data_size_out;
-    l_pkt->obj_count++;
-    memcpy(l_pkt->data + l_offset, &a_store_obj->type, sizeof(int));
-    l_offset += sizeof(int);
-    uint16_t group_size = (uint16_t) dap_strlen(a_store_obj->group);
-    memcpy(l_pkt->data + l_offset, &group_size, sizeof(uint16_t));
-    l_offset += sizeof(uint16_t);
-    memcpy(l_pkt->data + l_offset, a_store_obj->group, group_size);
-    l_offset += group_size;
-    memcpy(l_pkt->data + l_offset, &a_store_obj->id, sizeof(uint64_t));
-    l_offset += sizeof(uint64_t);
-    memcpy(l_pkt->data + l_offset, &a_store_obj->timestamp, sizeof(time_t));
-    l_offset += sizeof(time_t);
-    uint16_t key_size = (uint16_t) dap_strlen(a_store_obj->key);
-    memcpy(l_pkt->data + l_offset, &key_size, sizeof(uint16_t));
-    l_offset += sizeof(uint16_t);
-    memcpy(l_pkt->data + l_offset, a_store_obj->key, key_size);
-    l_offset += key_size;
-    memcpy(l_pkt->data + l_offset, &a_store_obj->value_len, sizeof(uint64_t));
-    l_offset += sizeof(uint64_t);
-    memcpy(l_pkt->data + l_offset, a_store_obj->value, a_store_obj->value_len);
-    l_offset += a_store_obj->value_len;
-    assert(l_offset == l_pkt->data_size);
-    return l_pkt;
-}
-/**
- * deserialization
- * @param store_obj_count[out] count of the output structures store_obj
- * @return NULL in case of an error*
- */
-
-dap_store_obj_t *dap_store_unpacket_multiple(const dap_store_obj_pkt_t *pkt, size_t *store_obj_count)
-{
-    if(!pkt || pkt->data_size < 1)
-        return NULL;
-    uint64_t offset = 0;
-    uint32_t count = pkt->obj_count;
-    dap_store_obj_t *store_obj = DAP_NEW_Z_SIZE(dap_store_obj_t, count * sizeof(struct dap_store_obj));
-    for(size_t q = 0; q < count; ++q) {
-        dap_store_obj_t *obj = store_obj + q;
-        uint16_t str_length;
-
-        if (offset+sizeof (int)> pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'type' field"); break;} // Check for buffer boundries
-        memcpy(&obj->type, pkt->data + offset, sizeof(int));
-        offset += sizeof(int);
-
-        //memcpy(&str_size, pkt->data + offset, sizeof(uint16_t));
-        //offset += sizeof(uint16_t);
-        //obj->section = DAP_NEW_Z_SIZE(char, str_size + 1);
-        //memcpy(obj->section, pkt->data + offset, str_size);
-        //offset += str_size;
-
-        if (offset+sizeof (uint16_t)> pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'group_length' field"); break;} // Check for buffer boundries
-        memcpy(&str_length, pkt->data + offset, sizeof(uint16_t));
-        offset += sizeof(uint16_t);
-
-        if (offset+str_length> pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'group' field"); break;} // Check for buffer boundries
-        obj->group = DAP_NEW_Z_SIZE(char, str_length + 1);
-        memcpy(obj->group, pkt->data + offset, str_length);
-        offset += str_length;
-
-        if (offset+sizeof (uint64_t)> pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'id' field"); break;} // Check for buffer boundries
-        memcpy(&obj->id, pkt->data + offset, sizeof(uint64_t));
-        offset += sizeof(uint64_t);
-
-        if (offset+sizeof (time_t)> pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'timestamp' field"); break;} // Check for buffer boundries
-        memcpy(&obj->timestamp, pkt->data + offset, sizeof(time_t));
-        offset += sizeof(time_t);
-
-        if (offset+sizeof (uint16_t)> pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'key_length' field"); break;} // Check for buffer boundries
-        memcpy(&str_length, pkt->data + offset, sizeof(uint16_t));
-        offset += sizeof(uint16_t);
-
-        if (offset+ str_length > pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'key' field"); break;} // Check for buffer boundries
-        obj->key = DAP_NEW_Z_SIZE(char, str_length + 1);
-        memcpy(obj->key, pkt->data + offset, str_length);
-        offset += str_length;
-
-        if (offset+sizeof (uint64_t)> pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'value_length' field"); break;} // Check for buffer boundries
-        memcpy(&obj->value_len, pkt->data + offset, sizeof(uint64_t));
-        offset += sizeof(uint64_t);
-
-        if (offset+obj->value_len> pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'value' field"); break;} // Check for buffer boundries
-        obj->value = DAP_NEW_Z_SIZE(uint8_t, obj->value_len + 1);
-        memcpy(obj->value, pkt->data + offset, obj->value_len);
-        offset += obj->value_len;
-    }
-    //assert(pkt->data_size == offset);
-    if(store_obj_count)
-        *store_obj_count = count;
-    return store_obj;
-}
-
 /**
  * Calc hash for data
  *
diff --git a/modules/global-db/dap_chain_global_db_driver_cdb.c b/modules/global-db/dap_chain_global_db_driver_cdb.c
index d22d8759a3..1aeba149aa 100644
--- a/modules/global-db/dap_chain_global_db_driver_cdb.c
+++ b/modules/global-db/dap_chain_global_db_driver_cdb.c
@@ -121,6 +121,23 @@ bool dap_cdb_get_cond_obj_iter_callback(void *arg, const char *key, int ksize, c
     return true;
 }
 
+bool dap_cdb_get_count_iter_callback(void *arg, const char *key, int ksize, const char *val, int vsize, uint32_t expire, uint64_t oid) {
+    UNUSED(ksize);
+    UNUSED(val);
+    UNUSED(vsize);
+    UNUSED(expire);
+    UNUSED(oid);
+    UNUSED(key);
+
+    if (dap_hex_to_uint(val, sizeof(uint64_t)) < ((pobj_arg)arg)->id) {
+        return true;
+    }
+    if (--((pobj_arg)arg)->q == 0) {
+        return false;
+    }
+    return true;
+}
+
 pcdb_instance dap_cdb_init_group(char *a_group, int a_flags) {
     pcdb_instance l_cdb_i = NULL;
     pthread_mutex_lock(&cdb_mutex);
@@ -421,19 +438,23 @@ dap_store_obj_t* dap_db_driver_cdb_read_cond_store_obj(const char *a_group, uint
 
 size_t dap_db_driver_cdb_read_count_store(const char *a_group, uint64_t a_id)
 {
-    if(!a_group) {
+    if (!a_group) {
         return 0;
     }
     pcdb_instance l_cdb_i = dap_cdb_get_db_by_group(a_group);
-    if(!l_cdb_i) {
+    if (!l_cdb_i) {
         return 0;
     }
     CDB *l_cdb = l_cdb_i->cdb;
     CDBSTAT l_cdb_stat;
     cdb_stat(l_cdb, &l_cdb_stat);
-    if(a_id > l_cdb_stat.rnum)
-        return 0;
-    return (size_t) l_cdb_stat.rnum - a_id + 1;
+    obj_arg l_arg;
+    l_arg.q = l_cdb_stat.rnum;
+    l_arg.id = a_id;
+    void *l_iter = cdb_iterate_new(l_cdb, 0);
+    cdb_iterate(l_cdb, dap_cdb_get_count_iter_callback, (void*)&l_arg, l_iter);
+    cdb_iterate_destroy(l_cdb, l_iter);
+    return l_cdb_stat.rnum - l_arg.q;
 }
 
 /**
@@ -446,11 +467,10 @@ dap_list_t* dap_db_driver_cdb_get_groups_by_mask(const char *a_group_mask)
         return NULL;
     cdb_instance *cur_cdb, *tmp;
     pthread_rwlock_rdlock(&cdb_rwlock);
-    HASH_ITER(hh, s_cdb, cur_cdb, tmp)
-    {
-        if(!dap_fnmatch(a_group_mask, cur_cdb->local_group, 0))
-            if(dap_fnmatch("*.del", cur_cdb->local_group, 0))
-                l_ret_list = dap_list_prepend(l_ret_list, dap_strdup(cur_cdb->local_group));
+    HASH_ITER(hh, s_cdb, cur_cdb, tmp) {
+        char *l_table_name = cur_cdb->local_group;
+        if(!dap_fnmatch(a_group_mask, l_table_name, 0))
+            l_ret_list = dap_list_prepend(l_ret_list, dap_strdup(l_table_name));
     }
     pthread_rwlock_unlock(&cdb_rwlock);
     return l_ret_list;
diff --git a/modules/global-db/dap_chain_global_db_driver_sqlite.c b/modules/global-db/dap_chain_global_db_driver_sqlite.c
index a0f820c03d..9b90abff13 100644
--- a/modules/global-db/dap_chain_global_db_driver_sqlite.c
+++ b/modules/global-db/dap_chain_global_db_driver_sqlite.c
@@ -133,6 +133,9 @@ int dap_db_driver_sqlite_init(const char *a_filename_db, dap_db_driver_callbacks
         a_drv_callback->read_last_store_obj = dap_db_driver_sqlite_read_last_store_obj;
         a_drv_callback->transaction_start = dap_db_driver_sqlite_start_transaction;
         a_drv_callback->transaction_end = dap_db_driver_sqlite_end_transaction;
+        a_drv_callback->get_groups_by_mask  = dap_db_driver_sqlite_get_groups_by_mask;
+        a_drv_callback->read_count_store = dap_db_driver_sqlite_read_count_store;
+        a_drv_callback->is_obj = dap_db_driver_sqlite_is_obj;
         a_drv_callback->deinit = dap_db_driver_sqlite_deinit;
         a_drv_callback->flush = dap_db_driver_sqlite_flush;
         s_filename_db = strdup(a_filename_db);
@@ -417,7 +420,6 @@ static int dap_db_driver_sqlite_fetch_array(sqlite3_stmt *l_res, SQLITE_ROW_VALU
                 if(cur_val->type == SQLITE_INTEGER)
                 {
                     cur_val->val.val_int64 = sqlite3_column_int64(l_res, l_iCol);
-                    cur_val->val.val_int = sqlite3_column_int(l_res, l_iCol);
                 }
                 else if(cur_val->type == SQLITE_FLOAT)
                     cur_val->val.val_float = sqlite3_column_double(l_res, l_iCol);
@@ -520,6 +522,22 @@ int dap_db_driver_sqlite_end_transaction(void)
     }
 }
 
+char *dap_db_driver_sqlite_make_group_name(const char *a_table_name)
+{
+    char *l_table_name = dap_strdup(a_table_name);
+    ssize_t l_table_name_len = (ssize_t)dap_strlen(l_table_name);
+    const char *l_needle = "_";
+    // replace '_' to '.'
+    while(1){
+    char *l_str = dap_strstr_len(l_table_name, l_table_name_len, l_needle);
+    if(l_str)
+        *l_str = '.';
+    else
+        break;
+    }
+    return l_table_name;
+}
+
 char *dap_db_driver_sqlite_make_table_name(const char *a_group_name)
 {
     char *l_group_name = dap_strdup(a_group_name);
@@ -728,10 +746,10 @@ dap_store_obj_t* dap_db_driver_sqlite_read_cond_store_obj(const char *a_group, u
         l_count_out = (int)*a_count_out;
     char *l_str_query;
     if(l_count_out)
-        l_str_query = sqlite3_mprintf("SELECT id,ts,key,value FROM '%s' WHERE id>'%lld' ORDER BY id ASC LIMIT %d",
+        l_str_query = sqlite3_mprintf("SELECT id,ts,key,value FROM '%s' WHERE id>='%lld' ORDER BY id ASC LIMIT %d",
                 l_table_name, a_id, l_count_out);
     else
-        l_str_query = sqlite3_mprintf("SELECT id,ts,key,value FROM '%s' WHERE id>'%lld' ORDER BY id ASC",
+        l_str_query = sqlite3_mprintf("SELECT id,ts,key,value FROM '%s' WHERE id>='%lld' ORDER BY id ASC",
                 l_table_name, a_id);
     pthread_rwlock_wrlock(&s_db_rwlock);
     if(!s_db){
@@ -792,12 +810,10 @@ dap_store_obj_t* dap_db_driver_sqlite_read_cond_store_obj(const char *a_group, u
  */
 dap_store_obj_t* dap_db_driver_sqlite_read_store_obj(const char *a_group, const char *a_key, size_t *a_count_out)
 {
+    if(!a_group || !s_db)
+        return NULL;
     dap_store_obj_t *l_obj = NULL;
-    char *l_error_message = NULL;
     sqlite3_stmt *l_res;
-    if(!a_group)
-        return NULL;
-
     char * l_table_name = dap_db_driver_sqlite_make_table_name(a_group);
     // no limit
     uint64_t l_count_out = 0;
@@ -820,18 +836,13 @@ dap_store_obj_t* dap_db_driver_sqlite_read_store_obj(const char *a_group, const
             l_str_query = sqlite3_mprintf("SELECT id,ts,key,value FROM '%s' ORDER BY id ASC", l_table_name);
     }
     pthread_rwlock_wrlock(&s_db_rwlock);
-    if(!s_db){
-        pthread_rwlock_unlock(&s_db_rwlock);
-        return NULL;
-    }
-
-    int l_ret = dap_db_driver_sqlite_query(s_db, l_str_query, &l_res, &l_error_message);
+    int l_ret = dap_db_driver_sqlite_query(s_db, l_str_query, &l_res, NULL);
     pthread_rwlock_unlock(&s_db_rwlock);
+
     sqlite3_free(l_str_query);
     DAP_DEL_Z(l_table_name);
     if(l_ret != SQLITE_OK) {
         //log_it(L_ERROR, "read l_ret=%d, %s\n", sqlite3_errcode(s_db), sqlite3_errmsg(s_db));
-        dap_db_driver_sqlite_free(l_error_message);
         return NULL;
     }
 
@@ -866,3 +877,85 @@ dap_store_obj_t* dap_db_driver_sqlite_read_store_obj(const char *a_group, const
         *a_count_out = l_count_out;
     return l_obj;
 }
+
+dap_list_t* dap_db_driver_sqlite_get_groups_by_mask(const char *a_group_mask)
+{
+    if(!a_group_mask || !s_db)
+        return NULL;
+    sqlite3_stmt *l_res;
+    const char *l_str_query = "SELECT name FROM sqlite_master WHERE type ='table' AND name NOT LIKE 'sqlite_%'";
+    dap_list_t *l_ret_list = NULL;
+    pthread_rwlock_wrlock(&s_db_rwlock);
+    int l_ret = dap_db_driver_sqlite_query(s_db, (char *)l_str_query, &l_res, NULL);
+    pthread_rwlock_unlock(&s_db_rwlock);
+    if(l_ret != SQLITE_OK) {
+        //log_it(L_ERROR, "Get tables l_ret=%d, %s\n", sqlite3_errcode(s_db), sqlite3_errmsg(s_db));
+        return NULL;
+    }
+    char * l_mask = dap_db_driver_sqlite_make_table_name(a_group_mask);
+    SQLITE_ROW_VALUE *l_row = NULL;
+    while (dap_db_driver_sqlite_fetch_array(l_res, &l_row) == SQLITE_ROW && l_row) {
+        char *l_table_name = (char *)l_row->val->val.val_str;
+        if(!dap_fnmatch(l_mask, l_table_name, 0))
+            l_ret_list = dap_list_prepend(l_ret_list, dap_db_driver_sqlite_make_group_name(l_table_name));
+        dap_db_driver_sqlite_row_free(l_row);
+    }
+    dap_db_driver_sqlite_query_free(l_res);
+    return l_ret_list;
+}
+
+size_t dap_db_driver_sqlite_read_count_store(const char *a_group, uint64_t a_id)
+{
+    sqlite3_stmt *l_res;
+    if(!a_group || ! s_db)
+        return 0;
+
+    char * l_table_name = dap_db_driver_sqlite_make_table_name(a_group);
+    char *l_str_query = sqlite3_mprintf("SELECT COUNT(*) FROM '%s' WHERE id>='%lld'", l_table_name, a_id);
+    pthread_rwlock_wrlock(&s_db_rwlock);
+    int l_ret = dap_db_driver_sqlite_query(s_db, l_str_query, &l_res, NULL);
+    pthread_rwlock_unlock(&s_db_rwlock);
+    sqlite3_free(l_str_query);
+    DAP_DEL_Z(l_table_name);
+
+    if(l_ret != SQLITE_OK) {
+        //log_it(L_ERROR, "Count l_ret=%d, %s\n", sqlite3_errcode(s_db), sqlite3_errmsg(s_db));
+        return 0;
+    }
+    size_t l_ret_val;
+    SQLITE_ROW_VALUE *l_row = NULL;
+    if (dap_db_driver_sqlite_fetch_array(l_res, &l_row) == SQLITE_ROW && l_row) {
+        l_ret_val = (size_t)l_row->val->val.val_int64;
+        dap_db_driver_sqlite_row_free(l_row);
+    }
+    dap_db_driver_sqlite_query_free(l_res);
+    return l_ret_val;
+}
+
+bool dap_db_driver_sqlite_is_obj(const char *a_group, const char *a_key)
+{
+    sqlite3_stmt *l_res;
+    if(!a_group || ! s_db)
+        return false;
+
+    char * l_table_name = dap_db_driver_sqlite_make_table_name(a_group);
+    char *l_str_query = sqlite3_mprintf("SELECT EXISTS(SELECT * FROM '%s' WHERE key='%s')", l_table_name, a_key);
+    pthread_rwlock_wrlock(&s_db_rwlock);
+    int l_ret = dap_db_driver_sqlite_query(s_db, l_str_query, &l_res, NULL);
+    pthread_rwlock_unlock(&s_db_rwlock);
+    sqlite3_free(l_str_query);
+    DAP_DEL_Z(l_table_name);
+
+    if(l_ret != SQLITE_OK) {
+        //log_it(L_ERROR, "Exists l_ret=%d, %s\n", sqlite3_errcode(s_db), sqlite3_errmsg(s_db));
+        return false;
+    }
+    bool l_ret_val;
+    SQLITE_ROW_VALUE *l_row = NULL;
+    if (dap_db_driver_sqlite_fetch_array(l_res, &l_row) == SQLITE_ROW && l_row) {
+        l_ret_val = (size_t)l_row->val->val.val_int64;
+        dap_db_driver_sqlite_row_free(l_row);
+    }
+    dap_db_driver_sqlite_query_free(l_res);
+    return l_ret_val;
+}
diff --git a/modules/global-db/dap_chain_global_db_hist.c b/modules/global-db/dap_chain_global_db_hist.c
index b061db898c..c68958a5fb 100644
--- a/modules/global-db/dap_chain_global_db_hist.c
+++ b/modules/global-db/dap_chain_global_db_hist.c
@@ -8,7 +8,7 @@
 #include <dap_string.h>
 #include <dap_hash.h>
 #include "dap_chain_datum_tx_items.h"
-
+#include "dap_chain_global_db_remote.h"
 #include "dap_chain_global_db_hist.h"
 
 #include "uthash.h"
@@ -72,1010 +72,6 @@ static char* dap_db_new_history_timestamp()
     return l_str;
 }
 
-// for dap_db_history()
-static dap_store_obj_t* get_prev_tx(dap_global_db_obj_t *a_objs, dap_tx_data_t *a_tx_data)
-{
-    if(!a_objs || !a_tx_data)
-        return NULL;
-    dap_global_db_obj_t *l_obj_cur = a_objs + a_tx_data->obj_num;
-    dap_global_db_hist_t l_rec;
-    if(dap_db_history_unpack_hist((char*) l_obj_cur->value, &l_rec) == -1)
-        return NULL;
-    char **l_keys = dap_strsplit(l_rec.keys, GLOBAL_DB_HIST_KEY_SEPARATOR, -1);
-    size_t l_count = dap_str_countv(l_keys);
-    if(a_tx_data->pos_num >= l_count) {
-        dap_strfreev(l_keys);
-        return NULL;
-    }
-    dap_store_obj_t *l_obj =
-            (dap_store_obj_t*) l_keys ? dap_chain_global_db_obj_get(l_keys[a_tx_data->pos_num], l_rec.group) : NULL;
-    dap_strfreev(l_keys);
-    return l_obj;
-}
-
-/**
- * Get data according the history log
- *
- * return history string
- */
-#if 0
-char* dap_db_history_tx(dap_chain_hash_fast_t* a_tx_hash, const char *a_group_mempool)
-{
-    dap_string_t *l_str_out = dap_string_new(NULL);
-    // load history
-    size_t l_data_size_out = 0;
-    dap_global_db_obj_t *l_objs = dap_chain_global_db_gr_load(GROUP_LOCAL_HISTORY, &l_data_size_out);
-    size_t i, j;
-    bool l_tx_hash_found = false;
-    dap_tx_data_t *l_tx_data_hash = NULL;
-    for(i = 0; i < l_data_size_out; i++) {
-        dap_global_db_obj_t *l_obj_cur = l_objs + i;
-
-        // parse global_db records in a history record
-        dap_global_db_hist_t l_rec;
-        if(dap_db_history_unpack_hist((char*) l_obj_cur->value, &l_rec) == -1)
-            continue;
-        // use only groups with datums
-        if(dap_strcmp(a_group_mempool, l_rec.group))
-            continue;
-
-        char **l_keys = dap_strsplit(l_rec.keys, GLOBAL_DB_HIST_KEY_SEPARATOR, -1);
-        size_t l_count = dap_str_countv(l_keys);
-        dap_store_obj_t *l_obj = NULL;
-        // all objs in one history records
-        for(j = 0; j < l_count; j++) {
-
-            if(l_rec.type != 'a')
-                continue;
-            l_obj = (dap_store_obj_t*) dap_chain_global_db_obj_get(l_keys[j], l_rec.group);
-            if(!l_obj)
-                continue;
-            // datum
-            dap_chain_datum_t *l_datum = (dap_chain_datum_t*) l_obj->value;
-            if(!l_datum && l_datum->header.type_id != DAP_CHAIN_DATUM_TX)
-                continue;
-
-            dap_tx_data_t *l_tx_data = NULL;
-
-            // transaction
-            dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*) l_datum->data;
-
-            // find Token items - present in emit transaction
-            dap_list_t *l_list_tx_token = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_TOKEN, NULL);
-
-            // find OUT items
-            dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT, NULL);
-            dap_list_t *l_list_tmp = l_list_out_items;
-            while(l_list_tmp) {
-                const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_list_tmp->data;
-                // save OUT item l_tx_out
-                if(!l_tx_data)
-                {
-                    // save tx hash
-                    l_tx_data = DAP_NEW_Z(dap_tx_data_t);
-                    dap_chain_hash_fast_t l_tx_hash;
-                    dap_hash_fast(l_tx, dap_chain_datum_tx_get_size(l_tx), &l_tx_hash);
-                    memcpy(&l_tx_data->tx_hash, &l_tx_hash, sizeof(dap_chain_hash_fast_t));
-                    memcpy(&l_tx_data->addr, &l_tx_out->addr, sizeof(dap_chain_addr_t));
-                    dap_chain_hash_fast_to_str(&l_tx_data->tx_hash, l_tx_data->tx_hash_str,
-                            sizeof(l_tx_data->tx_hash_str));
-                    l_tx_data->obj_num = i;
-                    l_tx_data->pos_num = j;
-                    // save token name
-                    if(l_list_tx_token) {
-                        dap_chain_tx_token_t *tk = l_list_tx_token->data;
-                        int d = sizeof(l_tx_data->token_ticker);
-                        memcpy(l_tx_data->token_ticker, tk->header.ticker, sizeof(l_tx_data->token_ticker));
-                    }
-                    // take token from prev out item
-                    else {
-
-                        // find IN items
-                        dap_list_t *l_list_in_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN, NULL);
-                        dap_list_t *l_list_tmp_in = l_list_in_items;
-                        // find token_ticker in prev OUT items
-                        while(l_list_tmp_in) {
-                            const dap_chain_tx_in_t *l_tx_in =
-                                    (const dap_chain_tx_in_t*) l_list_tmp_in->data;
-                            dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash;
-
-                            //find prev OUT item
-                            dap_tx_data_t *l_tx_data_prev = NULL;
-                            HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t),
-                                    l_tx_data_prev);
-                            if(l_tx_data_prev != NULL) {
-                                // fill token in l_tx_data from prev transaction
-                                if(l_tx_data) {
-                                    // get token from prev tx
-                                    memcpy(l_tx_data->token_ticker, l_tx_data_prev->token_ticker,
-                                            sizeof(l_tx_data->token_ticker));
-                                    break;
-                                }
-                                l_list_tmp_in = dap_list_next(l_list_tmp_in);
-                            }
-                        }
-                        if(l_list_in_items)
-                            dap_list_free(l_list_in_items);
-                    }
-                    HASH_ADD(hh, l_tx_data_hash, tx_hash, sizeof(dap_chain_hash_fast_t), l_tx_data);
-                }
-                l_list_tmp = dap_list_next(l_list_tmp);
-            }
-            if(l_list_out_items)
-                dap_list_free(l_list_out_items);
-
-            // calc hash
-            dap_chain_hash_fast_t l_tx_hash;
-            dap_hash_fast(l_tx, dap_chain_datum_tx_get_size(l_tx), &l_tx_hash);
-            // search tx with a_tx_hash
-            if(!dap_hash_fast_compare(a_tx_hash, &l_tx_hash))
-                continue;
-            // found a_tx_hash now
-
-            // transaction time
-            char *l_time_str = NULL;
-            if(l_tx->header.ts_created > 0) {
-                time_t rawtime = (time_t) l_tx->header.ts_created;
-                struct tm * timeinfo;
-                timeinfo = localtime(&rawtime);
-                if(timeinfo) {
-                    dap_string_append_printf(l_str_out, " %s", asctime(timeinfo));
-                }
-            }
-
-            // find all OUT items in transaction
-            l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT, NULL);
-            l_list_tmp = l_list_out_items;
-            while(l_list_tmp) {
-                const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_list_tmp->data;
-                dap_tx_data_t *l_tx_data_prev = NULL;
-
-                const char *l_token_str = NULL;
-                if(l_tx_data)
-                    l_token_str = l_tx_data->token_ticker;
-                char *l_dst_to_str =
-                        (l_tx_out) ? dap_chain_addr_to_str(&l_tx_out->addr) :
-                        NULL;
-                dap_string_append_printf(l_str_out, " OUT item %lld %s to %s\n",
-                        l_tx_out->header.value,
-                        dap_strlen(l_token_str) > 0 ? l_token_str : "?",
-                        l_dst_to_str ? l_dst_to_str : "?"
-                                       );
-                DAP_DELETE(l_dst_to_str);
-                l_list_tmp = dap_list_next(l_list_tmp);
-            }
-            // find all IN items in transaction
-            dap_list_t *l_list_in_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN, NULL);
-            l_list_tmp = l_list_in_items;
-            // find cur addr in prev OUT items
-            while(l_list_tmp) {
-                const dap_chain_tx_in_t *l_tx_in = (const dap_chain_tx_in_t*) l_list_tmp->data;
-                dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash;
-                char l_tx_hash_str[70];
-                if(!dap_hash_fast_is_blank(&tx_prev_hash))
-                    dap_chain_hash_fast_to_str(&tx_prev_hash, l_tx_hash_str, sizeof(l_tx_hash_str));
-                else
-                    strcpy(l_tx_hash_str,"Null");
-                dap_string_append_printf(l_str_out, " IN item \n  prev tx_hash %s\n", l_tx_hash_str);
-
-                //find prev OUT item
-                dap_tx_data_t *l_tx_data_prev = NULL;
-                HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), l_tx_data_prev);
-                if(l_tx_data_prev != NULL) {
-
-                    dap_store_obj_t *l_obj_prev = get_prev_tx(l_objs, l_tx_data_prev);
-                    dap_chain_datum_t *l_datum_prev =
-                            l_obj_prev ? (dap_chain_datum_t*) l_obj_prev->value : NULL;
-                    dap_chain_datum_tx_t *l_tx_prev =
-                            l_datum_prev ? (dap_chain_datum_tx_t*) l_datum_prev->data : NULL;
-
-                    // find OUT items in prev datum
-                    dap_list_t *l_list_out_prev_items = dap_chain_datum_tx_items_get(l_tx_prev,
-                            TX_ITEM_TYPE_OUT, NULL);
-                    // find OUT item for IN item;
-                    dap_list_t *l_list_out_prev_item = dap_list_nth(l_list_out_prev_items,
-                            l_tx_in->header.tx_out_prev_idx);
-                    dap_chain_tx_out_t *l_tx_prev_out =
-                            l_list_out_prev_item ?
-                                                   (dap_chain_tx_out_t*) l_list_out_prev_item->data :
-                                                   NULL;
-                    // print value from prev out item
-                    dap_string_append_printf(l_str_out, "  prev OUT item value=%lld",
-                            l_tx_prev_out->header.value
-                            );
-                }
-                dap_string_append_printf(l_str_out, "\n");
-                l_list_tmp = dap_list_next(l_list_tmp);
-            }
-
-            if(l_list_tx_token)
-                dap_list_free(l_list_tx_token);
-            if(l_list_out_items)
-                dap_list_free(l_list_out_items);
-            if(l_list_in_items)
-                dap_list_free(l_list_in_items);
-            l_tx_hash_found = true;
-            break;
-        }
-        dap_list_t *l_records_out = NULL;
-
-        DAP_DELETE(l_obj);
-        dap_strfreev(l_keys);
-        // transaction was found -> exit
-        if(l_tx_hash_found)
-            break;
-    }
-    dap_chain_global_db_objs_delete(l_objs, l_data_size_out);
-    // if no history
-    if(!l_str_out->len)
-        dap_string_append(l_str_out, "empty");
-    char *l_ret_str = l_str_out ? dap_string_free(l_str_out, false) : NULL;
-    return l_ret_str;
-}
-#endif
-
-/**
- * Get data according the history log
- *
- * return history string
- */
-#if 0
-char* dap_db_history_addr(dap_chain_addr_t * a_addr, const char *a_group_mempool)
-{
-    dap_string_t *l_str_out = dap_string_new(NULL);
-    // load history
-    size_t l_data_size_out = 0;
-    dap_global_db_obj_t *l_objs = dap_chain_global_db_gr_load(GROUP_LOCAL_HISTORY, &l_data_size_out);
-    size_t i, j;
-    dap_tx_data_t *l_tx_data_hash = NULL;
-    for(i = 0; i < l_data_size_out; i++) {
-        dap_global_db_obj_t *l_obj_cur = l_objs + i;
-        // parse global_db records in a history record
-        dap_global_db_hist_t l_rec;
-        if(dap_db_history_unpack_hist((char*) l_obj_cur->value, &l_rec) == -1)
-            continue;
-        // use only groups with datums
-        if(dap_strcmp(a_group_mempool, l_rec.group))
-            continue;
-
-        char **l_keys = dap_strsplit(l_rec.keys, GLOBAL_DB_HIST_KEY_SEPARATOR, -1);
-        size_t l_count = dap_str_countv(l_keys);
-        dap_store_obj_t *l_obj = NULL;
-        // all objs in one history records
-        for(j = 0; j < l_count; j++) {
-            if(l_rec.type != 'a')
-                continue;
-            l_obj = (dap_store_obj_t*) dap_chain_global_db_obj_get(l_keys[j], l_rec.group);
-            if(!l_obj)
-                continue;
-            // datum
-            dap_chain_datum_t *l_datum = (dap_chain_datum_t*) l_obj->value;
-            if(!l_datum && l_datum->header.type_id != DAP_CHAIN_DATUM_TX)
-                continue;
-
-            // transaction
-            dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*) l_datum->data;
-            dap_list_t *l_records_out = NULL;
-            // transaction time
-            char *l_time_str = NULL;
-            {
-                if(l_tx->header.ts_created > 0) {
-                    time_t rawtime = (time_t) l_tx->header.ts_created;
-                    struct tm * timeinfo;
-                    timeinfo = localtime(&rawtime);
-                    if(timeinfo)
-                        l_time_str = dap_strdup(asctime(timeinfo));
-                }
-                else
-                    l_time_str = dap_strdup(" ");
-            }
-
-            // transaction
-            dap_tx_data_t *l_tx_data = NULL;
-
-            // find Token items - present in emit transaction
-            dap_list_t *l_list_tx_token = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_TOKEN, NULL);
-
-            // find OUT items
-            dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT, NULL);
-            dap_list_t *l_list_tmp = l_list_out_items;
-            while(l_list_tmp) {
-                const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_list_tmp->data;
-                // save OUT item l_tx_out
-                {
-                    // save tx hash
-                    l_tx_data = DAP_NEW_Z(dap_tx_data_t);
-                    dap_chain_hash_fast_t l_tx_hash;
-                    dap_hash_fast(l_tx, dap_chain_datum_tx_get_size(l_tx), &l_tx_hash);
-                    memcpy(&l_tx_data->tx_hash, &l_tx_hash, sizeof(dap_chain_hash_fast_t));
-                    memcpy(&l_tx_data->addr, &l_tx_out->addr, sizeof(dap_chain_addr_t));
-                    dap_chain_hash_fast_to_str(&l_tx_data->tx_hash, l_tx_data->tx_hash_str,
-                                                                sizeof(l_tx_data->tx_hash_str));
-                    l_tx_data->obj_num = i;
-                    l_tx_data->pos_num = j;
-                    // save token name
-                    if(l_tx_data && l_list_tx_token) {
-                        dap_chain_tx_token_t *tk = l_list_tx_token->data;
-                        int d = sizeof(l_tx_data->token_ticker);
-                        memcpy(l_tx_data->token_ticker, tk->header.ticker, sizeof(l_tx_data->token_ticker));
-                    }
-                    HASH_ADD(hh, l_tx_data_hash, tx_hash, sizeof(dap_chain_hash_fast_t), l_tx_data);
-
-                    // save OUT items to list
-                    {
-                        l_records_out = dap_list_append(l_records_out, (void*) l_tx_out);
-                    }
-                }
-                l_list_tmp = dap_list_next(l_list_tmp);
-            }
-
-            // find IN items
-            l_count = 0;
-            dap_list_t *l_list_in_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN, NULL);
-            l_list_tmp = l_list_in_items;
-            // find cur addr in prev OUT items
-            bool l_is_use_all_cur_out = false;
-            {
-                while(l_list_tmp) {
-                    const dap_chain_tx_in_t *l_tx_in = (const dap_chain_tx_in_t*) l_list_tmp->data;
-                    dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash;
-
-                    //find prev OUT item
-                    dap_tx_data_t *l_tx_data_prev = NULL;
-                    HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), l_tx_data_prev);
-                    if(l_tx_data_prev != NULL) {
-                        // fill token in l_tx_data from prev transaction
-                        if(l_tx_data) {
-                            // get token from prev tx
-                            memcpy(l_tx_data->token_ticker, l_tx_data_prev->token_ticker,
-                                    sizeof(l_tx_data->token_ticker));
-                            dap_store_obj_t *l_obj_prev = get_prev_tx(l_objs, l_tx_data_prev);
-                            dap_chain_datum_t *l_datum_prev =
-                                    l_obj_prev ? (dap_chain_datum_t*) l_obj_prev->value : NULL;
-                            dap_chain_datum_tx_t *l_tx_prev =
-                                    l_datum_prev ? (dap_chain_datum_tx_t*) l_datum_prev->data : NULL;
-
-                            // find OUT items in prev datum
-                            dap_list_t *l_list_out_prev_items = dap_chain_datum_tx_items_get(l_tx_prev,
-                                    TX_ITEM_TYPE_OUT, NULL);
-                            // find OUT item for IN item;
-                            dap_list_t *l_list_out_prev_item = dap_list_nth(l_list_out_prev_items,
-                                    l_tx_in->header.tx_out_prev_idx);
-                            dap_chain_tx_out_t *l_tx_prev_out =
-                                    l_list_out_prev_item ?
-                                                           (dap_chain_tx_out_t*) l_list_out_prev_item->data :
-                                                           NULL;
-                            if(l_tx_prev_out && !memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t)))
-                                l_is_use_all_cur_out = true;
-
-                        }
-                    }
-
-                    // find prev OUT items for IN items
-                    l_list_tmp = l_list_in_items;
-                    while(l_list_tmp) {
-                        const dap_chain_tx_in_t *l_tx_in = (const dap_chain_tx_in_t*) l_list_tmp->data;
-                        dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash;
-                        // if first transaction - empty prev OUT item
-                        if(dap_hash_fast_is_blank(&tx_prev_hash)) {
-                            // add emit info to ret string
-                            if(!memcmp(&l_tx_data->addr, a_addr, sizeof(dap_chain_addr_t)))
-                                    {
-                                dap_list_t *l_records_tmp = l_records_out;
-                                while(l_records_tmp) {
-
-                                    const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_records_tmp->data;
-                                    dap_string_append_printf(l_str_out, "tx hash %s \n emit %lld %s\n",
-                                            l_tx_data->tx_hash_str,
-                                            l_tx_out->header.value,
-                                            l_tx_data->token_ticker);
-                                    l_records_tmp = dap_list_next(l_records_tmp);
-                                }
-                            }
-                            dap_list_free(l_records_out);
-                        }
-                        // in other transactions except first one
-                        else {
-                            //find prev OUT item
-                            dap_tx_data_t *l_tx_data_prev = NULL;
-                            HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), l_tx_data_prev);
-                            if(l_tx_data_prev != NULL) {
-                                char *l_src_str = NULL;
-                                bool l_src_str_is_cur = false;
-                                if(l_tx_data) {
-                                    // get token from prev tx
-                                    memcpy(l_tx_data->token_ticker, l_tx_data_prev->token_ticker,
-                                            sizeof(l_tx_data->token_ticker));
-
-                                    dap_store_obj_t *l_obj_prev = get_prev_tx(l_objs, l_tx_data_prev);
-                                    dap_chain_datum_t *l_datum_prev =
-                                            l_obj_prev ? (dap_chain_datum_t*) l_obj_prev->value : NULL;
-                                    dap_chain_datum_tx_t *l_tx_prev =
-                                            l_datum_prev ? (dap_chain_datum_tx_t*) l_datum_prev->data : NULL;
-
-                                    // find OUT items in prev datum
-                                    dap_list_t *l_list_out_prev_items = dap_chain_datum_tx_items_get(l_tx_prev,
-                                            TX_ITEM_TYPE_OUT, NULL);
-                                    // find OUT item for IN item;
-                                    dap_list_t *l_list_out_prev_item = dap_list_nth(l_list_out_prev_items,
-                                            l_tx_in->header.tx_out_prev_idx);
-                                    dap_chain_tx_out_t *l_tx_prev_out =
-                                            l_list_out_prev_item ?
-                                                                   (dap_chain_tx_out_t*) l_list_out_prev_item->data :
-                                                                   NULL;
-                                    // if use src addr
-                                    bool l_is_use_src_addr = false;
-                                    // find source addrs
-                                    dap_string_t *l_src_addr = dap_string_new(NULL);
-                                    {
-                                        // find IN items in prev datum - for get destination addr
-                                        dap_list_t *l_list_in_prev_items = dap_chain_datum_tx_items_get(l_tx_prev,
-                                                TX_ITEM_TYPE_IN, NULL);
-                                        dap_list_t *l_list_tmp = l_list_in_prev_items;
-                                        while(l_list_tmp) {
-                                            dap_chain_tx_in_t *l_tx_prev_in = l_list_tmp->data;
-                                            dap_chain_hash_fast_t l_tx_prev_prev_hash =
-                                                    l_tx_prev_in->header.tx_prev_hash;
-                                            //find prev OUT item
-                                            dap_tx_data_t *l_tx_data_prev_prev = NULL;
-                                            HASH_FIND(hh, l_tx_data_hash, &l_tx_prev_prev_hash,
-                                                    sizeof(dap_chain_hash_fast_t), l_tx_data_prev_prev);
-                                            if(l_tx_data_prev_prev) {
-                                                // if use src addr
-                                                if(!memcmp(&l_tx_data_prev_prev->addr, a_addr,
-                                                        sizeof(dap_chain_addr_t)))
-                                                    l_is_use_src_addr = true;
-                                                char *l_str = dap_chain_addr_to_str(&l_tx_data_prev_prev->addr);
-                                                if(l_src_addr->len > 0)
-                                                    dap_string_append_printf(l_src_addr, "\n   %s", l_str);
-                                                else
-                                                    dap_string_append_printf(l_src_addr, "%s", l_str); // first record
-                                                DAP_DELETE(l_str);
-                                            }
-                                            l_list_tmp = dap_list_next(l_list_tmp);
-                                        }
-                                    }
-
-                                    char *l_dst_to_str =
-                                            (l_tx_prev_out) ? dap_chain_addr_to_str(&l_tx_prev_out->addr) :
-                                            NULL;
-                                    // if use dst addr
-                                    bool l_is_use_dst_addr = false;
-                                    if(!memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t)))
-                                        l_is_use_dst_addr = true;
-
-                                    l_src_str_is_cur = l_is_use_src_addr;
-                                    if(l_src_addr->len <= 1) {
-                                        l_src_str =
-                                                (l_tx_data) ? dap_chain_addr_to_str(&l_tx_data->addr) :
-                                                NULL;
-                                        if(!memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t)))
-                                            l_src_str_is_cur = true;
-                                        dap_string_free(l_src_addr, true);
-                                    }
-                                    else
-                                        l_src_str = dap_string_free(l_src_addr, false);
-                                    if(l_is_use_src_addr && !l_is_use_dst_addr) {
-                                        dap_string_append_printf(l_str_out,
-                                                "tx hash %s \n %s in send  %lld %s from %s\n to %s\n",
-                                                l_tx_data->tx_hash_str,
-                                                l_time_str ? l_time_str : "",
-                                                l_tx_prev_out->header.value,
-                                                l_tx_data->token_ticker,
-                                                l_src_str ? l_src_str : "",
-                                                l_dst_to_str);
-                                    } else if(l_is_use_dst_addr && !l_is_use_src_addr) {
-                                        if(!l_src_str_is_cur)
-                                            dap_string_append_printf(l_str_out,
-                                                    "tx hash %s \n %s in recv %lld %s from %s\n",
-                                                    l_tx_data->tx_hash_str,
-                                                    l_time_str ? l_time_str : "",
-                                                    l_tx_prev_out->header.value,
-                                                    l_tx_data->token_ticker,
-                                                    l_src_str ? l_src_str : "");
-                                    }
-
-                                    DAP_DELETE(l_dst_to_str);
-                                    dap_list_free(l_list_out_prev_items);
-                                    DAP_DELETE(l_obj_prev);
-                                }
-
-                                // OUT items
-                                dap_list_t *l_records_tmp = l_records_out;
-                                while(l_records_tmp) {
-
-                                    const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_records_tmp->data;
-
-                                    if(l_is_use_all_cur_out
-                                            || !memcmp(&l_tx_out->addr, a_addr, sizeof(dap_chain_addr_t))) {
-
-                                        char *l_addr_str = (l_tx_out) ? dap_chain_addr_to_str(&l_tx_out->addr) : NULL;
-
-                                        if(!memcmp(&l_tx_out->addr, a_addr, sizeof(dap_chain_addr_t))) {
-                                            if(!l_src_str_is_cur)
-                                                dap_string_append_printf(l_str_out, "tx hash %s \n %s recv %lld %s from %s\n",
-                                                        l_tx_data->tx_hash_str,
-                                                        l_time_str ? l_time_str : "",
-                                                        l_tx_out->header.value,
-                                                        l_tx_data_prev->token_ticker,
-                                                        l_src_str ? l_src_str : "?");
-                                        }
-                                        else {
-                                            dap_string_append_printf(l_str_out, "tx hash %s \n %s send %lld %s to %sd\n",
-                                                    l_tx_data->tx_hash_str,
-                                                    l_time_str ? l_time_str : "",
-                                                    l_tx_out->header.value,
-                                                    l_tx_data_prev->token_ticker,
-                                                    l_addr_str ? l_addr_str : "");
-                                        }
-                                        DAP_DELETE(l_addr_str);
-                                    }
-                                    l_records_tmp = dap_list_next(l_records_tmp);
-                                }
-                                dap_list_free(l_records_out);
-                                DAP_DELETE(l_src_str);
-
-                            }
-                        }
-                        l_list_tmp = dap_list_next(l_list_tmp);
-                    }
-                    l_list_tmp = dap_list_next(l_list_tmp);
-                }
-            }
-
-
-
-            if(l_list_tx_token)
-                dap_list_free(l_list_tx_token);
-            if(l_list_out_items)
-                dap_list_free(l_list_out_items);
-            if(l_list_in_items)
-                dap_list_free(l_list_in_items);
-
-            DAP_DELETE(l_time_str);
-        }
-        DAP_DELETE(l_obj);
-        dap_strfreev(l_keys);
-
-    }
-    // delete hashes
-    dap_tx_data_t *l_iter_current, *l_item_tmp;
-    HASH_ITER(hh, l_tx_data_hash , l_iter_current, l_item_tmp)
-    {
-        // delete struct
-        DAP_DELETE(l_iter_current);
-        HASH_DEL(l_tx_data_hash, l_iter_current);
-    }
-    dap_chain_global_db_objs_delete(l_objs, l_data_size_out);
-    // if no history
-    if(!l_str_out->len)
-        dap_string_append(l_str_out, " empty");
-    char *l_ret_str = l_str_out ? dap_string_free(l_str_out, false) : NULL;
-    return l_ret_str;
-}
-
-/*static int compare_items(const void * l_a, const void * l_b)
-{
-    const dap_global_db_obj_t *l_item_a = (const dap_global_db_obj_t*) l_a;
-    const dap_global_db_obj_t *l_item_b = (const dap_global_db_obj_t*) l_b;
-    int l_ret = strcmp(l_item_a->key, l_item_b->key);
-    return l_ret;
-}*/
-
-/**
- * Get log diff as list
- */
-dap_list_t* dap_db_log_get_list(uint64_t first_id)
-{
-    dap_list_t *l_list = NULL;
-    size_t l_data_size_out = 0;
-    //log_it(L_DEBUG,"loading db list...");
-    dap_store_obj_t *l_objs = dap_chain_global_db_cond_load(GROUP_LOCAL_HISTORY, first_id, &l_data_size_out);
-    //dap_global_db_obj_t *l_objs = dap_chain_global_db_gr_load(GROUP_LOCAL_HISTORY, first_timestamp, &l_data_size_out);
-    for(size_t i = 0; i < l_data_size_out; i++) {
-        dap_store_obj_t *l_obj_cur = l_objs + i;
-        dap_global_db_obj_t *l_item = DAP_NEW(dap_global_db_obj_t);
-        l_item->id = l_obj_cur->id;
-        l_item->key = dap_strdup(l_obj_cur->key);
-        l_item->value = (uint8_t*) dap_strdup((char*) l_obj_cur->value);
-        l_list = dap_list_append(l_list, l_item);
-    }
-    //log_it(L_DEBUG,"loaded db list n=%d", l_data_size_out);
-    dap_store_obj_free(l_objs, l_data_size_out);
-
-    return l_list;
-    /*
-     size_t l_list_count = 0;
-     char *l_first_key_str = dap_strdup_printf("%lld", (int64_t) first_timestamp);
-     size_t l_data_size_out = 0;
-
-     for(size_t i = 0; i < l_data_size_out; i++) {
-     dap_global_db_obj_t *l_obj_cur = l_objs[i];
-     //        log_it(L_DEBUG,"%lld and %lld tr",strtoll(l_obj_cur->key,NULL,10), first_timestamp );
-     if( strtoll(l_obj_cur->key,NULL,10) > (long long) first_timestamp  ) {
-     dap_global_db_obj_t *l_item = DAP_NEW(dap_global_db_obj_t);
-     l_item->key = dap_strdup(l_obj_cur->key);
-     l_item->value =(uint8_t*) dap_strdup((char*) l_obj_cur->value);
-     l_list = dap_list_append(l_list, l_item);
-     l_list_count++;
-     }
-     }
-     // sort list by key (time str)
-     //dap_list_sort(l_list, (dap_callback_compare_t) compare_items);
-     log_it(L_DEBUG,"Prepared %u items (list size %u)", l_list_count, dap_list_length(l_list));
-     DAP_DELETE(l_first_key_str);
-     dap_chain_global_db_objs_delete(l_objs);
-     */
-    /*/ dbg - sort result
-     l_data_size_out = dap_list_length(l_list);
-     for(size_t i = 0; i < l_data_size_out; i++) {
-     dap_list_t *l_list_tmp = dap_list_nth(l_list, i);
-     dap_global_db_obj_t *l_item = l_list_tmp->data;
-     printf("2 %d %s\n", i, l_item->key);
-     }*/
-
-}
-
-/**
- * Free list getting from dap_db_log_get_list()
- */
-void dap_db_log_del_list(dap_list_t *a_list)
-{
-    dap_list_free_full(a_list, (dap_callback_destroyed_t) dap_chain_global_db_obj_delete);
-}
-#endif
-
-
-/**
- * Get data according the history log
- *
- * return history string
- */
-char* dap_db_history(dap_chain_addr_t * a_addr, const char *a_group_mempool)
-{
-    dap_string_t *l_str_out = dap_string_new(NULL);
-    // load history
-    size_t l_data_size_out = 0;
-    dap_global_db_obj_t *l_objs = dap_chain_global_db_gr_load(GROUP_LOCAL_HISTORY, &l_data_size_out);
-    size_t i, j;
-    dap_tx_data_t *l_tx_data_hash = NULL;
-    for(i = 0; i < l_data_size_out; i++) {
-        dap_global_db_obj_t *l_obj_cur = l_objs + i;
-
-        // parse global_db records in a history record
-        dap_global_db_hist_t l_rec;
-        if(dap_db_history_unpack_hist((char*) l_obj_cur->value, &l_rec) == -1)
-            continue;
-        // use only groups with datums
-        if(dap_strcmp(a_group_mempool, l_rec.group))
-            continue;
-
-        char **l_keys = dap_strsplit(l_rec.keys, GLOBAL_DB_HIST_KEY_SEPARATOR, -1);
-        size_t l_count = dap_str_countv(l_keys);
-        dap_store_obj_t *l_obj = NULL;
-        // all objs in one history records
-        for(j = 0; j < l_count; j++) {
-            // add record
-            if(l_rec.type == 'a') {
-                l_obj = (dap_store_obj_t*) dap_chain_global_db_obj_get(l_keys[j], l_rec.group);
-                if(!l_obj)
-                    continue;
-                dap_chain_datum_t *l_datum = (dap_chain_datum_t*) l_obj->value;
-                if(!l_datum)
-                    continue;
-                switch (l_datum->header.type_id) {
-                /*                case DAP_CHAIN_DATUM_TOKEN_DECL: {
-                 dap_chain_datum_token_t *l_token = (dap_chain_datum_token_t*) l_datum->data;
-                 }
-                 break;
-                 case DAP_CHAIN_DATUM_TOKEN_EMISSION: {
-                 dap_chain_datum_token_emission_t *l_token_emission =
-                 (dap_chain_datum_token_emission_t*) l_datum->data;
-                 }
-                 break;*/
-                // find transaction
-                case DAP_CHAIN_DATUM_TX: {
-                    dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*) l_datum->data;
-                    dap_list_t *l_records_out = NULL;
-
-                    // transaction time
-                    char *l_time_str = NULL;
-                    if(l_tx->header.ts_created > 0) {
-                        time_t rawtime = (time_t) l_tx->header.ts_created;
-                        struct tm * timeinfo;
-                        timeinfo = localtime(&rawtime);
-                        if(timeinfo)
-                            l_time_str = dap_strdup(asctime(timeinfo));
-                    }
-                    else
-                        l_time_str = dap_strdup(" ");
-
-                    int l_count = 0;
-                    dap_tx_data_t *l_tx_data = NULL;
-                    // find Token items - present in emit transaction
-                    l_count = 0;
-                    dap_list_t *l_list_tx_token = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_TOKEN, &l_count);
-
-                    // find OUT items
-                    dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT, &l_count);
-                    dap_list_t *l_list_tmp = l_list_out_items;
-                    while(l_list_tmp) {
-                        dap_chain_tx_out_t *l_tx_out = (dap_chain_tx_out_t*) l_list_tmp->data;
-                        // save OUT item l_tx_out
-                        {
-                            // save tx hash
-                            l_tx_data = DAP_NEW_Z(dap_tx_data_t);
-                            dap_chain_hash_fast_t l_tx_hash;
-                            dap_hash_fast(l_tx, dap_chain_datum_tx_get_size(l_tx), &l_tx_hash);
-                            memcpy(&l_tx_data->tx_hash, &l_tx_hash, sizeof(dap_chain_hash_fast_t));
-                            memcpy(&l_tx_data->addr, &l_tx_out->addr, sizeof(dap_chain_addr_t));
-                            l_tx_data->obj_num = i;
-                            l_tx_data->pos_num = j;
-                            // save token name
-                            if(l_tx_data && l_list_tx_token) {
-                                dap_chain_tx_token_t *tk = l_list_tx_token->data;
-//                                int d = sizeof(l_tx_data->token_ticker);
-                                memcpy(l_tx_data->token_ticker, tk->header.ticker, sizeof(l_tx_data->token_ticker));
-                            }
-                            HASH_ADD(hh, l_tx_data_hash, tx_hash, sizeof(dap_chain_hash_fast_t), l_tx_data);
-
-                            // save OUT items to list
-                            {
-                                l_records_out = dap_list_append(l_records_out, (void*) l_tx_out);
-                            }
-                        }
-                        l_list_tmp = dap_list_next(l_list_tmp);
-                    }
-
-                    // find IN items
-                    l_count = 0;
-                    dap_list_t *l_list_in_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN, &l_count);
-                    l_list_tmp = l_list_in_items;
-
-                    // find cur addr in prev OUT items
-                    bool l_is_use_all_cur_out = false;
-                    {
-                        while(l_list_tmp) {
-                            const dap_chain_tx_in_t *l_tx_in = (const dap_chain_tx_in_t*) l_list_tmp->data;
-                            dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash;
-
-                            //find prev OUT item
-                            dap_tx_data_t *l_tx_data_prev = NULL;
-                            HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), l_tx_data_prev);
-                            if(l_tx_data_prev != NULL) {
-                                // fill token in l_tx_data from prev transaction
-                                if(l_tx_data) {
-                                    // get token from prev tx
-                                    memcpy(l_tx_data->token_ticker, l_tx_data_prev->token_ticker,
-                                            sizeof(l_tx_data->token_ticker));
-                                    dap_store_obj_t *l_obj_prev = get_prev_tx(l_objs, l_tx_data_prev);
-                                    dap_chain_datum_t *l_datum_prev =
-                                            l_obj_prev ? (dap_chain_datum_t*) l_obj_prev->value : NULL;
-                                    dap_chain_datum_tx_t *l_tx_prev =
-                                            l_datum_prev ? (dap_chain_datum_tx_t*) l_datum_prev->data : NULL;
-
-                                    // find OUT items in prev datum
-                                    dap_list_t *l_list_out_prev_items = dap_chain_datum_tx_items_get(l_tx_prev,
-                                            TX_ITEM_TYPE_OUT, &l_count);
-                                    // find OUT item for IN item;
-                                    dap_list_t *l_list_out_prev_item = dap_list_nth(l_list_out_prev_items,
-                                            l_tx_in->header.tx_out_prev_idx);
-                                    dap_chain_tx_out_t *l_tx_prev_out =
-                                            l_list_out_prev_item ?
-                                                                   (dap_chain_tx_out_t*) l_list_out_prev_item->data :
-                                                                   NULL;
-                                    if(l_tx_prev_out && !memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t)))
-                                        l_is_use_all_cur_out = true;
-
-                                }
-                            }
-                            l_list_tmp = dap_list_next(l_list_tmp);
-                        }
-                    }
-
-                    // find prev OUT items for IN items
-                    l_list_tmp = l_list_in_items;
-                    while(l_list_tmp) {
-                        const dap_chain_tx_in_t *l_tx_in = (const dap_chain_tx_in_t*) l_list_tmp->data;
-                        dap_chain_hash_fast_t tx_prev_hash = l_tx_in->header.tx_prev_hash;
-                        // if first transaction - empty prev OUT item
-                        if(dap_hash_fast_is_blank(&tx_prev_hash)) {
-                            // add emit info to ret string
-                            if(l_tx_data && a_addr &&
-                                    ( memcmp(&l_tx_data->addr, a_addr, sizeof(dap_chain_addr_t) ) == 0 )
-                                ) {
-                                dap_list_t *l_records_tmp = l_records_out;
-                                while(l_records_tmp) {
-                                    const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_records_tmp->data;
-                                    dap_string_append_printf(l_str_out, "emit %lld %s\n",
-                                            l_tx_out->header.value,
-                                            l_tx_data->token_ticker);
-                                    l_records_tmp = dap_list_next(l_records_tmp);
-                                }
-                            }
-                            dap_list_free(l_records_out);
-                        }
-                        // in other transactions except first one
-                        else {
-                            //find prev OUT item
-                            dap_tx_data_t *l_tx_data_prev = NULL;
-                            HASH_FIND(hh, l_tx_data_hash, &tx_prev_hash, sizeof(dap_chain_hash_fast_t), l_tx_data_prev);
-                            if(l_tx_data_prev != NULL) {
-                                char *l_src_str = NULL;
-                                bool l_src_str_is_cur = false;
-                                if(l_tx_data) {
-                                    // get token from prev tx
-                                    memcpy(l_tx_data->token_ticker, l_tx_data_prev->token_ticker,
-                                            sizeof(l_tx_data->token_ticker));
-
-                                    dap_store_obj_t *l_obj_prev = get_prev_tx(l_objs, l_tx_data_prev);
-                                    dap_chain_datum_t *l_datum_prev =
-                                            l_obj_prev ? (dap_chain_datum_t*) l_obj_prev->value : NULL;
-                                    dap_chain_datum_tx_t *l_tx_prev =
-                                            l_datum_prev ? (dap_chain_datum_tx_t*) l_datum_prev->data : NULL;
-
-                                    // find OUT items in prev datum
-                                    dap_list_t *l_list_out_prev_items = dap_chain_datum_tx_items_get(l_tx_prev,
-                                            TX_ITEM_TYPE_OUT, &l_count);
-                                    // find OUT item for IN item;
-                                    dap_list_t *l_list_out_prev_item = dap_list_nth(l_list_out_prev_items,
-                                            l_tx_in->header.tx_out_prev_idx);
-                                    dap_chain_tx_out_t *l_tx_prev_out =
-                                            l_list_out_prev_item ?
-                                                                   (dap_chain_tx_out_t*) l_list_out_prev_item->data :
-                                                                   NULL;
-                                    // if use src addr
-                                    bool l_is_use_src_addr = false;
-                                    // find source addrs
-                                    dap_string_t *l_src_addr = dap_string_new(NULL);
-                                    {
-                                        // find IN items in prev datum - for get destination addr
-                                        dap_list_t *l_list_in_prev_items = dap_chain_datum_tx_items_get(l_tx_prev,
-                                                TX_ITEM_TYPE_IN, &l_count);
-                                        dap_list_t *l_list_tmp = l_list_in_prev_items;
-                                        while(l_list_tmp) {
-                                            dap_chain_tx_in_t *l_tx_prev_in = l_list_tmp->data;
-                                            dap_chain_hash_fast_t l_tx_prev_prev_hash =
-                                                    l_tx_prev_in->header.tx_prev_hash;
-                                            //find prev OUT item
-                                            dap_tx_data_t *l_tx_data_prev_prev = NULL;
-                                            HASH_FIND(hh, l_tx_data_hash, &l_tx_prev_prev_hash,
-                                                    sizeof(dap_chain_hash_fast_t), l_tx_data_prev_prev);
-                                            if(l_tx_data_prev_prev) {
-                                                // if use src addr
-                                                if(!memcmp(&l_tx_data_prev_prev->addr, a_addr,
-                                                        sizeof(dap_chain_addr_t)))
-                                                    l_is_use_src_addr = true;
-                                                char *l_str = dap_chain_addr_to_str(&l_tx_data_prev_prev->addr);
-                                                if(l_src_addr->len > 0)
-                                                    dap_string_append_printf(l_src_addr, "\n   %s", l_str);
-                                                else
-                                                    dap_string_append_printf(l_src_addr, "%s", l_str); // first record
-                                                DAP_DELETE(l_str);
-                                            }
-                                            l_list_tmp = dap_list_next(l_list_tmp);
-                                        }
-                                    }
-
-                                    char *l_dst_to_str =
-                                            (l_tx_prev_out) ? dap_chain_addr_to_str(&l_tx_prev_out->addr) :
-                                            NULL;
-                                    // if use dst addr
-                                    bool l_is_use_dst_addr = false;
-                                    if(l_tx_prev_out &&  a_addr &&
-                                            ( memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t) ) == 0 )){
-                                        l_is_use_dst_addr = true;
-                                    }
-
-                                    l_src_str_is_cur = l_is_use_src_addr;
-                                    if(l_src_addr->len <= 1) {
-                                        l_src_str =
-                                                (l_tx_data) ? dap_chain_addr_to_str(&l_tx_data->addr) :
-                                                NULL;
-                                        if(!memcmp(&l_tx_prev_out->addr, a_addr, sizeof(dap_chain_addr_t)))
-                                            l_src_str_is_cur = true;
-                                        dap_string_free(l_src_addr, true);
-                                    }
-                                    else
-                                        l_src_str = dap_string_free(l_src_addr, false);
-                                    if(l_is_use_src_addr && !l_is_use_dst_addr) {
-                                        dap_string_append_printf(l_str_out,
-                                                "%s in send  %lld %s from %s\n to %s\n",
-                                                l_time_str ? l_time_str : "",
-                                                l_tx_prev_out?l_tx_prev_out->header.value:0,
-                                                l_tx_data->token_ticker,
-                                                l_src_str ? l_src_str : "",
-                                                l_dst_to_str);
-                                    } else if(l_is_use_dst_addr && !l_is_use_src_addr) {
-                                        if(!l_src_str_is_cur)
-                                            dap_string_append_printf(l_str_out,
-                                                    "%s in recv %lld %s from %s\n",
-                                                    l_time_str ? l_time_str : "",
-                                                    l_tx_prev_out->header.value,
-                                                    l_tx_data->token_ticker,
-                                                    l_src_str ? l_src_str : "");
-                                    }
-
-                                    DAP_DELETE(l_dst_to_str);
-                                    dap_list_free(l_list_out_prev_items);
-                                    DAP_DELETE(l_obj_prev);
-                                }
-
-                                // OUT items
-                                dap_list_t *l_records_tmp = l_records_out;
-                                while(l_records_tmp) {
-
-                                    const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_records_tmp->data;
-
-                                    if(l_is_use_all_cur_out
-                                            || !memcmp(&l_tx_out->addr, a_addr, sizeof(dap_chain_addr_t))) {
-
-                                        char *l_addr_str = (l_tx_out) ? dap_chain_addr_to_str(&l_tx_out->addr) : NULL;
-
-                                        if(!memcmp(&l_tx_out->addr, a_addr, sizeof(dap_chain_addr_t))) {
-                                            if(!l_src_str_is_cur)
-                                                dap_string_append_printf(l_str_out, "%s recv %lld %s from %s\n",
-                                                        l_time_str ? l_time_str : "",
-                                                        l_tx_out->header.value,
-                                                        l_tx_data_prev->token_ticker,
-                                                        l_src_str ? l_src_str : "?");
-                                        }
-                                        else {
-                                            dap_string_append_printf(l_str_out, "%s send %lld %s to %sd\n",
-                                                    l_time_str ? l_time_str : "",
-                                                    l_tx_out->header.value,
-                                                    l_tx_data_prev->token_ticker,
-                                                    l_addr_str ? l_addr_str : "");
-                                        }
-                                        DAP_DELETE(l_addr_str);
-                                    }
-                                    l_records_tmp = dap_list_next(l_records_tmp);
-                                }
-                                dap_list_free(l_records_out);
-                                DAP_DELETE(l_src_str);
-
-                            }
-                        }
-                        l_list_tmp = dap_list_next(l_list_tmp);
-                    }
-                    if(l_list_tx_token)
-                        dap_list_free(l_list_tx_token);
-                    if(l_list_out_items)
-                        dap_list_free(l_list_out_items);
-                    if(l_list_in_items)
-                        dap_list_free(l_list_in_items);
-
-                    DAP_DELETE(l_time_str);
-                }
-                    break;
-                default:
-                    continue;
-                }
-            }
-            // delete record
-            else if(l_rec.type == 'd') {
-                //printf("del_gr%d_%d=%s\n", i, j, l_rec.group);
-            }
-        }
-        DAP_DELETE(l_obj);
-        dap_strfreev(l_keys);
-    }
-    // delete hashes
-    dap_tx_data_t *l_iter_current, *l_item_tmp;
-    HASH_ITER(hh, l_tx_data_hash , l_iter_current, l_item_tmp)
-    {
-        // delete struct
-        DAP_DELETE(l_iter_current);
-        HASH_DEL(l_tx_data_hash, l_iter_current);
-    }
-    dap_chain_global_db_objs_delete(l_objs, l_data_size_out);
-    // if no history
-    if(!l_str_out->len)
-        dap_string_append(l_str_out, "empty");
-    char *l_ret_str = l_str_out ? dap_string_free(l_str_out, false) : NULL;
-    return l_ret_str;
-}
-
 /**
  * Add data to the history log
  */
@@ -1125,22 +121,13 @@ bool dap_db_history_add(char a_type, pdap_store_obj_t a_store_obj, size_t a_dap_
     return false;
 }
 
-/**
- * Truncate the history log
- */
-bool dap_db_history_truncate(void)
-{
-    // TODO
-    return true;
-}
-
 /**
  * Get last id in log
  */
-uint64_t dap_db_log_get_group_history_last_id(const char *a_history_group_name)
+uint64_t dap_db_log_get_group_last_id(const char *a_group_name)
 {
     uint64_t result = 0;
-    dap_store_obj_t *l_last_obj = dap_chain_global_db_get_last(a_history_group_name);
+    dap_store_obj_t *l_last_obj = dap_chain_global_db_get_last(a_group_name);
     if(l_last_obj) {
         result = l_last_obj->id;
         dap_store_obj_free(l_last_obj, 1);
@@ -1153,7 +140,7 @@ uint64_t dap_db_log_get_group_history_last_id(const char *a_history_group_name)
  */
 uint64_t dap_db_log_get_last_id(void)
 {
-    return dap_db_log_get_group_history_last_id(GROUP_LOCAL_HISTORY);
+    return dap_db_log_get_group_last_id(GROUP_LOCAL_HISTORY);
 }
 
 /**
@@ -1162,107 +149,60 @@ uint64_t dap_db_log_get_last_id(void)
  */
 static void *s_list_thread_proc(void *arg)
 {
-    dap_db_log_list_t *l_dap_db_log_list = (dap_db_log_list_t*) arg;
-    size_t l_items_number = 0;
-    while(1) {
-        bool is_process;
-        char *l_group_cur_name = NULL;
-        // check for break process
-        pthread_mutex_lock(&l_dap_db_log_list->list_mutex);
-        is_process = l_dap_db_log_list->is_process;
-        size_t l_item_start = l_dap_db_log_list->item_start;
-        size_t l_item_last = l_dap_db_log_list->item_last;
-        if(l_dap_db_log_list->group_cur == -1)
-            l_group_cur_name = GROUP_LOCAL_HISTORY;
-        else
-            l_group_cur_name = l_dap_db_log_list->group_names[l_dap_db_log_list->group_cur];
-        pthread_mutex_unlock(&l_dap_db_log_list->list_mutex);
-        if(!is_process)
-            break;
-        // calculating how many items required to read
-        size_t l_item_count =(uint64_t) min(10, (int64_t)l_item_last - (int64_t)l_item_start + 1);
-        dap_store_obj_t *l_objs = NULL;
-        // read next 1...10 items
-        if(l_item_count > 0)
-            l_objs = dap_chain_global_db_cond_load(l_group_cur_name, l_item_start, &l_item_count);
-        // go to next group
-        if(!l_objs) {
-            pthread_mutex_lock(&l_dap_db_log_list->list_mutex);
-            while(l_dap_db_log_list->group_cur < l_dap_db_log_list->group_number) {
-                l_dap_db_log_list->group_cur++;
-                // check for empty group
-                if( !(l_dap_db_log_list->group_number) || (l_dap_db_log_list->group_number_items[l_dap_db_log_list->group_cur] < 1)) {
-                    continue;
-                }
-                break;
-            }
-            // end of all groups
-            if(l_dap_db_log_list->group_cur >= l_dap_db_log_list->group_number) {
-                pthread_mutex_unlock(&l_dap_db_log_list->list_mutex);
-                break;
-            }
-            l_dap_db_log_list->item_start = 0;
-            l_dap_db_log_list->item_last = l_dap_db_log_list->group_last_id[l_dap_db_log_list->group_cur];
-            l_item_start = l_dap_db_log_list->item_start;
-            l_item_last = l_dap_db_log_list->item_last;
-            if(l_dap_db_log_list->group_cur == -1)
-                l_group_cur_name = GROUP_LOCAL_HISTORY;
-            else
-                l_group_cur_name = l_dap_db_log_list->group_names[l_dap_db_log_list->group_cur];
-            pthread_mutex_unlock(&l_dap_db_log_list->list_mutex);
-            continue;
+    dap_db_log_list_t *l_dap_db_log_list = (dap_db_log_list_t *)arg;
+    for (dap_list_t *l_groups = l_dap_db_log_list->groups; l_groups && l_dap_db_log_list->is_process; l_groups = dap_list_next(l_groups)) {
+        dap_db_log_list_group_t *l_group_cur = (dap_db_log_list_group_t *)l_groups->data;
+        char *l_del_group_name_replace = NULL;
+        char l_obj_type;
+        if (!dap_fnmatch("*.del", l_group_cur->name, 0)) {
+            l_obj_type = 'd';
+            size_t l_del_name_len = strlen(l_group_cur->name) - 4; //strlen(".del");
+            l_del_group_name_replace = DAP_NEW_SIZE(char, l_del_name_len + 1);
+            strncpy(l_del_group_name_replace, l_group_cur->name, l_del_name_len);
+            l_del_group_name_replace[l_del_name_len] = '\0';
+        } else {
+            l_obj_type = 'a';
         }
-        dap_list_t *l_list = NULL;
-        for (size_t i = 0; i < l_item_count; i++) {
-            dap_store_obj_t *l_obj_cur = l_objs + i;
-            if (l_group_cur_name == (char *)GROUP_LOCAL_HISTORY) {
-                dap_global_db_hist_t l_rec;
-                if (dap_db_history_unpack_hist((char *)l_obj_cur->value, &l_rec) == -1)
-                    continue;
-                time_t l_timestamp = strtoll(l_obj_cur->key, NULL, 10);
-                // parse global_db records in a history record
-                char **l_keys = dap_strsplit(l_rec.keys, GLOBAL_DB_HIST_KEY_SEPARATOR, -1);
-                // read records from global_db
-                for (int i = 0; l_keys[i]; i++) {
-                    dap_db_log_list_obj_t *l_list_obj = DAP_NEW_Z(dap_db_log_list_obj_t);
-                    // add record - read record
-                    if (l_rec.type == 'a') {
-                        l_list_obj->obj = (dap_store_obj_t*) dap_chain_global_db_obj_get(l_keys[i], l_rec.group);
-                    }
-                    // delete record - save only key for record
-                    else if (l_rec.type == 'd') {
-                        l_list_obj->obj = (dap_store_obj_t *) DAP_NEW_Z(dap_store_obj_t);
-                        l_list_obj->obj->group = dap_strdup(l_rec.group);
-                        l_list_obj->obj->key = dap_strdup(l_keys[i]);
-                        l_list_obj->obj->timestamp = global_db_gr_del_get_timestamp(l_list_obj->obj->group, l_list_obj->obj->key);
-                    }
-                    if (l_list_obj->obj == NULL) {
-                        continue;
-                    }
-                    l_list_obj->obj->type = (uint8_t)l_rec.type;
-                    l_list_obj->obj->id = l_obj_cur->id;
-                    dap_hash_fast(l_list_obj->obj->key, strlen(l_list_obj->obj->key), &l_list_obj->hash);
-                    l_list = dap_list_append(l_list, l_list_obj);
+        uint64_t l_item_start = l_group_cur->last_id_synced + 1;
+        while (l_group_cur->count && l_dap_db_log_list->is_process) { // Number of records to be synchronized
+            size_t l_item_count = min(32, l_group_cur->count);
+            dap_store_obj_t *l_objs = dap_chain_global_db_cond_load(l_group_cur->name, l_item_start, &l_item_count);
+            // go to next group
+            if (!l_objs)
+                break;
+            // set new start pos = lastitem pos + 1
+            l_item_start = l_objs[l_item_count - 1].id + 1;
+            l_group_cur->count -= l_item_count;
+            dap_list_t *l_list = NULL;
+            for (size_t i = 0; i < l_item_count; i++) {
+                dap_store_obj_t *l_obj_cur = l_objs + i;
+                l_obj_cur->type = l_obj_type;
+                if (l_obj_type == 'd') {
+                    DAP_DELETE(l_obj_cur->group);
+                    l_obj_cur->group = dap_strdup(l_del_group_name_replace);
                 }
-            } else {
                 dap_db_log_list_obj_t *l_list_obj = DAP_NEW_Z(dap_db_log_list_obj_t);
-                l_list_obj->obj = l_obj_cur;
-                dap_hash_fast(l_list_obj->obj->key, strlen(l_list_obj->obj->key), &l_list_obj->hash);
+                uint64_t l_cur_id = l_obj_cur->id;
+                l_obj_cur->id = 0;
+                dap_store_obj_pkt_t *l_pkt = dap_store_packet_single(l_obj_cur);
+                dap_hash_fast(l_pkt->data, l_pkt->data_size, &l_list_obj->hash);
+                dap_store_packet_change_id(l_pkt, l_cur_id);
+                l_list_obj->pkt = l_pkt;
                 l_list = dap_list_append(l_list, l_list_obj);
+                if (!l_dap_db_log_list->is_process)
+                    break;
             }
+            if (l_del_group_name_replace)
+                DAP_DELETE(l_del_group_name_replace);
+            dap_store_obj_free(l_objs, l_item_count);
+            pthread_mutex_lock(&l_dap_db_log_list->list_mutex);
+            // add l_list to list_write
+            l_dap_db_log_list->list_write = dap_list_concat(l_dap_db_log_list->list_write, l_list);
+            // init read list if it ended already
+            if(!l_dap_db_log_list->list_read)
+                l_dap_db_log_list->list_read = l_list;
+            pthread_mutex_unlock(&l_dap_db_log_list->list_mutex);
         }
-        pthread_mutex_lock(&l_dap_db_log_list->list_mutex);
-        // add l_list to list_write
-        l_dap_db_log_list->list_write = dap_list_concat(l_dap_db_log_list->list_write, l_list);
-        // init read list if it ended already
-        if(!l_dap_db_log_list->list_read)
-            l_dap_db_log_list->list_read = l_list;
-        // set new start pos = lastitem pos + 1
-        if(l_item_count > 0)
-            l_dap_db_log_list->item_start = l_objs[l_item_count - 1].id + 1;
-        pthread_mutex_unlock(&l_dap_db_log_list->list_mutex);
-        l_items_number += l_item_count;
-        //log_it(L_DEBUG, "loaded items n=%u/%u", l_data_size_out, l_items_number);
     }
 
     pthread_mutex_lock(&l_dap_db_log_list->list_mutex);
@@ -1274,68 +214,39 @@ static void *s_list_thread_proc(void *arg)
 /**
  * instead dap_db_log_get_list()
  */
-dap_db_log_list_t* dap_db_log_list_start(uint64_t first_id, dap_list_t *a_add_groups_mask)
+dap_db_log_list_t* dap_db_log_list_start(dap_chain_node_addr_t a_addr, int a_flags)
 {
-
+#ifdef GDB_SYNC_ALWAYS_FROM_ZERO
+    a_flags |= F_DB_LOG_SYNC_FROM_ZERO;
+#endif
     //log_it(L_DEBUG, "Start loading db list_write...");
-
-    size_t l_add_groups_num = 0;// number of group
-    dap_list_t *l_add_groups_mask = a_add_groups_mask;
-    // calc l_add_groups_num
-    while(l_add_groups_mask) {
-        // не считать группы del
-        dap_list_t *l_groups = dap_chain_global_db_driver_get_groups_by_mask(l_add_groups_mask->data);
-        l_add_groups_num += dap_list_length(l_groups);
-        dap_list_free_full(l_groups, (dap_callback_destroyed_t) free);
-        l_add_groups_mask = dap_list_next(l_add_groups_mask);
+    dap_db_log_list_t *l_dap_db_log_list = DAP_NEW_Z(dap_db_log_list_t);
+    dap_list_t *l_groups_masks = dap_chain_db_get_sync_groups();
+    if (a_flags & F_DB_LOG_ADD_EXTRA_GROUPS) {
+        l_groups_masks = dap_list_concat(l_groups_masks, dap_chain_db_get_sync_extra_groups());
+    }
+    for (dap_list_t *l_cur_mask = l_groups_masks; l_cur_mask; l_cur_mask = dap_list_next(l_cur_mask)) {
+        l_dap_db_log_list->groups = dap_list_concat(l_dap_db_log_list->groups,
+                                                    dap_chain_global_db_driver_get_groups_by_mask(l_cur_mask->data));
     }
+    dap_list_free(l_groups_masks);
 
-    size_t l_data_size_out_main = dap_db_log_get_last_id() - first_id + 1;
-            //dap_chain_global_db_driver_count(GROUP_LOCAL_HISTORY, first_id); - not working for sqlite
-    size_t *l_data_size_out_add_items = DAP_NEW_Z_SIZE(size_t, sizeof(size_t) * l_add_groups_num);
-    uint64_t *l_group_last_id = DAP_NEW_Z_SIZE(uint64_t, sizeof(uint64_t) * l_add_groups_num);
-    char **l_group_names = DAP_NEW_Z_SIZE(char*, sizeof(char*) * l_add_groups_num);
-    size_t l_data_size_out_add_items_count = 0;
-    l_add_groups_mask = a_add_groups_mask;
-    while(l_add_groups_mask){
-        dap_list_t *l_groups0 = dap_chain_global_db_driver_get_groups_by_mask(l_add_groups_mask->data);
-        dap_list_t *l_groups = l_groups0;
-        size_t l_group_cur = 0;
-        while(l_groups){
-            const char *l_group_name = (const char *) l_groups->data;
-            l_group_names[l_group_cur] = dap_strdup(dap_chain_global_db_get_history_group_by_group_name(l_group_name));
-            dap_store_obj_t *l_obj = dap_chain_global_db_driver_read_last(l_group_names[l_group_cur]);
-            if(l_obj) {
-                l_group_last_id[l_group_cur] = l_obj->id;
-                dap_store_obj_free(l_obj, 1);
-            }
-            l_data_size_out_add_items[l_group_cur] = dap_chain_global_db_driver_count(l_group_names[l_group_cur], 1);
-            l_data_size_out_add_items_count += l_data_size_out_add_items[l_group_cur];
-            l_group_cur++;
-            l_groups = dap_list_next(l_groups);
-        }
-        dap_list_free_full(l_groups0, (dap_callback_destroyed_t) free);
-        l_add_groups_mask = dap_list_next(l_add_groups_mask);
+    for (dap_list_t *l_groups = l_dap_db_log_list->groups; l_groups; l_groups = dap_list_next(l_groups)) {
+        dap_db_log_list_group_t *l_replace = DAP_NEW_Z(dap_db_log_list_group_t);
+        l_replace->name = (char *)l_groups->data;
+        if (a_flags & F_DB_LOG_SYNC_FROM_ZERO)
+            l_replace->last_id_synced = 0;
+        else
+            l_replace->last_id_synced = dap_db_get_last_id_remote(a_addr.uint64, l_replace->name);
+        l_replace->count = dap_chain_global_db_driver_count(l_replace->name, l_replace->last_id_synced + 1);
+        l_dap_db_log_list->items_number += l_replace->count;
+        l_groups->data = (void *)l_replace;
     }
-    if(!(l_data_size_out_main + l_data_size_out_add_items_count)){
-        DAP_DELETE(l_data_size_out_add_items);
-        DAP_DELETE(l_group_last_id);
-        DAP_DELETE(l_group_names);
+    l_dap_db_log_list->items_rest = l_dap_db_log_list->items_number;
+    if (!l_dap_db_log_list->items_number) {
+        DAP_DELETE(l_dap_db_log_list);
         return NULL;
     }
-    dap_db_log_list_t *l_dap_db_log_list = DAP_NEW_Z(dap_db_log_list_t);
-    l_dap_db_log_list->item_start = first_id;
-    l_dap_db_log_list->item_last = first_id + l_data_size_out_main;
-    l_dap_db_log_list->items_number_main = l_data_size_out_main;
-    l_dap_db_log_list->items_number_add = l_data_size_out_add_items_count;
-    l_dap_db_log_list->items_number = l_data_size_out_main + l_data_size_out_add_items_count;
-    l_dap_db_log_list->items_rest = l_dap_db_log_list->items_number;
-    l_dap_db_log_list->group_number = (int64_t)l_add_groups_num;
-    l_dap_db_log_list->group_number_items = l_data_size_out_add_items;
-    l_dap_db_log_list->group_last_id = l_group_last_id;
-    l_dap_db_log_list->group_names = l_group_names;
-    l_dap_db_log_list->group_cur = -1;
-    l_dap_db_log_list->add_groups = a_add_groups_mask;
     l_dap_db_log_list->is_process = true;
     pthread_mutex_init(&l_dap_db_log_list->list_mutex, NULL);
     pthread_create(&l_dap_db_log_list->thread, NULL, s_list_thread_proc, l_dap_db_log_list);
@@ -1404,7 +315,7 @@ dap_db_log_list_obj_t* dap_db_log_list_get(dap_db_log_list_t *a_db_log_list)
 void dap_db_log_list_delete_item(void *a_item)
 {
     dap_db_log_list_obj_t *l_list_item = (dap_db_log_list_obj_t *)a_item;
-    dap_store_obj_free(l_list_item->obj, 1);
+    DAP_DELETE(l_list_item->pkt);
     DAP_DELETE(l_list_item);
 }
 
@@ -1422,12 +333,7 @@ void dap_db_log_list_delete(dap_db_log_list_t *a_db_log_list)
         pthread_mutex_unlock(&a_db_log_list->list_mutex);
         pthread_join(a_db_log_list->thread, NULL);
     }
-    for(int64_t i = 0; i < a_db_log_list->group_number; i++)
-        DAP_DELETE(a_db_log_list->group_names[i]);
-    DAP_DELETE(a_db_log_list->group_names);
-    DAP_DELETE(a_db_log_list->group_last_id);
-    DAP_DELETE(a_db_log_list->group_number_items);
-    dap_list_free(a_db_log_list->add_groups);
+    dap_list_free_full(a_db_log_list->groups, free);
     dap_list_free_full(a_db_log_list->list_write, (dap_callback_destroyed_t)dap_db_log_list_delete_item);
     pthread_mutex_destroy(&a_db_log_list->list_mutex);
     DAP_DELETE(a_db_log_list);
diff --git a/modules/global-db/dap_chain_global_db_remote.c b/modules/global-db/dap_chain_global_db_remote.c
index e0b3650be6..8075026a12 100644
--- a/modules/global-db/dap_chain_global_db_remote.c
+++ b/modules/global-db/dap_chain_global_db_remote.c
@@ -90,7 +90,6 @@ uint64_t dap_db_get_cur_node_addr(char *a_net_name)
     time_t l_dt = time(NULL) - l_node_time;
     //NODE_TIME_EXPIRED
     if(l_node_time && l_dt > addr_time_expired) {
-        //log_it(L_NOTICE, "Node 0x%016X set last synced timestamp %"DAP_UINT64_FORMAT_U"", a_id);
         l_node_addr_ret = 0;
     }
     DAP_DELETE(l_key);
@@ -103,31 +102,33 @@ uint64_t dap_db_get_cur_node_addr(char *a_net_name)
 /**
  * Set last id for remote node
  */
-bool dap_db_set_last_id_remote(uint64_t a_node_addr, uint64_t a_id)
+bool dap_db_set_last_id_remote(uint64_t a_node_addr, uint64_t a_id, char *a_group)
 {
-    //log_it( L_DEBUG, "Node 0x%016X set last synced timestamp %"DAP_UINT64_FORMAT_U"", a_node_addr, a_id);
-    uint64_t *l_id = DAP_NEW(uint64_t);
-    *l_id = a_id;
-    return dap_chain_global_db_gr_set(dap_strdup_printf("%ju", a_node_addr),
-                                      l_id, sizeof(uint64_t),
-                                      GROUP_LOCAL_NODE_LAST_ID);
+    //log_it( L_DEBUG, "Node 0x%016X set last synced id %"DAP_UINT64_FORMAT_u"", a_node_addr, a_id);
+    char *l_node_addr_str = dap_strdup_printf("%ju%s", a_node_addr, a_group);
+    bool l_ret = dap_chain_global_db_gr_set(l_node_addr_str, &a_id, sizeof(uint64_t),
+                                            GROUP_LOCAL_NODE_LAST_ID);
+    DAP_DELETE(l_node_addr_str);
+    return l_ret;
 }
 
 /**
  * Get last id for remote node
  */
-uint64_t dap_db_get_last_id_remote(uint64_t a_node_addr)
+uint64_t dap_db_get_last_id_remote(uint64_t a_node_addr, char *a_group)
 {
-    char *l_node_addr_str = dap_strdup_printf("%ju", a_node_addr);
-    size_t l_timestamp_len = 0;
-    uint8_t *l_timestamp = dap_chain_global_db_gr_get((const char*) l_node_addr_str, &l_timestamp_len,
-    GROUP_LOCAL_NODE_LAST_ID);
-    uint64_t l_ret_timestamp = 0;
-    if(l_timestamp && l_timestamp_len == sizeof(uint64_t))
-        memcpy(&l_ret_timestamp, l_timestamp, l_timestamp_len);
+    char *l_node_addr_str = dap_strdup_printf("%ju%s", a_node_addr, a_group);
+    size_t l_id_len = 0;
+    uint8_t *l_id = dap_chain_global_db_gr_get((const char*) l_node_addr_str, &l_id_len,
+                                                GROUP_LOCAL_NODE_LAST_ID);
+    uint64_t l_ret_id = 0;
+    if (l_id) {
+        if (l_id_len == sizeof(uint64_t))
+            memcpy(&l_ret_id, l_id, l_id_len);
+        DAP_DELETE(l_id);
+    }
     DAP_DELETE(l_node_addr_str);
-    DAP_DELETE(l_timestamp);
-    return l_ret_timestamp;
+    return l_ret_id;
 }
 
 /**
@@ -152,3 +153,166 @@ dap_chain_hash_fast_t *dap_db_get_last_hash_remote(uint64_t a_node_addr, dap_cha
     DAP_DELETE(l_node_chain_str);
     return (dap_chain_hash_fast_t *)l_hash;
 }
+
+static size_t dap_db_get_size_pdap_store_obj_t(pdap_store_obj_t store_obj)
+{
+    size_t size = sizeof(uint32_t) + 2 * sizeof(uint16_t) + sizeof(time_t)
+            + 2 * sizeof(uint64_t) + dap_strlen(store_obj->group) +
+            dap_strlen(store_obj->key) + store_obj->value_len;
+    return size;
+}
+
+/**
+ * serialization
+ * @param a_old_pkt an object for multiplexation
+ * @param a_new_pkt an object for multiplexation
+ * @return NULL in case of an error
+ */
+dap_store_obj_pkt_t *dap_store_packet_multiple(dap_store_obj_pkt_t *a_old_pkt, dap_store_obj_pkt_t *a_new_pkt)
+{
+    if (!a_new_pkt)
+        return a_old_pkt;
+    if (a_old_pkt)
+        a_old_pkt = (dap_store_obj_pkt_t *)DAP_REALLOC(a_old_pkt,
+                                                       a_old_pkt->data_size + a_new_pkt->data_size + sizeof(dap_store_obj_pkt_t));
+    else
+        a_old_pkt = DAP_NEW_Z_SIZE(dap_store_obj_pkt_t, a_new_pkt->data_size + sizeof(dap_store_obj_pkt_t));
+    memcpy(a_old_pkt->data + a_old_pkt->data_size, a_new_pkt->data, a_new_pkt->data_size);
+    a_old_pkt->data_size += a_new_pkt->data_size;
+    a_old_pkt->obj_count++;
+    return a_old_pkt;
+}
+
+char *dap_store_packet_get_group(dap_store_obj_pkt_t *a_pkt)
+{
+    uint16_t l_gr_len;
+    memcpy(&l_gr_len, a_pkt->data + sizeof(uint32_t), sizeof(uint16_t));
+    char *l_ret_str = DAP_NEW_SIZE(char, l_gr_len + 1);
+    size_t l_gr_offset = sizeof(uint32_t) + sizeof(uint16_t);
+    memcpy(l_ret_str, a_pkt->data + l_gr_offset, l_gr_len);
+    l_ret_str[l_gr_len] = '\0';
+    return l_ret_str;
+}
+
+uint64_t dap_store_packet_get_id(dap_store_obj_pkt_t *a_pkt)
+{
+    uint16_t l_gr_len;
+    memcpy(&l_gr_len, a_pkt->data + sizeof(uint32_t), sizeof(uint16_t));
+    size_t l_id_offset = sizeof(uint32_t) + sizeof(uint16_t) + l_gr_len;
+    uint64_t l_ret_id;
+    memcpy(&l_ret_id, a_pkt->data + l_id_offset, sizeof(uint64_t));
+    return l_ret_id;
+}
+
+void dap_store_packet_change_id(dap_store_obj_pkt_t *a_pkt, uint64_t a_id)
+{
+    uint16_t l_gr_len;
+    memcpy(&l_gr_len, a_pkt->data + sizeof(uint32_t), sizeof(uint16_t));
+    size_t l_id_offset = sizeof(uint32_t) + sizeof(uint16_t) + l_gr_len;
+    memcpy(a_pkt->data + l_id_offset, &a_id, sizeof(uint64_t));
+}
+
+/**
+ * serialization
+ * @param a_store_obj an object for serialization
+ * @return NULL in case of an error
+ */
+dap_store_obj_pkt_t *dap_store_packet_single(pdap_store_obj_t a_store_obj)
+{
+    if (!a_store_obj)
+        return NULL;
+
+    uint32_t l_data_size_out = dap_db_get_size_pdap_store_obj_t(a_store_obj);
+    dap_store_obj_pkt_t *l_pkt = DAP_NEW_SIZE(dap_store_obj_pkt_t, l_data_size_out + sizeof(dap_store_obj_pkt_t));
+    l_pkt->data_size = l_data_size_out;
+    l_pkt->obj_count = 1;
+    l_pkt->timestamp = 0;
+    uint32_t l_type = a_store_obj->type;
+    memcpy(l_pkt->data, &l_type, sizeof(uint32_t));
+    uint64_t l_offset = sizeof(uint32_t);
+    uint16_t group_size = (uint16_t) dap_strlen(a_store_obj->group);
+    memcpy(l_pkt->data + l_offset, &group_size, sizeof(uint16_t));
+    l_offset += sizeof(uint16_t);
+    memcpy(l_pkt->data + l_offset, a_store_obj->group, group_size);
+    l_offset += group_size;
+    memcpy(l_pkt->data + l_offset, &a_store_obj->id, sizeof(uint64_t));
+    l_offset += sizeof(uint64_t);
+    memcpy(l_pkt->data + l_offset, &a_store_obj->timestamp, sizeof(time_t));
+    l_offset += sizeof(time_t);
+    uint16_t key_size = (uint16_t) dap_strlen(a_store_obj->key);
+    memcpy(l_pkt->data + l_offset, &key_size, sizeof(uint16_t));
+    l_offset += sizeof(uint16_t);
+    memcpy(l_pkt->data + l_offset, a_store_obj->key, key_size);
+    l_offset += key_size;
+    memcpy(l_pkt->data + l_offset, &a_store_obj->value_len, sizeof(uint64_t));
+    l_offset += sizeof(uint64_t);
+    memcpy(l_pkt->data + l_offset, a_store_obj->value, a_store_obj->value_len);
+    l_offset += a_store_obj->value_len;
+    assert(l_offset == l_data_size_out);
+    return l_pkt;
+}
+/**
+ * deserialization
+ * @param store_obj_count[out] count of the output structures store_obj
+ * @return NULL in case of an error*
+ */
+
+dap_store_obj_t *dap_store_unpacket_multiple(const dap_store_obj_pkt_t *pkt, size_t *store_obj_count)
+{
+    if(!pkt || pkt->data_size < 1)
+        return NULL;
+    uint64_t offset = 0;
+    uint32_t count = pkt->obj_count;
+    dap_store_obj_t *store_obj = DAP_NEW_SIZE(dap_store_obj_t, count * sizeof(struct dap_store_obj));
+    for(size_t q = 0; q < count; ++q) {
+        dap_store_obj_t *obj = store_obj + q;
+        uint16_t str_length;
+
+        uint32_t l_type;
+        if (offset+sizeof (uint32_t)> pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'type' field"); break;} // Check for buffer boundries
+        memcpy(&l_type, pkt->data + offset, sizeof(uint32_t));
+        obj->type = l_type;
+        offset += sizeof(uint32_t);
+
+        if (offset+sizeof (uint16_t)> pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'group_length' field"); break;} // Check for buffer boundries
+        memcpy(&str_length, pkt->data + offset, sizeof(uint16_t));
+        offset += sizeof(uint16_t);
+
+        if (offset+str_length> pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'group' field"); break;} // Check for buffer boundries
+        obj->group = DAP_NEW_SIZE(char, str_length + 1);
+        memcpy(obj->group, pkt->data + offset, str_length);
+        obj->group[str_length] = '\0';
+        offset += str_length;
+
+        if (offset+sizeof (uint64_t)> pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'id' field"); break;} // Check for buffer boundries
+        memcpy(&obj->id, pkt->data + offset, sizeof(uint64_t));
+        offset += sizeof(uint64_t);
+
+        if (offset+sizeof (time_t)> pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'timestamp' field"); break;} // Check for buffer boundries
+        memcpy(&obj->timestamp, pkt->data + offset, sizeof(time_t));
+        offset += sizeof(time_t);
+
+        if (offset+sizeof (uint16_t)> pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'key_length' field"); break;} // Check for buffer boundries
+        memcpy(&str_length, pkt->data + offset, sizeof(uint16_t));
+        offset += sizeof(uint16_t);
+
+        if (offset+ str_length > pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'key' field"); break;} // Check for buffer boundries
+        obj->key = DAP_NEW_SIZE(char, str_length + 1);
+        memcpy(obj->key, pkt->data + offset, str_length);
+        obj->key[str_length] = '\0';
+        offset += str_length;
+
+        if (offset+sizeof (uint64_t)> pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'value_length' field"); break;} // Check for buffer boundries
+        memcpy(&obj->value_len, pkt->data + offset, sizeof(uint64_t));
+        offset += sizeof(uint64_t);
+
+        if (offset+obj->value_len> pkt->data_size) {log_it(L_ERROR, "Broken GDB element: can't read 'value' field"); break;} // Check for buffer boundries
+        obj->value = DAP_NEW_SIZE(uint8_t, obj->value_len);
+        memcpy(obj->value, pkt->data + offset, obj->value_len);
+        offset += obj->value_len;
+    }
+    //assert(pkt->data_size == offset);
+    if(store_obj_count)
+        *store_obj_count = count;
+    return store_obj;
+}
diff --git a/modules/global-db/include/dap_chain_global_db.h b/modules/global-db/include/dap_chain_global_db.h
index cbaf1b29ca..0cefa57fac 100644
--- a/modules/global-db/include/dap_chain_global_db.h
+++ b/modules/global-db/include/dap_chain_global_db.h
@@ -23,9 +23,8 @@ typedef struct dap_global_db_obj {
     size_t value_len;
 }DAP_ALIGN_PACKED dap_global_db_obj_t, *pdap_global_db_obj_t;
 
-typedef void (*dap_global_db_obj_callback_notify_t) (void * a_arg, const char a_op_code, const char * a_prefix, const char * a_group,
-                                                     const char * a_key, const void * a_value,
-                                                     const size_t a_value_len);
+typedef void (*dap_global_db_obj_callback_notify_t) (void * a_arg, const char a_op_code, const char * a_group,
+                                                     const char * a_key, const void * a_value, const size_t a_value_len);
 
 /**
  * Flush DB
@@ -49,23 +48,15 @@ void dap_chain_global_db_objs_delete(dap_global_db_obj_t *objs, size_t a_count);
 int dap_chain_global_db_init(dap_config_t * a_config);
 
 void dap_chain_global_db_deinit(void);
-
-/*
- * Get history group by group name
- */
-char* dap_chain_global_db_get_history_group_by_group_name(const char * a_group_name);
-
 /**
  * Setup callbacks and filters
  */
-// Add group prefix that will be tracking all changes
-void dap_chain_global_db_add_history_group_prefix(const char * a_group_prefix, const char * a_group_name_for_history);
-void dap_chain_global_db_add_history_callback_notify(const char * a_group_prefix,
-                                                     dap_global_db_obj_callback_notify_t a_callback, void * a_arg);
-const char* dap_chain_global_db_add_history_extra_group(const char * a_group_name, dap_chain_node_addr_t *a_nodes, uint16_t *a_nodes_count);
-void dap_chain_global_db_add_history_extra_group_callback_notify(const char * a_group_prefix,
-        dap_global_db_obj_callback_notify_t a_callback, void * a_arg);
-void dap_global_db_obj_track_history(void* a_store_data, size_t a_objs_count);
+// Add group name that will be synchronized
+void dap_chain_global_db_add_sync_group(const char *a_group_prefix, dap_global_db_obj_callback_notify_t a_callback, void *a_arg);
+void dap_chain_global_db_add_sync_extra_group(const char *a_group_mask, dap_global_db_obj_callback_notify_t a_callback, void *a_arg);
+dap_list_t *dap_chain_db_get_sync_groups();
+dap_list_t *dap_chain_db_get_sync_extra_groups();
+void dap_global_db_obj_track_history(void* a_store_data);
 /**
  * Get entry from base
  */
@@ -117,26 +108,3 @@ bool dap_chain_global_db_save(dap_global_db_obj_t* a_objs, size_t a_objs_count);
  */
 char* dap_chain_global_db_hash(const uint8_t *data, size_t data_size);
 char* dap_chain_global_db_hash_fast(const uint8_t *data, size_t data_size);
-
-// Get data according the history log
-dap_list_t* dap_db_log_pack(dap_global_db_obj_t *a_obj, size_t *a_data_size_out);
-
-// Get data according the history log
-//char* dap_db_history_tx(dap_chain_hash_fast_t * a_tx_hash, const char *a_group_mempool);
-//char* dap_db_history_addr(dap_chain_addr_t * a_addr, const char *a_group_mempool);
-//char* dap_db_history(dap_chain_addr_t * a_addr, const char *a_group_mempool);
-
-// Parse data from dap_db_log_pack()
-void* dap_db_log_unpack(const void *a_data, size_t a_data_size, size_t *a_store_obj_count);
-// Get timestamp from dap_db_log_pack()
-//time_t dap_db_log_unpack_get_timestamp(uint8_t *a_data, size_t a_data_size);
-
-// Get last id in log
-uint64_t dap_db_log_get_group_history_last_id(const char *a_history_group_name);
-uint64_t dap_db_log_get_last_id(void);
-// Get log diff as list
-dap_list_t* dap_db_log_get_list(uint64_t first_id);
-// Free list getting from dap_db_log_get_list()
-void dap_db_log_del_list(dap_list_t *a_list);
-// Get log diff as string
-char* dap_db_log_get_diff(size_t *a_data_size_out);
diff --git a/modules/global-db/include/dap_chain_global_db_driver.h b/modules/global-db/include/dap_chain_global_db_driver.h
index 9371684314..ecf75715ec 100644
--- a/modules/global-db/include/dap_chain_global_db_driver.h
+++ b/modules/global-db/include/dap_chain_global_db_driver.h
@@ -35,7 +35,6 @@ typedef struct dap_store_obj {
 	uint8_t type;
     char *group;
     char *key;
-    //const char *c_group;
     const char *c_key;
     uint8_t *value;
 	size_t value_len;
@@ -90,8 +89,3 @@ dap_store_obj_t* dap_chain_global_db_driver_read(const char *a_group, const char
 bool dap_chain_global_db_driver_is(const char *a_group, const char *a_key);
 size_t dap_chain_global_db_driver_count(const char *a_group, uint64_t id);
 dap_list_t* dap_chain_global_db_driver_get_groups_by_mask(const char *a_group_mask);
-
-dap_store_obj_pkt_t *dap_store_packet_multiple(pdap_store_obj_t a_store_obj, time_t a_timestamp,
-                                               dap_store_obj_pkt_t *a_old_pkt);
-dap_store_obj_t *dap_store_unpacket_multiple(const dap_store_obj_pkt_t *a_pkt,
-                                             size_t *a_store_obj_count);
diff --git a/modules/global-db/include/dap_chain_global_db_driver_sqlite.h b/modules/global-db/include/dap_chain_global_db_driver_sqlite.h
index 2d1175df09..7c05f5fd33 100644
--- a/modules/global-db/include/dap_chain_global_db_driver_sqlite.h
+++ b/modules/global-db/include/dap_chain_global_db_driver_sqlite.h
@@ -50,3 +50,6 @@ int dap_db_driver_sqlite_apply_store_obj(dap_store_obj_t *a_store_obj);
 dap_store_obj_t* dap_db_driver_sqlite_read_last_store_obj(const char *a_group);
 dap_store_obj_t* dap_db_driver_sqlite_read_cond_store_obj(const char *a_group, uint64_t a_id, size_t *a_count_out);
 dap_store_obj_t* dap_db_driver_sqlite_read_store_obj(const char *a_group, const char *a_key, size_t *a_count_out);
+dap_list_t* dap_db_driver_sqlite_get_groups_by_mask(const char *a_group_mask);
+size_t dap_db_driver_sqlite_read_count_store(const char *a_group, uint64_t a_id);
+bool dap_db_driver_sqlite_is_obj(const char *a_group, const char *a_key);
diff --git a/modules/global-db/include/dap_chain_global_db_hist.h b/modules/global-db/include/dap_chain_global_db_hist.h
index 840edbc8de..a74fe304f1 100644
--- a/modules/global-db/include/dap_chain_global_db_hist.h
+++ b/modules/global-db/include/dap_chain_global_db_hist.h
@@ -8,6 +8,9 @@
 #define GLOBAL_DB_HIST_REC_SEPARATOR "\r;"
 #define GLOBAL_DB_HIST_KEY_SEPARATOR "\a;"
 
+#define F_DB_LOG_ADD_EXTRA_GROUPS   1
+#define F_DB_LOG_SYNC_FROM_ZERO     2
+
 typedef struct dap_global_db_hist {
     char type;// 'a' add or 'd' delete
     const char *group;
@@ -15,42 +18,39 @@ typedef struct dap_global_db_hist {
     char *keys;
 } dap_global_db_hist_t;
 
-typedef struct dap_db_log_list_obj {
-    pdap_store_obj_t obj;
-    dap_hash_fast_t hash;
-} dap_db_log_list_obj_t;
-
 //Add data to the history log
 bool dap_db_history_add(char a_type, pdap_store_obj_t a_store_obj, size_t a_dap_store_count, const char *a_group);
 
-// Truncate the history log
-bool dap_db_history_truncate(void);
+// for dap_db_log_list_xxx()
 
+typedef struct dap_db_log_list_group {
+    char *name;
+    uint64_t last_id_synced;
+    uint64_t count;
+} dap_db_log_list_group_t;
+
+typedef struct dap_db_log_list_obj {
+    dap_store_obj_pkt_t *pkt;
+    dap_hash_fast_t hash;
+} dap_db_log_list_obj_t;
 
-// for dap_db_log_list_xxx()
 typedef struct dap_db_log_list {
     dap_list_t *list_write; // writed list
     dap_list_t *list_read; // readed list (inside list_write)
     bool is_process;
-    size_t item_start; // first item to read from db
-    size_t item_last; // last item to read from db
     size_t items_rest; // rest items to read from list_read
-    size_t items_number_main;
-    size_t items_number_add;
-    size_t items_number; // remaining items in list_write after reading from db
-    char **group_names;
-    int64_t group_number; // number of group
-    int64_t group_cur; // current group number, -1 for the main group, 0 ... group_count for the additional group
-    size_t *group_number_items; // number of items for each group
-    uint64_t *group_last_id;
-    dap_list_t *add_groups; // additional group for sync
+    size_t items_number; // total items in list_write after reading from db
+    dap_list_t *groups;
     pthread_t thread;
     pthread_mutex_t list_mutex;
 } dap_db_log_list_t;
 
-dap_db_log_list_t* dap_db_log_list_start(uint64_t first_id, dap_list_t *a_add_groups);
+dap_db_log_list_t* dap_db_log_list_start(dap_chain_node_addr_t a_addr, int flags);
 size_t dap_db_log_list_get_count(dap_db_log_list_t *a_db_log_list);
 size_t dap_db_log_list_get_count_rest(dap_db_log_list_t *a_db_log_list);
 dap_db_log_list_obj_t *dap_db_log_list_get(dap_db_log_list_t *a_db_log_list);
 void dap_db_log_list_delete(dap_db_log_list_t *a_db_log_list);
+// Get last id in log
+uint64_t dap_db_log_get_group_last_id(const char *a_group_name);
+uint64_t dap_db_log_get_last_id(void);
 
diff --git a/modules/global-db/include/dap_chain_global_db_remote.h b/modules/global-db/include/dap_chain_global_db_remote.h
index 7a58f3b1e4..7e63ac02b7 100644
--- a/modules/global-db/include/dap_chain_global_db_remote.h
+++ b/modules/global-db/include/dap_chain_global_db_remote.h
@@ -4,16 +4,24 @@
 #include <time.h>
 #include "dap_chain.h"
 #include "dap_chain_common.h"
+#include "dap_chain_global_db_driver.h"
 // Set addr for current node
 bool dap_db_set_cur_node_addr(uint64_t a_address, char *a_net_name);
 bool dap_db_set_cur_node_addr_exp(uint64_t a_address, char *a_net_name );
 uint64_t dap_db_get_cur_node_addr(char *a_net_name);
 
 // Set last id for remote node
-bool dap_db_set_last_id_remote(uint64_t a_node_addr, uint64_t a_id);
+bool dap_db_set_last_id_remote(uint64_t a_node_addr, uint64_t a_id, char *a_group);
 // Get last id for remote node
-uint64_t dap_db_get_last_id_remote(uint64_t a_node_addr);
+uint64_t dap_db_get_last_id_remote(uint64_t a_node_addr, char *a_group);
 // Set last hash for chain for remote node
 bool dap_db_set_last_hash_remote(uint64_t a_node_addr, dap_chain_t *a_chain, dap_chain_hash_fast_t *a_hash);
 // Get last hash for chain for remote node
 dap_chain_hash_fast_t *dap_db_get_last_hash_remote(uint64_t a_node_addr, dap_chain_t *a_chain);
+
+dap_store_obj_pkt_t *dap_store_packet_single(pdap_store_obj_t a_store_obj);
+dap_store_obj_pkt_t *dap_store_packet_multiple(dap_store_obj_pkt_t *a_old_pkt, dap_store_obj_pkt_t *a_new_pkt);
+dap_store_obj_t *dap_store_unpacket_multiple(const dap_store_obj_pkt_t *a_pkt, size_t *a_store_obj_count);
+char *dap_store_packet_get_group(dap_store_obj_pkt_t *a_pkt);
+uint64_t dap_store_packet_get_id(dap_store_obj_pkt_t *a_pkt);
+void dap_store_packet_change_id(dap_store_obj_pkt_t *a_pkt, uint64_t a_id);
diff --git a/modules/net/dap_chain_net.c b/modules/net/dap_chain_net.c
index 0f08f63f2f..1d26565b6d 100644
--- a/modules/net/dap_chain_net.c
+++ b/modules/net/dap_chain_net.c
@@ -241,7 +241,7 @@ static void s_net_proc_kill( dap_chain_net_t * a_net );
 int s_net_load(const char * a_net_name, uint16_t a_acl_idx);
 
 // Notify callback for GlobalDB changes
-static void s_gbd_history_callback_notify (void * a_arg,const char a_op_code, const char * a_prefix, const char * a_group,
+static void s_gbd_history_callback_notify (void * a_arg, const char a_op_code, const char * a_group,
                                                      const char * a_key, const void * a_value,
                                                      const size_t a_value_len);
 static void s_chain_callback_notify(void * a_arg, dap_chain_t *a_chain, dap_chain_cell_id_t a_id, void *a_atom, size_t a_atom_size);
@@ -296,7 +296,7 @@ int dap_chain_net_state_go_to(dap_chain_net_t * a_net, dap_chain_net_state_t a_n
     pthread_mutex_lock( &PVT(a_net)->state_mutex_cond); // Preventing call of state_go_to before wait cond will be armed
     // set flag for sync
     PVT(a_net)->flags |= F_DAP_CHAIN_NET_GO_SYNC;
-    dap_chain_net_set_flag_sync_from_zero(a_net, true);
+    //PVT(a_net)->flags |= F_DAP_CHAIN_NET_SYNC_FROM_ZERO;
 #ifndef _WIN32
     pthread_cond_signal( &PVT(a_net)->state_proc_cond );
 #else
@@ -313,10 +313,10 @@ void dap_chain_net_set_srv_callback_notify(dap_global_db_obj_callback_notify_t a
     s_srv_callback_notify = a_callback;
 }
 
-void dap_chain_net_sync_gdb_broadcast(void *a_arg, const char a_op_code, const char *a_prefix, const char *a_group,
+void dap_chain_net_sync_gdb_broadcast(void *a_arg, const char a_op_code, const char *a_group,
                                       const char *a_key, const void *a_value, const size_t a_value_len)
 {
-    UNUSED(a_prefix);
+    UNUSED(a_value);
     UNUSED(a_value_len);
     dap_chain_net_t *l_net = (dap_chain_net_t *)a_arg;
     if (PVT(l_net)->state == NET_STATE_ONLINE) {
@@ -337,7 +337,7 @@ void dap_chain_net_sync_gdb_broadcast(void *a_arg, const char a_op_code, const c
         l_obj->type = (uint8_t)a_op_code;
         DAP_DELETE(l_obj->group);
         l_obj->group = dap_strdup(a_group);
-        dap_store_obj_pkt_t *l_data_out = dap_store_packet_multiple(l_obj, 0, NULL);
+        dap_store_obj_pkt_t *l_data_out = dap_store_packet_single(l_obj);
         dap_store_obj_free(l_obj, 1);
         dap_chain_t *l_chain = dap_chain_net_get_chain_by_name(l_net, "gdb");
         dap_chain_id_t l_chain_id = l_chain ? l_chain->id : (dap_chain_id_t) {};
@@ -348,6 +348,7 @@ void dap_chain_net_sync_gdb_broadcast(void *a_arg, const char a_op_code, const c
                                                  l_chain_id.uint64, l_net->pub.cell_id.uint64, l_data_out,
                                                  sizeof(dap_store_obj_pkt_t) + l_data_out->data_size);
         }
+        pthread_rwlock_unlock(&PVT(l_net)->rwlock);
         DAP_DELETE(l_data_out);
     }
 }
@@ -362,16 +363,16 @@ void dap_chain_net_sync_gdb_broadcast(void *a_arg, const char a_op_code, const c
  * @param a_value
  * @param a_value_len
  */
-static void s_gbd_history_callback_notify (void * a_arg, const char a_op_code, const char * a_prefix, const char * a_group,
+static void s_gbd_history_callback_notify (void * a_arg, const char a_op_code, const char * a_group,
                                                      const char * a_key, const void * a_value, const size_t a_value_len)
 {
     if (!a_arg) {
         return;
     }
-    dap_chain_node_mempool_autoproc_notify(a_arg, a_op_code, a_prefix, a_group, a_key, a_value, a_value_len);
-    dap_chain_net_sync_gdb_broadcast(a_arg, a_op_code, a_prefix, a_group, a_key, a_value, a_value_len);
+    dap_chain_node_mempool_autoproc_notify(a_arg, a_op_code, a_group, a_key, a_value, a_value_len);
+    dap_chain_net_sync_gdb_broadcast(a_arg, a_op_code, a_group, a_key, a_value, a_value_len);
     if (s_srv_callback_notify) {
-        s_srv_callback_notify(a_arg, a_op_code, a_prefix, a_group, a_key, a_value, a_value_len);
+        s_srv_callback_notify(a_arg, a_op_code, a_group, a_key, a_value, a_value_len);
     }
 }
 
@@ -1074,9 +1075,6 @@ int dap_chain_net_init()
         "net -net <chain net name> ledger reload\n"
             "\tPurge the cache of chain net ledger and recalculate it from chain file\n"                                        );
     s_seed_mode = dap_config_get_item_bool_default(g_config,"general","seed_mode",false);
-    dap_chain_global_db_add_history_group_prefix("global", GROUP_LOCAL_HISTORY);
-
-    dap_chain_global_db_add_history_callback_notify("global", s_gbd_history_callback_notify, NULL );
 
     // maximum number of connections to other nodes
     s_max_links_count = dap_config_get_item_int32_default(g_config, "general", "max_links", s_max_links_count);
@@ -1598,13 +1596,13 @@ int s_net_load(const char * a_net_name, uint16_t a_acl_idx)
         l_net->pub.gdb_groups_prefix = dap_strdup (
                     dap_config_get_item_str_default(l_cfg , "general" , "gdb_groups_prefix",
                                                     dap_config_get_item_str(l_cfg , "general" , "name" ) ) );
-        dap_chain_global_db_add_history_group_prefix( l_net->pub.gdb_groups_prefix, GROUP_LOCAL_HISTORY);
-        dap_chain_global_db_add_history_callback_notify(l_net->pub.gdb_groups_prefix, s_gbd_history_callback_notify, l_net );
+        dap_chain_global_db_add_sync_group("global", s_gbd_history_callback_notify, l_net);
+        dap_chain_global_db_add_sync_group(l_net->pub.gdb_groups_prefix, s_gbd_history_callback_notify, l_net);
 
         l_net->pub.gdb_nodes = dap_strdup_printf("%s.nodes",l_net->pub.gdb_groups_prefix);
         l_net->pub.gdb_nodes_aliases = dap_strdup_printf("%s.nodes.aliases",l_net->pub.gdb_groups_prefix);
 
-        // for sync special groups - nodes
+        // nodes for special sync
         char **l_gdb_sync_nodes_addrs = dap_config_get_array_str(l_cfg, "general", "gdb_sync_nodes_addrs",
                 &l_net_pvt->gdb_sync_nodes_addrs_count);
         if(l_gdb_sync_nodes_addrs && l_net_pvt->gdb_sync_nodes_addrs_count > 0) {
@@ -1614,37 +1612,13 @@ int s_net_load(const char * a_net_name, uint16_t a_acl_idx)
                 dap_chain_node_addr_from_str(l_net_pvt->gdb_sync_nodes_addrs + i, l_gdb_sync_nodes_addrs[i]);
             }
         }
-        // for sync special groups - groups
-        char **l_gdb_sync_groups = dap_config_get_array_str(l_cfg, "general", "gdb_sync_groups", &l_net_pvt->gdb_sync_groups_count);
-        if(l_gdb_sync_groups && l_net_pvt->gdb_sync_groups_count > 0) {
-            l_net_pvt->gdb_sync_groups = (char **) DAP_NEW_SIZE(char**, sizeof(char*)*l_net_pvt->gdb_sync_groups_count);
-            for(uint16_t i = 0; i < l_net_pvt->gdb_sync_groups_count; i++) {
-                l_net_pvt->gdb_sync_groups[i] = dap_strdup(l_gdb_sync_groups[i]);
-                // added group to history log
-                dap_list_t *l_groups0 = dap_chain_global_db_driver_get_groups_by_mask(l_net_pvt->gdb_sync_groups[i]);
-                dap_list_t *l_groups = l_groups0;
-                while(l_groups) {
-                    char *l_group_name = l_groups->data;
-                    // do not use groups with names like *.del
-                    if(dap_fnmatch("*.del", l_group_name, 0)) {
-                        const char *l_history_group = dap_chain_global_db_add_history_extra_group(l_group_name,
-                                                        l_net_pvt->gdb_sync_nodes_addrs,
-                                                        &l_net_pvt->gdb_sync_nodes_addrs_count);
-                        dap_chain_global_db_add_history_extra_group_callback_notify(l_group_name,
-                                s_gbd_history_callback_notify, l_net);
-                        // create history for group
-                        if(dap_db_log_get_group_history_last_id(l_history_group) <= 0) {
-                            size_t l_data_size_out = 0;
-                            dap_store_obj_t *l_obj = dap_chain_global_db_obj_gr_get(NULL, &l_data_size_out, l_group_name);
-                            if(l_obj && l_data_size_out > 0) {
-                                dap_db_history_add('a', l_obj, l_data_size_out, l_history_group);
-                                dap_store_obj_free(l_obj, l_data_size_out);
-                            }
-                        }
-                    }
-                    l_groups = dap_list_next(l_groups);
-                }
-                dap_list_free_full(l_groups0, (dap_callback_destroyed_t)free);
+        // groups for special sync
+        uint16_t l_gdb_sync_groups_count;
+        char **l_gdb_sync_groups = dap_config_get_array_str(l_cfg, "general", "gdb_sync_groups", &l_gdb_sync_groups_count);
+        if (l_gdb_sync_groups && l_gdb_sync_groups_count > 0) {
+            for(uint16_t i = 0; i < l_gdb_sync_groups_count; i++) {
+                // add group to special sync
+                dap_chain_global_db_add_sync_extra_group(l_gdb_sync_groups[i], s_gbd_history_callback_notify, l_net);
             }
         }
 
@@ -2487,18 +2461,16 @@ dap_chain_datum_tx_t * dap_chain_net_get_tx_by_hash(dap_chain_net_t * a_net, dap
  * @param a_node_addr
  * @return
  */
-dap_list_t * dap_chain_net_get_add_gdb_group(dap_chain_net_t * a_net, dap_chain_node_addr_t a_node_addr)
+bool dap_chain_net_get_add_gdb_group(dap_chain_net_t *a_net, dap_chain_node_addr_t a_node_addr)
 {
-    dap_list_t *l_list_groups = NULL;
     if(!a_net || !PVT(a_net) || !PVT(a_net)->gdb_sync_nodes_addrs)
-        return NULL;
+        return false;
     for(uint16_t i = 0; i < PVT(a_net)->gdb_sync_nodes_addrs_count; i++) {
         if(a_node_addr.uint64 == PVT(a_net)->gdb_sync_nodes_addrs[i].uint64) {
-            for(uint16_t j = 0; j < PVT(a_net)->gdb_sync_groups_count; j++)
-                l_list_groups = dap_list_append(l_list_groups, PVT(a_net)->gdb_sync_groups[j]);
+            return true;
         }
     }
-    return l_list_groups;
+    return false;
 }
 
 /**
diff --git a/modules/net/dap_chain_node.c b/modules/net/dap_chain_node.c
index 5f9ac6fcd0..817455ff9e 100644
--- a/modules/net/dap_chain_node.c
+++ b/modules/net/dap_chain_node.c
@@ -323,10 +323,9 @@ void dap_chain_node_mempool_autoproc_deinit()
     s_mempool_auto = false;
 }
 
-void dap_chain_node_mempool_autoproc_notify(void *a_arg, const char a_op_code, const char *a_prefix, const char *a_group,
+void dap_chain_node_mempool_autoproc_notify(void *a_arg, const char a_op_code, const char *a_group,
                                              const char *a_key, const void *a_value, const size_t a_value_len)
 {
-    UNUSED(a_prefix);
     UNUSED(a_value_len);
     if (!a_arg || !a_value || !s_mempool_auto || a_op_code != 'a') {
         return;
diff --git a/modules/net/dap_chain_node_cli_cmd.c b/modules/net/dap_chain_node_cli_cmd.c
index 13a4da7ee3..e7da6ab2f7 100644
--- a/modules/net/dap_chain_node_cli_cmd.c
+++ b/modules/net/dap_chain_node_cli_cmd.c
@@ -1053,7 +1053,7 @@ int com_node(int a_argc, char ** a_argv, void *arg_func, char **a_str_reply)
         dap_stream_ch_chain_sync_request_t l_sync_request = { { 0 } };
          dap_stream_ch_t * l_ch_chain = dap_client_get_stream_ch_unsafe(l_node_client->client, dap_stream_ch_chain_get_id());
          // fill begin id
-         l_sync_request.id_start = dap_db_get_last_id_remote(l_remote_node_info->hdr.address.uint64);
+         l_sync_request.id_start = 1;
          // fill current node address
          l_sync_request.node_addr.uint64 = dap_chain_net_get_cur_addr_int(l_net);
 
diff --git a/modules/net/dap_chain_node_client.c b/modules/net/dap_chain_node_client.c
index 43107e3e93..70bde830cd 100644
--- a/modules/net/dap_chain_node_client.c
+++ b/modules/net/dap_chain_node_client.c
@@ -166,9 +166,7 @@ static void s_stage_status_error_callback(dap_client_t *a_client, void *a_arg)
 
         if (l_node_client->callbacks.disconnected) {
             l_node_client->callbacks.disconnected(l_node_client, l_node_client->callbacks_arg);
-        }
-
-        if (l_node_client->keep_connection) {
+        } else if (l_node_client->keep_connection) {
             dap_events_socket_uuid_t *l_uuid = DAP_NEW(dap_events_socket_uuid_t);
             memcpy(l_uuid, &l_node_client->uuid, sizeof(dap_events_socket_uuid_t));
             dap_timerfd_start_on_worker(l_node_client->stream_worker? l_node_client->stream_worker->worker: dap_events_worker_get_auto(),s_timer_update_states*1000,s_timer_update_states_callback, l_uuid);
diff --git a/modules/net/include/dap_chain_net.h b/modules/net/include/dap_chain_net.h
index 77f3bc52c6..ad6475656f 100644
--- a/modules/net/include/dap_chain_net.h
+++ b/modules/net/include/dap_chain_net.h
@@ -170,12 +170,12 @@ DAP_STATIC_INLINE char * dap_chain_net_get_gdb_group_mempool(dap_chain_t * l_cha
 dap_chain_t * dap_chain_net_get_chain_by_chain_type(dap_chain_net_t * l_net, dap_chain_type_t a_datum_type);
 char * dap_chain_net_get_gdb_group_mempool_by_chain_type(dap_chain_net_t * l_net, dap_chain_type_t a_datum_type);
 dap_chain_net_t **dap_chain_net_list(uint16_t *a_size);
-dap_list_t * dap_chain_net_get_add_gdb_group(dap_chain_net_t * a_net, dap_chain_node_addr_t a_node_addr);
+bool dap_chain_net_get_add_gdb_group(dap_chain_net_t *a_net, dap_chain_node_addr_t a_node_addr);
 
 int dap_chain_net_verify_datum_for_add(dap_chain_net_t *a_net, dap_chain_datum_t * a_datum );
 void dap_chain_net_dump_datum(dap_string_t * a_str_out, dap_chain_datum_t * a_datum, const char *a_hash_out_type);
 void dap_chain_net_set_srv_callback_notify(dap_global_db_obj_callback_notify_t a_callback);
-void dap_chain_net_sync_gdb_broadcast(void *a_arg, const char a_op_code, const char *a_prefix, const char *a_group,
+void dap_chain_net_sync_gdb_broadcast(void *a_arg, const char a_op_code, const char *a_group,
                                       const char *a_key, const void *a_value, const size_t a_value_len);
 
 struct dap_chain_node_client * dap_chain_net_client_create_n_connect( dap_chain_net_t * a_net, struct dap_chain_node_info *a_link_info);
diff --git a/modules/net/include/dap_chain_node.h b/modules/net/include/dap_chain_node.h
index 4bfc7f23c1..9d84beabcc 100644
--- a/modules/net/include/dap_chain_node.h
+++ b/modules/net/include/dap_chain_node.h
@@ -147,6 +147,6 @@ inline static char* dap_chain_node_addr_to_hash_str(dap_chain_node_addr_t *addre
 bool dap_chain_node_mempool_process(dap_chain_t *a_chain, dap_chain_node_role_t a_role, dap_chain_datum_t *a_datum);
 bool dap_chain_node_mempool_autoproc_init();
 void dap_chain_node_mempool_autoproc_deinit();
-void dap_chain_node_mempool_autoproc_notify(void *a_arg, const char a_op_code, const char *a_prefix, const char *a_group,
+void dap_chain_node_mempool_autoproc_notify(void *a_arg, const char a_op_code, const char *a_group,
                                              const char *a_key, const void *a_value, const size_t a_value_len);
 
diff --git a/modules/net/srv/dap_chain_net_srv_order.c b/modules/net/srv/dap_chain_net_srv_order.c
index 1c06eaf7e8..9e1f98e2e0 100644
--- a/modules/net/srv/dap_chain_net_srv_order.c
+++ b/modules/net/srv/dap_chain_net_srv_order.c
@@ -63,7 +63,7 @@ char *s_server_continents[]={
 		"Antarctica"
  };
 
-static void s_srv_order_callback_notify(void *a_arg, const char a_op_code, const char *a_prefix, const char *a_group,
+static void s_srv_order_callback_notify(void *a_arg, const char a_op_code, const char *a_group,
                                    const char *a_key, const void *a_value, const size_t a_value_len);
 
 /**
@@ -520,10 +520,9 @@ void dap_chain_net_srv_order_dump_to_string(dap_chain_net_srv_order_t *a_order,d
     }
 }
 
-static void s_srv_order_callback_notify(void *a_arg, const char a_op_code, const char *a_prefix, const char *a_group,
+static void s_srv_order_callback_notify(void *a_arg, const char a_op_code, const char *a_group,
                                    const char *a_key, const void *a_value, const size_t a_value_len)
 {
-    UNUSED(a_prefix);
     UNUSED(a_value_len);
     if (!a_arg || !a_value || a_op_code != 'a' || !dap_config_get_item_bool_default(g_config, "srv", "order_signed_only", false)) {
         return;
-- 
GitLab