From 04c9c3f2ef5d72c1b2f52fc53e062244fe0bc627 Mon Sep 17 00:00:00 2001
From: Roman Khlopkov <roman.khlopkov@demlabs.net>
Date: Fri, 3 Sep 2021 06:32:43 +0000
Subject: [PATCH] bugs-4779

---
 dap-sdk/net/core/dap_proc_thread.c            |  23 +-
 modules/channel/chain/dap_stream_ch_chain.c   |  16 +-
 modules/global-db/dap_chain_global_db.c       | 377 ++++++------------
 modules/global-db/dap_chain_global_db_hist.c  |   2 -
 .../global-db/include/dap_chain_global_db.h   |   1 +
 modules/net/dap_chain_net.c                   |   3 +-
 modules/net/include/dap_chain_net.h           |   2 +-
 7 files changed, 149 insertions(+), 275 deletions(-)

diff --git a/dap-sdk/net/core/dap_proc_thread.c b/dap-sdk/net/core/dap_proc_thread.c
index dcb689326b..4aabf43938 100644
--- a/dap-sdk/net/core/dap_proc_thread.c
+++ b/dap-sdk/net/core/dap_proc_thread.c
@@ -295,9 +295,9 @@ int dap_proc_thread_esocket_update_poll_flags(dap_proc_thread_t * a_thread, dap_
     }
     a_thread->poll[a_esocket->poll_index].events= a_esocket->poll_base_flags;
     if( a_esocket->flags & DAP_SOCK_READY_TO_READ)
-        a_thread->poll[a_esocket->poll_index].revents |= POLLIN;
+        a_thread->poll[a_esocket->poll_index].events |= POLLIN;
     if( a_esocket->flags & DAP_SOCK_READY_TO_WRITE)
-        a_thread->poll[a_esocket->poll_index].revents |= POLLOUT;
+        a_thread->poll[a_esocket->poll_index].events |= POLLOUT;
         
 #elif defined (DAP_EVENTS_CAPS_KQUEUE)
 
@@ -451,18 +451,6 @@ static void * s_proc_thread_function(void * a_arg)
         return NULL;
     }
 	
-    // Add exit event
-    l_thread->event_exit->ev.events     = l_thread->event_exit->ev_base_flags;
-    l_thread->event_exit->ev.data.ptr   = l_thread->event_exit;
-    if( epoll_ctl(l_thread->epoll_ctl, EPOLL_CTL_ADD, l_thread->event_exit->socket , &l_thread->event_exit->ev) != 0 ){
-#ifdef DAP_OS_WINDOWS
-        errno = WSAGetLastError();
-#endif
-        log_it(L_CRITICAL, "Can't add exit event on epoll ctl, err: %d", errno);
-        return NULL;
-    }
-
-
     // Add exit event
     l_thread->event_exit->ev.events     = l_thread->event_exit->ev_base_flags;
     l_thread->event_exit->ev.data.ptr   = l_thread->event_exit;
@@ -524,13 +512,6 @@ static void * s_proc_thread_function(void * a_arg)
     l_thread->esockets[l_thread->poll_count] = l_thread->proc_event;
     l_thread->poll_count++;
 	
-    // Add exit event
-    l_thread->poll[l_thread->poll_count].fd = l_thread->event_exit->fd;
-    l_thread->poll[l_thread->poll_count].events = l_thread->event_exit->poll_base_flags;
-    l_thread->esockets[l_thread->poll_count] = l_thread->event_exit;
-    l_thread->poll_count++;
-
-
     // Add exit event
     l_thread->poll[l_thread->poll_count].fd = l_thread->event_exit->fd;
     l_thread->poll[l_thread->poll_count].events = l_thread->event_exit->poll_base_flags;
diff --git a/modules/channel/chain/dap_stream_ch_chain.c b/modules/channel/chain/dap_stream_ch_chain.c
index d010b6d519..7c8392f5b5 100644
--- a/modules/channel/chain/dap_stream_ch_chain.c
+++ b/modules/channel/chain/dap_stream_ch_chain.c
@@ -400,7 +400,7 @@ static bool s_sync_out_gdb_proc_callback(dap_proc_thread_t *a_thread, void *a_ar
                             l_start_item, l_local_last_id, NODE_ADDR_FP_ARGS_S(l_sync_request->request.node_addr));
     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(l_start_item, l_add_groups);
+    dap_db_log_list_t *l_db_log = dap_db_log_list_start(1/*l_start_item*/, l_add_groups);
 
     if(l_db_log) {
         l_sync_request->gdb.db_log = l_db_log;
@@ -441,7 +441,7 @@ static bool s_sync_update_gdb_proc_callback(dap_proc_thread_t *a_thread, void *a
     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, l_buf);
+                      l_sync_request->request.id_start + 1, l_buf);
     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);
@@ -667,6 +667,8 @@ static bool s_gdb_in_pkt_proc_callback(dap_proc_thread_t *a_thread, void *a_arg)
             }
 
             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);
@@ -862,11 +864,11 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                     memcpy(&l_hash_item->hash, &l_element->hash, sizeof (l_element->hash));
                     l_hash_item->size = l_element->size;
                     HASH_ADD(hh, l_ch_chain->remote_gdbs, hash, sizeof (l_hash_item->hash), l_hash_item);
-                    if (s_debug_more){
+                    /*if (s_debug_more){
                         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;
@@ -1117,6 +1119,7 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                 }
                 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_CHAINS;
+                l_ch_chain->stats_request_atoms_processed = 0;
                 if (l_ch_pkt->hdr.type == DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNC_CHAINS) {
                     char *l_hash_from_str = dap_chain_hash_fast_to_str_new(&l_ch_chain->request.hash_from);
                     char *l_hash_to_str = dap_chain_hash_fast_to_str_new(&l_ch_chain->request.hash_to);
@@ -1144,7 +1147,6 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                        NODE_ADDR_FP_ARGS_S(l_ch_chain->request.node_addr),
                        l_chain_pkt_data_size,      l_ch_chain->request_hdr.net_id.uint64 ,
                        l_ch_chain->request_hdr.chain_id.uint64, l_ch_chain->request_hdr.cell_id.uint64);
-                l_ch_chain->stats_request_atoms_processed = 0;
             }else{
                 log_it(L_WARNING,"Incorrect data size %zd in packet DAP_STREAM_CH_CHAIN_PKT_TYPE_FIRST_CHAIN", l_chain_pkt_data_size);
                 s_stream_ch_write_error_unsafe(a_ch, l_chain_pkt->hdr.net_id.uint64,
@@ -1353,7 +1355,8 @@ void s_stream_ch_packet_out(dap_stream_ch_t* a_ch, void* a_arg)
             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);
             size_t l_pkt_size = 0;
-            while (l_obj) {
+            uint_fast16_t l_skip_count = 0;
+            while (l_obj && l_skip_count < s_skip_in_reactor_count) {
                 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
@@ -1363,6 +1366,7 @@ void s_stream_ch_packet_out(dap_stream_ch_t* a_ch, void* a_arg)
                         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 {
                     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));
diff --git a/modules/global-db/dap_chain_global_db.c b/modules/global-db/dap_chain_global_db.c
index 4a6ca4f938..b844ddca94 100644
--- a/modules/global-db/dap_chain_global_db.c
+++ b/modules/global-db/dap_chain_global_db.c
@@ -168,9 +168,11 @@ void dap_chain_global_db_add_history_callback_notify(const char * 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)
 {
+    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("local.history.%s", 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;
 }
@@ -398,18 +400,20 @@ uint8_t * dap_chain_global_db_get(const char *a_key, size_t *a_data_out)
  */
 static bool global_db_gr_del_add(char *a_key,const char *a_group, time_t a_timestamp)
 {
-    dap_store_obj_t store_data;// = DAP_NEW_Z_SIZE(dap_store_obj_t, sizeof(struct dap_store_obj));
+    dap_store_obj_t store_data;
     memset(&store_data, 0, sizeof(dap_store_obj_t));
     store_data.type = 'a';
-    store_data.key = a_key;//dap_strdup(a_key);
+    store_data.key = a_key;
     // no data
     store_data.value = NULL;
     store_data.value_len = 0;
     // group = parent group + '.del'
     store_data.group = dap_strdup_printf("%s.del", a_group);
-    store_data.timestamp = a_timestamp;//time(NULL);
+    store_data.timestamp = a_timestamp;
     lock();
-    int l_res = dap_chain_global_db_driver_add(&store_data, 1);
+    int l_res = 0;
+    if (!dap_chain_global_db_driver_is(store_data.group, store_data.key))
+        l_res = dap_chain_global_db_driver_add(&store_data, 1);
     unlock();
     DAP_DELETE(store_data.group);
     if(l_res>=0)
@@ -424,7 +428,7 @@ static bool global_db_gr_del_del(char *a_key,const char *a_group)
 {
     if(!a_key)
         return NULL;
-    dap_store_obj_t store_data;// = DAP_NEW_Z_SIZE(dap_store_obj_t, sizeof(struct dap_store_obj));
+    dap_store_obj_t store_data;
     memset(&store_data, 0, sizeof(dap_store_obj_t));
     store_data.key = a_key;
    // store_data->c_key = a_key;
@@ -468,155 +472,7 @@ time_t global_db_gr_del_get_timestamp(const char *a_group, char *a_key)
     return l_timestamp;
 }
 
-/**
- *
- */
-
-/**
- * @brief dap_chain_global_db_gr_set
- * @param a_key
- * @param a_value
- * @param a_value_len
- * @param a_group
- * @details Set one entry to base. IMPORTANT: a_key and a_value should be passed without free after (it will be released by gdb itself)
- * @return
- */
-bool dap_chain_global_db_gr_set(char *a_key, void *a_value, size_t a_value_len, const char *a_group)
-{
-    dap_store_obj_t store_data;// = DAP_NEW_Z_SIZE(dap_store_obj_t, sizeof(struct dap_store_obj));
-    memset(&store_data, 0, sizeof(dap_store_obj_t));
-    store_data.type = 'a';
-    store_data.key = a_key;//dap_strdup(a_key);
-    store_data.value = a_value;//DAP_NEW_Z_SIZE(uint8_t, a_value_len);
-
-    //memcpy(store_data.value, a_value, a_value_len);
-
-    store_data.value_len = (a_value_len == (size_t) -1) ? dap_strlen((const char*) a_value) : a_value_len;
-    store_data.group = (char*)a_group;//dap_strdup(a_group);
-    store_data.timestamp = time(NULL);
-    lock();
-    int l_res = dap_chain_global_db_driver_add(&store_data, 1);
-    unlock();
-
-    // Extract prefix if added successfuly, add history log and call notify callback if present
-    if(!l_res) {
-        // Delete info about the deleted entry from the base if one present
-        global_db_gr_del_del(a_key, a_group);
-
-        char * l_group_prefix = extract_group_prefix(a_group);
-        history_group_item_t * l_history_group_item = NULL;
-        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('a', &store_data, 1, l_history_group_item->group_name_for_history);
-                unlock();
-            }
-            if(l_history_group_item->callback_notify)
-                l_history_group_item->callback_notify(l_history_group_item->callback_arg, 'a', l_group_prefix, a_group,
-                        a_key, a_value, a_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, a_group, l_history_extra_group_item);
-
-            if(l_history_extra_group_item) {
-                lock();
-                dap_db_history_add('a', &store_data, 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, 'a',
-                            l_group_prefix,
-                            a_group,
-                            a_key, a_value, a_value_len);
-            }
-        }
-        if(l_group_prefix)
-            DAP_DELETE(l_group_prefix);
-    } else {
-        log_it(L_ERROR, "Save error: %d", l_res);
-    }
-    //DAP_DELETE(store_data);
-
-    return !l_res;
-}
-
-bool dap_chain_global_db_set( char *a_key,  void *a_value, size_t a_value_len)
-{
-    return dap_chain_global_db_gr_set(a_key, a_value, a_value_len, GROUP_LOCAL_GENERAL);
-}
 
-/**
- * Delete entry from base
- */
-bool dap_chain_global_db_gr_del(char *a_key,const char *a_group)
-{
-    if(!a_key)
-        return NULL;
-    dap_store_obj_t store_data;// = DAP_NEW_Z_SIZE(dap_store_obj_t, sizeof(struct dap_store_obj));
-    memset(&store_data, 0, sizeof(dap_store_obj_t));
-    store_data.key = a_key;
-    store_data.group = (char*)a_group;
-    lock();
-    int l_res = dap_chain_global_db_driver_delete(&store_data, 1);
-    unlock();
-    // do not add to history if l_res=1 (already deleted)
-    if(!l_res) {
-        // added to Del group
-        global_db_gr_del_add(a_key, a_group, time(NULL));
-        // Extract prefix
-        char * l_group_prefix = extract_group_prefix(a_group);
-        history_group_item_t * l_history_group_item = NULL;
-        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('d', &store_data, 1, l_history_group_item->group_name_for_history);
-                unlock();
-            }
-            if(l_history_group_item->callback_notify)
-                l_history_group_item->callback_notify(l_history_group_item->callback_arg, 'd', l_group_prefix, a_group,
-                        a_key, NULL, 0);
-        }
-        // looking for extra group
-        else {
-            history_extra_group_item_t * l_history_extra_group_item = NULL;
-            HASH_FIND_STR(s_history_extra_group_items, a_group, l_history_extra_group_item);
-
-            if(l_history_extra_group_item) {
-                lock();
-                dap_db_history_add('d', &store_data, 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, 'd',
-                            l_group_prefix, a_group, a_key, NULL, 0);
-            }
-        }
-        if(l_group_prefix)
-            DAP_DELETE(l_group_prefix);
-    }
-    //DAP_DELETE(store_data);
-    if(l_res>=0){
-        // added to Del group
-        global_db_gr_del_add(a_key, a_group, time(NULL));
-        /*/ read del info
-        char *l_group = dap_strdup_printf("%s.del", a_group);
-        size_t l_data_size_out = 0;
-        dap_store_obj_t *l_objs = dap_chain_global_db_obj_gr_get(a_key, &l_data_size_out,l_group);
-        // update timestamp
-        if(l_objs){
-            if(l_objs->timestamp<time(NULL))
-        dap_store_obj_free(l_objs, l_data_size_out);
-        }
-        DAP_DELETE(l_group);*/
-        return true;
-    }
-    return false;
-}
 bool dap_chain_global_db_del(char *a_key)
 {
     return dap_chain_global_db_gr_del(a_key, GROUP_LOCAL_GENERAL);
@@ -687,6 +543,124 @@ 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)
+{
+    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);
+
+
+        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;
+                }
+            }
+        }
+        // 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);
+            }
+        }
+
+        DAP_DELETE(l_group_prefix);
+    }
+}
+
+
+/**
+ * @brief dap_chain_global_db_gr_set
+ * @param a_key
+ * @param a_value
+ * @param a_value_len
+ * @param a_group
+ * @details Set one entry to base. IMPORTANT: a_key and a_value should be passed without free after (it will be released by gdb itself)
+ * @return
+ */
+bool dap_chain_global_db_gr_set(char *a_key, void *a_value, size_t a_value_len, const char *a_group)
+{
+    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;
+    store_data.group = (char*)a_group;
+    store_data.timestamp = time(NULL);
+    lock();
+    int l_res = dap_chain_global_db_driver_add(&store_data, 1);
+    unlock();
+
+    // 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);
+    } else {
+        log_it(L_ERROR, "Save error: %d", l_res);
+    }
+
+    return !l_res;
+}
+
+bool dap_chain_global_db_set( char *a_key,  void *a_value, size_t a_value_len)
+{
+    return dap_chain_global_db_gr_set(a_key, a_value, a_value_len, GROUP_LOCAL_GENERAL);
+}
+
+/**
+ * Delete entry from base
+ */
+bool dap_chain_global_db_gr_del(char *a_key,const char *a_group)
+{
+    if(!a_key)
+        return NULL;
+    dap_store_obj_t store_data;
+    memset(&store_data, 0, sizeof(dap_store_obj_t));
+    store_data.key = a_key;
+    store_data.group = (char*)a_group;
+    lock();
+    int l_res = dap_chain_global_db_driver_delete(&store_data, 1);
+    unlock();
+    // do not add to history if l_res=1 (already deleted)
+    if(!l_res) {
+        dap_global_db_obj_track_history(&store_data, 1);
+    }
+    return !l_res;
+}
+
 /**
  * Write to the database from an array of data_size bytes
  *
@@ -704,65 +678,9 @@ bool dap_chain_global_db_obj_save(void* a_store_data, size_t a_objs_count)
 
     // Extract prefix if added successfuly, add history log and call notify callback if present
     if(!l_res) {
-        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);
-
-
-            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;
-                    }
-                }
-            }
-            // 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);
-                }
-            }
-
-            DAP_DELETE(l_group_prefix);
-        }
-
+        dap_global_db_obj_track_history(a_store_data, a_objs_count);
     }
-    if(l_res >= 0) {
-        return true;
-    }
-    return false;
+    return !l_res;
 }
 
 bool dap_chain_global_db_gr_save(dap_global_db_obj_t* a_objs, size_t a_objs_count, const char *a_group)
@@ -784,38 +702,10 @@ bool dap_chain_global_db_gr_save(dap_global_db_obj_t* a_objs, size_t a_objs_coun
         int l_res = dap_chain_global_db_driver_add(l_store_data, a_objs_count);
         unlock();
         if(!l_res) {
-            for(size_t i = 0; i < a_objs_count; i++) {
-                history_group_item_t * l_history_group_item = NULL;
-                dap_store_obj_t *l_obj = l_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('a', l_store_data, 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, 'a',
-                                    l_group_prefix, l_obj->group, l_obj->key,
-                                    l_obj->value, l_obj->value_len);
-                        } else {
-                            break;
-                        }
-                    }
-                }
-                DAP_DELETE(l_group_prefix);
-            }
-
-        }
-        DAP_DELETE(l_store_data); //dap_store_obj_free(store_data, a_objs_count);
-        if(!l_res) {
-            return true;
+            dap_global_db_obj_track_history(l_store_data, a_objs_count);
         }
+        DAP_DELETE(l_store_data);
+        return !l_res;
     }
     return false;
 }
@@ -872,7 +762,6 @@ time_t dap_db_log_unpack_get_timestamp(uint8_t *a_data, size_t a_data_size)
  */
 char* dap_db_log_get_diff(size_t *a_data_size_out)
 {
-    //DapList *l_group_list = dap_list_append(l_group_list,GROUP_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);
     // make keys & val vector
diff --git a/modules/global-db/dap_chain_global_db_hist.c b/modules/global-db/dap_chain_global_db_hist.c
index 2017b32e00..b061db898c 100644
--- a/modules/global-db/dap_chain_global_db_hist.c
+++ b/modules/global-db/dap_chain_global_db_hist.c
@@ -1289,8 +1289,6 @@ dap_db_log_list_t* dap_db_log_list_start(uint64_t first_id, dap_list_t *a_add_gr
         dap_list_free_full(l_groups, (dap_callback_destroyed_t) free);
         l_add_groups_mask = dap_list_next(l_add_groups_mask);
     }
-    if(l_add_groups_num == 0)
-        return NULL;
 
     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
diff --git a/modules/global-db/include/dap_chain_global_db.h b/modules/global-db/include/dap_chain_global_db.h
index 807ca35db6..cbaf1b29ca 100644
--- a/modules/global-db/include/dap_chain_global_db.h
+++ b/modules/global-db/include/dap_chain_global_db.h
@@ -65,6 +65,7 @@ void dap_chain_global_db_add_history_callback_notify(const char * 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);
 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);
 /**
  * Get entry from base
  */
diff --git a/modules/net/dap_chain_net.c b/modules/net/dap_chain_net.c
index 7eefcf8b8d..0f08f63f2f 100644
--- a/modules/net/dap_chain_net.c
+++ b/modules/net/dap_chain_net.c
@@ -296,6 +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);
 #ifndef _WIN32
     pthread_cond_signal( &PVT(a_net)->state_proc_cond );
 #else
@@ -2350,7 +2351,7 @@ dap_list_t* dap_chain_net_get_node_list(dap_chain_net_t * l_net)
  * @param a_net
  * @param a_flag_sync_from_zero
  */
-void dap_chain_net_set_flag_sync_from_zero( dap_chain_net_t * a_net, bool a_flag_sync_from_zero)
+void dap_chain_net_set_flag_sync_from_zero(dap_chain_net_t * a_net, bool a_flag_sync_from_zero)
 {
     if( a_flag_sync_from_zero)
         PVT(a_net)->flags |= F_DAP_CHAIN_NET_SYNC_FROM_ZERO;
diff --git a/modules/net/include/dap_chain_net.h b/modules/net/include/dap_chain_net.h
index 7f59409edf..77f3bc52c6 100644
--- a/modules/net/include/dap_chain_net.h
+++ b/modules/net/include/dap_chain_net.h
@@ -115,7 +115,7 @@ static inline const char * dap_chain_net_state_to_str(dap_chain_net_state_t a_st
 
 void dap_chain_net_delete( dap_chain_net_t * a_net);
 void dap_chain_net_proc_mempool (dap_chain_net_t * a_net);
-void dap_chain_net_set_flag_sync_from_zero( dap_chain_net_t * a_net, bool a_flag_sync_from_zero);
+void dap_chain_net_set_flag_sync_from_zero(dap_chain_net_t * a_net, bool a_flag_sync_from_zero);
 bool dap_chain_net_get_flag_sync_from_zero( dap_chain_net_t * a_net);
 
 
-- 
GitLab