From e307af7c31f65ca15360a8d9496f066d4a49617e Mon Sep 17 00:00:00 2001
From: Aleksey Feoktistov <aleksey@synestproject.com>
Date: Sat, 29 Jan 2022 17:45:31 +0500
Subject: [PATCH] [+] add dag-poa auto confirmation

---
 .../consensus/dag-poa/dap_chain_cs_dag_poa.c  | 282 ++++++++++++++----
 modules/type/dag/dap_chain_cs_dag.c           |  51 ++--
 modules/type/dag/dap_chain_cs_dag_event.c     |  23 +-
 modules/type/dag/include/dap_chain_cs_dag.h   |   4 +
 .../type/dag/include/dap_chain_cs_dag_event.h |   8 +-
 5 files changed, 277 insertions(+), 91 deletions(-)

diff --git a/modules/consensus/dag-poa/dap_chain_cs_dag_poa.c b/modules/consensus/dag-poa/dap_chain_cs_dag_poa.c
index 467594e3d8..236f44c1fb 100644
--- a/modules/consensus/dag-poa/dap_chain_cs_dag_poa.c
+++ b/modules/consensus/dag-poa/dap_chain_cs_dag_poa.c
@@ -61,6 +61,9 @@ typedef struct dap_chain_cs_dag_poa_pvt
     uint16_t auth_certs_count;
     uint16_t auth_certs_count_verify; // Number of signatures, needed for event verification
     uint32_t confirmations_timeout; // wait signs over min value (auth_certs_count_verify)
+    bool auto_confirmation;
+    bool auto_round_complete;
+    uint32_t wait_sync_before_complete;
     uint8_t padding[4];
     dap_chain_callback_new_cfg_t prev_callback_created; // global network config init
 } dap_chain_cs_dag_poa_pvt_t;
@@ -81,6 +84,14 @@ static int s_callback_event_verify(dap_chain_cs_dag_t * a_dag, dap_chain_cs_dag_
 static dap_chain_cs_dag_event_t * s_callback_event_create(dap_chain_cs_dag_t * a_dag, dap_chain_datum_t * a_datum,
                                                           dap_chain_hash_fast_t * a_hashes, size_t a_hashes_count, size_t* a_event_size);
 static bool s_callback_round_event_to_chain(dap_chain_cs_dag_poa_callback_timer_arg_t * a_callback_arg);
+static int s_callback_event_round_sync(dap_chain_cs_dag_t * a_dag, const char a_op_code, const char *a_group,
+                                        const char *a_key, const void *a_value, const size_t a_value_size);
+static bool s_round_event_ready_minimum_check(dap_chain_cs_dag_t * a_dag, dap_chain_cs_dag_event_t * a_event, 
+                                            size_t a_event_size, char * a_event_hash_hex_str, 
+                                            dap_chain_cs_dag_event_round_cfg_t * a_event_round_cfg);
+static void s_round_event_cs_done(dap_chain_cs_dag_t * a_dag, dap_chain_cs_dag_event_t * a_event,
+                                    char * a_event_hash_hex_str, dap_chain_cs_dag_event_round_cfg_t * a_event_round_cfg);
+static void s_round_event_clean_dup(dap_chain_cs_dag_t * a_dag, const char *a_event_hash_hex_str);
 
 // CLI commands
 static int s_cli_dag_poa(int argc, char ** argv, char **str_reply);
@@ -209,73 +220,35 @@ static int s_cli_dag_poa(int argc, char ** argv, char **a_str_reply)
                     char * l_event_new_hash_base58_str = dap_enc_base58_encode_hash_to_str(&l_event_new_hash);
                     //char * l_event_new_hash_base58_str = dap_enc_base58_from_hex_str_to_str(l_event_new_hash_hex_str);
 
-                    bool l_event_is_ready = false;
-                    if ( l_event_new->header.signs_count >= l_event_round_cfg.confirmations_minimum ) {
-                        log_it(L_NOTICE,"Event %s minimum confirmations completed", l_event_new_hash_hex_str);
-                        int l_ret_event_verify;
-                        l_dag->callback_cs_set_event_round_cfg(l_dag, &l_event_round_cfg);
-                        if ( ( l_ret_event_verify = l_dag->callback_cs_verify(l_dag, l_event_new, l_event_size_new)) == 0 ) {
-                            log_it(L_NOTICE,"Event %s verification passed", l_event_new_hash_hex_str);
-
-                            if (l_event_round_cfg.ts_confirmations_minimum_completed == (uint64_t)0) {
-                                l_event_round_cfg.ts_confirmations_minimum_completed = (uint64_t)time(NULL);
-                            }
-                            l_event_is_ready = true;
-                        } else {
-                            log_it(L_NOTICE,"Error! Event %s is not passing consensus verification, ret code %d\n",
-                                l_event_new_hash_hex_str, l_ret_event_verify);
-                        }
-                    }
+                    bool l_event_is_ready = s_round_event_ready_minimum_check(l_dag, l_event_new, l_event_size_new,
+                                                                        l_event_new_hash_hex_str, &l_event_round_cfg);
 
                     if (dap_chain_cs_dag_event_gdb_set(l_event_new_hash_hex_str, l_event_new,
                                                         l_event_size_new, l_gdb_group_events, &l_event_round_cfg) ){
-                        if ( dap_chain_global_db_gr_del(dap_strdup(l_event_hash_hex_str), l_gdb_group_events) ) { // Delete old event
-                            if(!dap_strcmp(l_hash_out_type, "hex")) {
-                                dap_chain_node_cli_set_reply_text(a_str_reply,
-                                        "Added new sign with cert \"%s\", event %s placed back in round.new\n",
-                                        l_poa_pvt->events_sign_cert->name, l_event_new_hash_hex_str);
-                            }
-                            else {
-                                dap_chain_node_cli_set_reply_text(a_str_reply,
-                                        "Added new sign with cert \"%s\", event %s placed back in round.new\n",
-                                        l_poa_pvt->events_sign_cert->name, l_event_new_hash_base58_str);
-                            }
-                            ret = 0;
-                            // dap_chain_net_sync_gdb(l_chain_net); // Propagate changes in pool
-
-                            if (l_event_is_ready) { // minimum signs & verify passed
-                                dap_chain_cs_dag_poa_callback_timer_arg_t * l_callback_arg = DAP_NEW_Z(dap_chain_cs_dag_poa_callback_timer_arg_t);
-                                l_callback_arg->dag = l_dag;
-                                l_callback_arg->l_event_hash_hex_str = dap_strdup(l_event_new_hash_hex_str);
-                                memcpy(&l_callback_arg->event_round_cfg, &l_event_round_cfg, sizeof(dap_chain_cs_dag_event_round_cfg_t));
-                                uint32_t l_timeout = l_event_round_cfg.confirmations_timeout;
-
-                                if ( l_event_new->header.signs_count >= l_poa_pvt->auth_certs_count) {
-                                    s_callback_round_event_to_chain(l_callback_arg); // placement in chain now if max signs
-                                }
-                                else if ( l_timeout > ((uint64_t)time(NULL) - l_event_round_cfg.ts_confirmations_minimum_completed) ) {
-                                    l_timeout = l_timeout - ((uint64_t)time(NULL) - l_event_round_cfg.ts_confirmations_minimum_completed);
-                                    // placement in chain by timer
-                                    if (dap_timerfd_start(l_timeout*1000, 
-                                                        (dap_timerfd_callback_t)s_callback_round_event_to_chain, 
-                                                        l_callback_arg) == NULL) {
-                                        log_it(L_ERROR,"Can't run timer for Event %s", l_event_new_hash_hex_str);
-                                    } else {
-                                        log_it(L_NOTICE,"Run timer %dsec. for Event %s", l_timeout, l_event_new_hash_hex_str);
-                                    }
-                                } else { // placement in chain now if timer out
-                                    s_callback_round_event_to_chain(l_callback_arg);
-                                }
-                            }
-
-                        }else {
+                        if ( !dap_chain_global_db_gr_del(dap_strdup(l_event_hash_hex_str), l_gdb_group_events) ) { // Delete old event
                             ret = 1;
                             dap_chain_node_cli_set_reply_text(a_str_reply, "Added new sign with cert \"%s\", event %s placed back in round.new\n"
                                                                            "WARNING! Old event %s with same datum is still in round.new, produced DUP!\n",
                                                                            l_poa_pvt->events_sign_cert->name ,l_event_new_hash_hex_str, l_event_hash_str);
                         }
-                    }else {
 
+                        if(!dap_strcmp(l_hash_out_type, "hex")) {
+                            dap_chain_node_cli_set_reply_text(a_str_reply,
+                                    "Added new sign with cert \"%s\", event %s placed back in round.new\n",
+                                    l_poa_pvt->events_sign_cert->name, l_event_new_hash_hex_str);
+                        }
+                        else {
+                            dap_chain_node_cli_set_reply_text(a_str_reply,
+                                    "Added new sign with cert \"%s\", event %s placed back in round.new\n",
+                                    l_poa_pvt->events_sign_cert->name, l_event_new_hash_base58_str);
+                        }
+                        ret = 0;
+                        // dap_chain_net_sync_gdb(l_chain_net); // Propagate changes in pool
+                        if (l_event_is_ready && l_poa_pvt->auto_round_complete) { // cs done (minimum signs & verify passed)
+                            s_round_event_cs_done(l_dag, l_event_new, l_event_new_hash_hex_str, &l_event_round_cfg);
+                        }
+
+                    }else {
                         if(!dap_strcmp(l_hash_out_type, "hex")) {
                             dap_chain_node_cli_set_reply_text(a_str_reply,
                                     "GDB Error: Can't place event %s with new sign back in round.new\n",
@@ -289,6 +262,7 @@ static int s_cli_dag_poa(int argc, char ** argv, char **a_str_reply)
                         ret=-31;
 
                     }
+                    DAP_DELETE(l_event_new);
                     DAP_DELETE(l_event_new_hash_hex_str);
                     DAP_DELETE(l_event_new_hash_base58_str);
                 } else {
@@ -297,11 +271,11 @@ static int s_cli_dag_poa(int argc, char ** argv, char **a_str_reply)
                                                   l_event_hash_str);
                     ret=-1;              
                 }
-                DAP_DELETE(l_event_new);
+                //DAP_DELETE(l_event_new);
             }
             // DAP_DELETE( l_gdb_group_events );
             // DAP_DELETE(l_event_round_cfg);
-            // DAP_DELETE(l_event);
+            DAP_DELETE(l_event);
         } else {
             dap_chain_node_cli_set_reply_text(a_str_reply, "Command dag_poa requires subcommand 'sign'");
         }
@@ -360,6 +334,141 @@ static int s_callback_new(dap_chain_t * a_chain, dap_config_t * a_chain_cfg)
     return 0;
 }
 
+typedef struct event_clean_dup_items {
+    uint16_t signs_count;
+    uint64_t ts_update;
+    char * hash_str;
+    UT_hash_handle hh;
+} event_clean_dup_items_t;
+
+static event_clean_dup_items_t *s_event_clean_dup_items = NULL;
+
+static void s_round_event_clean_dup(dap_chain_cs_dag_t * a_dag, const char *a_event_hash_hex_str) {
+    char * l_gdb_group_events = a_dag->gdb_group_events_round_new;
+    size_t l_event_size = 0;
+    dap_chain_cs_dag_event_t * l_event;
+    dap_chain_cs_dag_event_round_cfg_t l_event_round_cfg;
+    if ( (l_event = dap_chain_cs_dag_event_gdb_get( a_event_hash_hex_str, &l_event_size,
+                                                    l_gdb_group_events, &l_event_round_cfg )) == NULL  ) {
+        return;
+    }
+
+    //char * l_event_first_hash_str = dap_chain_hash_fast_to_str_new(&l_event_round_cfg.first_event_hash);
+    size_t l_events_round_size = 0;
+    // dap_global_db_obj_t * l_events_round = dap_chain_global_db_gr_load(a_dag->gdb_group_events_round_new, &l_events_round_size );
+    dap_store_obj_t *l_events_round = dap_chain_global_db_driver_read(a_dag->gdb_group_events_round_new, NULL, &l_events_round_size);
+    uint16_t l_max_signs_count = 0;
+    //char * l_max_signs_hash;
+    for (size_t l_index = 0; l_index<l_events_round_size; l_index++) {
+        // dap_chain_cs_dag_event_t * l_event = (dap_chain_cs_dag_event_t *)
+        //         ((dap_chain_cs_dag_event_round_item_t *)l_events_round[l_index].value)->event;
+        // size_t l_event_size = ((dap_chain_cs_dag_event_round_item_t *)l_events_round[l_index].value)->event_size;
+        dap_chain_cs_dag_event_round_item_t *l_event_round_item = (dap_chain_cs_dag_event_round_item_t *)l_events_round[l_index].value;
+        dap_chain_cs_dag_event_t * l_event = (dap_chain_cs_dag_event_t *)l_event_round_item->event;
+        if ( memcmp(&l_event_round_cfg.first_event_hash, 
+                        &l_event_round_item->cfg.first_event_hash, sizeof(dap_chain_hash_fast_t)) == 0 ) {
+            event_clean_dup_items_t * l_item = DAP_NEW_Z(event_clean_dup_items_t);
+            l_item->signs_count = l_event->header.signs_count;
+            // l_item->ts_update = l_events_round[l_index].timestamp;
+            l_item->ts_update = l_event_round_item->cfg.ts_update;
+            l_item->hash_str = l_events_round[l_index].key;
+            HASH_ADD_STR(s_event_clean_dup_items, hash_str, l_item);
+            if ( l_event->header.signs_count > l_max_signs_count ) {
+                l_max_signs_count = l_event->header.signs_count;
+            }
+        }
+    }
+
+    uint64_t l_max_ts_update = 0;
+    char * l_max_ts_update_hash;
+    event_clean_dup_items_t *l_clean_item=NULL, *l_clean_tmp=NULL;
+    HASH_ITER(hh, s_event_clean_dup_items, l_clean_item, l_clean_tmp) {
+        if ( l_clean_item->signs_count < l_max_signs_count ) {
+            // delete dup by min signatures 
+            dap_chain_global_db_gr_del(dap_strdup(l_clean_item->hash_str), l_gdb_group_events);
+            HASH_DEL(s_event_clean_dup_items, l_clean_item);
+            DAP_DELETE(l_clean_item);
+        } else if ( l_clean_item->ts_update > l_max_ts_update ) {
+            l_max_ts_update = l_clean_item->ts_update;
+            l_max_ts_update_hash = l_clean_item->hash_str;
+        }
+    }
+    HASH_ITER(hh, s_event_clean_dup_items, l_clean_item, l_clean_tmp) {
+        if ( dap_strcmp(l_max_ts_update_hash, l_clean_item->hash_str) != 0 ) {
+            // delete dup by older
+            dap_chain_global_db_gr_del(dap_strdup(l_clean_item->hash_str), l_gdb_group_events);
+        }
+        HASH_DEL(s_event_clean_dup_items, l_clean_item);
+        DAP_DELETE(l_clean_item);
+    }
+    //HASH_CLEAR(hh, s_event_clean_dup_items);
+    dap_store_obj_free(l_events_round, l_events_round_size);
+}
+
+static bool s_round_event_ready_minimum_check(dap_chain_cs_dag_t * a_dag, dap_chain_cs_dag_event_t * a_event, 
+                                            size_t a_event_size, char * a_event_hash_hex_str,
+                                            dap_chain_cs_dag_event_round_cfg_t * a_event_round_cfg) {
+    if ( a_event->header.signs_count < a_event_round_cfg->confirmations_minimum ) {
+        return false;
+    }
+    a_dag->callback_cs_set_event_round_cfg(a_dag, a_event_round_cfg);
+    int l_ret_event_verify = a_dag->callback_cs_verify(a_dag, a_event, a_event_size);
+    if ( l_ret_event_verify == 0 ) {
+        if (a_event_round_cfg->ts_confirmations_minimum_completed == (uint64_t)0) {
+            a_event_round_cfg->ts_confirmations_minimum_completed = (uint64_t)time(NULL);
+        }
+        return true;
+    }
+    log_it(L_ERROR,"Round auto-complete error! Event %s is not passing consensus verification, ret code %d\n",
+                          a_event_hash_hex_str, l_ret_event_verify );
+    return false;
+}
+
+static void s_round_event_cs_done(dap_chain_cs_dag_t * a_dag, dap_chain_cs_dag_event_t * a_event,
+                                    char * a_event_hash_hex_str, dap_chain_cs_dag_event_round_cfg_t * a_event_round_cfg) {
+    dap_chain_cs_dag_poa_t * l_poa = DAP_CHAIN_CS_DAG_POA( a_dag );
+    dap_chain_cs_dag_poa_callback_timer_arg_t * l_callback_arg = DAP_NEW_Z(dap_chain_cs_dag_poa_callback_timer_arg_t);
+    l_callback_arg->dag = a_dag;
+    l_callback_arg->l_event_hash_hex_str = dap_strdup(a_event_hash_hex_str);
+    memcpy(&l_callback_arg->event_round_cfg, a_event_round_cfg, sizeof(dap_chain_cs_dag_event_round_cfg_t));
+    uint32_t l_timeout = a_event_round_cfg->confirmations_timeout;
+
+    if (a_event_round_cfg->ts_confirmations_minimum_completed == (uint64_t)0) {
+        a_event_round_cfg->ts_confirmations_minimum_completed = (uint64_t)time(NULL);
+    }
+
+    if ( a_event->header.signs_count >= PVT(l_poa)->auth_certs_count) {
+        // placement in chain now if max signs
+        if (dap_timerfd_start(PVT(l_poa)->wait_sync_before_complete*1000, 
+                            (dap_timerfd_callback_t)s_callback_round_event_to_chain, 
+                            l_callback_arg) == NULL) {
+            log_it(L_ERROR,"Can't run timer for Event %s", a_event_hash_hex_str);
+        } else {
+            log_it(L_NOTICE,"Run timer %dsec. for Event %s", PVT(l_poa)->wait_sync_before_complete, a_event_hash_hex_str);
+        }
+    }
+    else if ( l_timeout > ((uint64_t)time(NULL) - a_event_round_cfg->ts_confirmations_minimum_completed) ) {
+        l_timeout = l_timeout - ((uint64_t)time(NULL) - a_event_round_cfg->ts_confirmations_minimum_completed);
+        // placement in chain by timer
+        l_timeout += PVT(l_poa)->wait_sync_before_complete;
+        if (dap_timerfd_start(l_timeout*1000, 
+                            (dap_timerfd_callback_t)s_callback_round_event_to_chain, 
+                            l_callback_arg) == NULL) {
+            log_it(L_ERROR,"Can't run timer for Event %s", a_event_hash_hex_str);
+        } else {
+            log_it(L_NOTICE,"Run timer %dsec. for Event %s", l_timeout, a_event_hash_hex_str);
+        }
+    } else { // placement in chain now if timer out
+        if (dap_timerfd_start(PVT(l_poa)->wait_sync_before_complete*1000, 
+                            (dap_timerfd_callback_t)s_callback_round_event_to_chain, 
+                            l_callback_arg) == NULL) {
+            log_it(L_ERROR,"Can't run timer for Event %s", a_event_hash_hex_str);
+        } else {
+            log_it(L_NOTICE,"Run timer %dsec. for Event %s", PVT(l_poa)->wait_sync_before_complete, a_event_hash_hex_str);
+        }
+    }
+}
+
 static void s_callback_get_round_cfg(dap_chain_cs_dag_t * a_dag, dap_chain_cs_dag_event_round_cfg_t * a_event_round_cfg) {
     dap_chain_cs_dag_poa_t * l_poa = DAP_CHAIN_CS_DAG_POA(a_dag);
     dap_chain_cs_dag_poa_pvt_t * l_poa_pvt = PVT (l_poa);
@@ -368,7 +477,6 @@ static void s_callback_get_round_cfg(dap_chain_cs_dag_t * a_dag, dap_chain_cs_da
     a_event_round_cfg->ts_confirmations_minimum_completed = 0;
 }
 
-
 static bool s_callback_round_event_to_chain(dap_chain_cs_dag_poa_callback_timer_arg_t * a_callback_arg) {
     dap_chain_cs_dag_t * l_dag = a_callback_arg->dag;
     dap_chain_net_t *l_net = dap_chain_net_by_id(l_dag->chain->net_id);
@@ -489,6 +597,57 @@ static dap_chain_cs_dag_event_t * s_callback_event_create(dap_chain_cs_dag_t * a
         return NULL;
 }
 
+static int s_callback_event_round_sync(dap_chain_cs_dag_t * a_dag, const char a_op_code, const char *a_group,
+                                        const char *a_key, const void *a_value, const size_t a_value_size)
+{
+
+    dap_chain_net_t *l_net = dap_chain_net_by_id( a_dag->chain->net_id);
+
+    if ( a_value == NULL || a_op_code != 'a' ) {
+        return 0;
+    }
+
+    dap_chain_cs_dag_poa_t * l_poa = DAP_CHAIN_CS_DAG_POA(a_dag);
+    if ( !PVT(l_poa)->auto_confirmation ) {
+        s_round_event_clean_dup(a_dag, a_key); // Delete dup for manual mode
+        return 0;
+    }
+    dap_chain_cs_dag_event_round_item_t *l_event_round_item = (dap_chain_cs_dag_event_round_item_t *)a_value;
+    size_t l_event_size = l_event_round_item->event_size;
+    dap_chain_cs_dag_event_t * l_event = (dap_chain_cs_dag_event_t *)l_event_round_item->event;
+    // dap_chain_cs_dag_event_t * l_event = (dap_chain_cs_dag_event_t *)DAP_DUP_SIZE(l_event_round_item->event, l_event_size);
+    size_t l_event_size_new = 0;
+    dap_chain_cs_dag_event_t *l_event_new = dap_chain_cs_dag_event_copy_with_sign_add(l_event, l_event_size, 
+                                            &l_event_size_new, l_net,
+                                            PVT(l_poa)->events_sign_cert->enc_key);
+    
+    if ( l_event_new ) {
+        char * l_gdb_group_events = a_dag->gdb_group_events_round_new;
+        dap_chain_hash_fast_t l_event_new_hash;
+        dap_chain_cs_dag_event_calc_hash(l_event_new, l_event_size_new, &l_event_new_hash);
+        char * l_event_new_hash_hex_str = dap_chain_hash_fast_to_str_new(&l_event_new_hash);
+        bool l_event_is_ready = s_round_event_ready_minimum_check(a_dag, l_event_new, l_event_size_new, 
+                                                                    l_event_new_hash_hex_str,  &l_event_round_item->cfg);
+        if (dap_chain_cs_dag_event_gdb_set(l_event_new_hash_hex_str, l_event_new,
+                                            l_event_size_new, l_gdb_group_events, &l_event_round_item->cfg) ){
+            dap_chain_global_db_gr_del(dap_strdup(a_key), l_gdb_group_events); // Delete old event
+            if (l_event_is_ready && PVT(l_poa)->auto_round_complete) { // cs done (minimum signs & verify passed)
+                s_round_event_cs_done(a_dag, l_event_new, l_event_new_hash_hex_str, &l_event_round_item->cfg);
+            }
+        }
+        s_round_event_clean_dup(a_dag, l_event_new_hash_hex_str); // Delete dup
+        DAP_DELETE(l_event_new);
+    } else {
+        // if (PVT(l_poa)->auto_round_complete) {
+        //     if (s_round_event_ready_minimum_check(a_dag, l_event, l_event_size, &l_event_round_item->cfg)){
+        //         s_round_event_cs_done(a_dag, l_event, a_key, &l_event_round_item->cfg);
+        //     }
+        // }
+        s_round_event_clean_dup(a_dag, a_key); // Delete dup
+    }
+
+    return 0;
+}
 
 /**
  * @brief 
@@ -510,7 +669,6 @@ static int s_callback_event_verify(dap_chain_cs_dag_t * a_dag, dap_chain_cs_dag_
     uint16_t l_certs_count_verify = a_dag->use_event_round_cfg ? a_dag->event_round_cfg.confirmations_minimum
                                                                 : l_poa_pvt->auth_certs_count_verify;
     a_dag->use_event_round_cfg = false;
-
     // if ( a_dag_event->header.signs_count >= l_poa_pvt->auth_certs_count_verify ){
     if ( a_dag_event->header.signs_count >= l_certs_count_verify ){
         size_t l_verified = 0;
diff --git a/modules/type/dag/dap_chain_cs_dag.c b/modules/type/dag/dap_chain_cs_dag.c
index cfcf793260..767270a9ee 100644
--- a/modules/type/dag/dap_chain_cs_dag.c
+++ b/modules/type/dag/dap_chain_cs_dag.c
@@ -163,15 +163,17 @@ void dap_chain_cs_dag_deinit(void)
 
 }
 
-static void s_history_callback_notify(void * a_arg, const char a_op_code, const char * a_group,
+static void s_history_callback_round_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){
         dap_chain_cs_dag_t * l_dag = (dap_chain_cs_dag_t *) a_arg;
         dap_chain_net_t *l_net = dap_chain_net_by_id( l_dag->chain->net_id);
-        log_it(L_DEBUG,"%s.%s: op_code='%c' group=\"%s\" key=\"%s\" value_size=%zu",l_net->pub.name,
-               l_dag->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_group, a_key, a_value, a_value_size);
+        log_it(L_DEBUG,"%s.%s: op_code='%c' group=\"%s\" key=\"%s\" value_size=%zu",
+            l_net->pub.name, l_dag->chain->name, a_op_code, a_group, a_key, a_value_size);
+        if (l_dag->callback_cs_event_round_sync) {
+            l_dag->callback_cs_event_round_sync(l_dag, 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);
     }
 }
@@ -251,19 +253,22 @@ int dap_chain_cs_dag_new(dap_chain_t * a_chain, dap_config_t * a_chain_cfg)
     // l_dag->gdb_group_events_round_new = dap_strdup( dap_config_get_item_str_default(a_chain_cfg,"dag","gdb_group_events_round_new",
     //                                                                     "events.round.new"));
 
-    char * l_round_new_str = dap_strdup( dap_config_get_item_str_default(a_chain_cfg,"dag","gdb_group_events_round_new", "events.round.new"));
+    char * l_round_new_str = dap_strdup( dap_config_get_item_str_default(a_chain_cfg,"dag","gdb_group_events_round_new", "new"));
     dap_chain_net_t *l_net = dap_chain_net_by_id(a_chain->net_id);
-    //if(!l_dag->is_celled){
-    l_dag->gdb_group_events_round_new = dap_strdup_printf( "round-gdb.%s.chain-%016llX.%s",l_net->pub.name,
-                                                  a_chain->id.uint64, l_round_new_str);
-    // }else {
-    //     l_dag->gdb_group_events_round_new = dap_strdup_printf( "round-gdb.%s.chain-%016llX.cell-%016llX.%s",l_net->pub.name,
-    //                                               a_chain->id.uint64, l_net->pub.cell_id.uint64, l_round_new_str);
-    // }
+    if(!l_dag->is_celled){
+        //char * gdb_group = dap_strdup_printf( "%016llx-%016llx-round", l_net->pub.id.uint64, a_chain->id.uint64);
+        char * gdb_group = dap_strdup_printf( "%s-%s-round", l_net->pub.name, a_chain->name);
+        l_dag->gdb_group_events_round_new = dap_strdup_printf( "%s.%s", gdb_group, l_round_new_str);
+        dap_chain_global_db_add_sync_group(gdb_group, s_history_callback_round_notify, l_dag);
+    }else {
+        //char * gdb_group = dap_strdup_printf( "%016llx-%016llx-%016llx-round", l_net->pub.id.uint64, a_chain->id.uint64, l_net->pub.cell_id.uint64);
+        char * gdb_group = dap_strdup_printf( "%s-%s-%016llx-round", l_net->pub.name, a_chain->name, a_chain->cells->id.uint64);
+        l_dag->gdb_group_events_round_new = dap_strdup_printf( "%s.%s", gdb_group, l_round_new_str);
+        dap_chain_global_db_add_sync_group(gdb_group, s_history_callback_round_notify, l_dag);
+    }
+ 
     DAP_DELETE(l_round_new_str);
 
-    dap_chain_global_db_add_sync_group("round-gdb", s_history_callback_notify, l_dag);
-
     if ( l_dag->is_single_line ) {
         log_it (L_NOTICE, "DAG chain initialized (single line)");
     } else {
@@ -1671,10 +1676,22 @@ static int s_cli_dag(int argc, char ** argv, char **a_str_reply)
                     dap_string_append_printf(l_str_tmp,"\nEvent %s:\n", l_event_hash_str);
 
                     // Round cfg
-                    if ( strcmp(l_from_events_str,"round.new") == 0 )
+                    if ( strcmp(l_from_events_str,"round.new") == 0 ){
+                        dap_string_append_printf(l_str_tmp,
+                            "\t\tRound cfg:\n\t\t\t\tconfirmations_minimum: %d\n\t\t\t\tconfirmations_timeout: %d\n", 
+                            l_event_round_cfg.confirmations_minimum,
+                            l_event_round_cfg.confirmations_timeout);
+                        char * l_hash_str = dap_chain_hash_fast_to_str_new(&l_event_round_cfg.first_event_hash);
+                        dap_string_append_printf(l_str_tmp, "\t\t\t\tfirst_event_hash: %s\n", l_hash_str);
+                        DAP_DELETE(l_hash_str);
                         dap_string_append_printf(l_str_tmp,
-                            "\t\tRound cfg:\n\t\t\t\tconfirmations_minimum:%d\n\t\t\t\tconfirmations_timeout:%d\n", 
-                            l_event_round_cfg.confirmations_minimum, l_event_round_cfg.confirmations_timeout);
+                            "\t\t\t\tts_update: %s", 
+                            dap_ctime_r((time_t *)&l_event_round_cfg.ts_update, buf) );
+                        if (l_event_round_cfg.ts_confirmations_minimum_completed != 0)
+                            dap_string_append_printf(l_str_tmp,
+                                "\t\t\t\tts_confirmations_minimum_completed: %s", 
+                                dap_ctime_r((time_t *)&l_event_round_cfg.ts_confirmations_minimum_completed, buf) );
+                    }
 
                      // Header
                     dap_string_append_printf(l_str_tmp,"\t\tHeader:\n");
diff --git a/modules/type/dag/dap_chain_cs_dag_event.c b/modules/type/dag/dap_chain_cs_dag_event.c
index b8f1eae97d..9abd79deb0 100644
--- a/modules/type/dag/dap_chain_cs_dag_event.c
+++ b/modules/type/dag/dap_chain_cs_dag_event.c
@@ -111,7 +111,7 @@ dap_chain_cs_dag_event_t * dap_chain_cs_dag_event_copy(dap_chain_cs_dag_event_t
  */
 dap_chain_cs_dag_event_t * dap_chain_cs_dag_event_copy_with_sign_add( dap_chain_cs_dag_event_t * a_event, size_t a_event_size,
                                                             size_t * a_event_size_new,
-                                                            dap_chain_net_t * l_net, dap_enc_key_t * l_key)
+                                                            dap_chain_net_t * a_net, dap_enc_key_t * a_key)
 {
     size_t l_hashes_size = a_event->header.hash_count*sizeof(dap_chain_hash_fast_t);
     dap_chain_datum_t * l_datum = (dap_chain_datum_t*)(a_event->hashes_n_datum_n_signs + l_hashes_size);
@@ -120,12 +120,12 @@ dap_chain_cs_dag_event_t * dap_chain_cs_dag_event_copy_with_sign_add( dap_chain_
     // size_t l_event_size_excl_sign = dap_chain_cs_dag_event_calc_size_excl_signs(a_event,a_event_size);
     size_t l_event_size = a_event_size;
     size_t l_event_signs_size = l_event_size - l_event_size_excl_sign;
-    dap_sign_t * l_sign = dap_sign_create(l_key,a_event,l_event_size_excl_sign,0);
+    dap_sign_t * l_sign = dap_sign_create(a_key,a_event,l_event_size_excl_sign,0);
     size_t l_sign_size = dap_sign_get_size(l_sign);
     dap_chain_addr_t l_addr = {0};
     dap_chain_hash_fast_t l_pkey_hash;
     dap_sign_get_pkey_hash(l_sign, &l_pkey_hash);
-    dap_chain_addr_fill(&l_addr, l_sign->header.type, &l_pkey_hash, l_net->pub.id);
+    dap_chain_addr_fill(&l_addr, l_sign->header.type, &l_pkey_hash, a_net->pub.id);
     char * l_addr_str = dap_chain_addr_to_str(&l_addr);
 
     size_t l_offset = l_hashes_size+l_datum_size;
@@ -136,20 +136,24 @@ dap_chain_cs_dag_event_t * dap_chain_cs_dag_event_copy_with_sign_add( dap_chain_
         dap_chain_addr_t l_item_addr = {0};
         dap_chain_hash_fast_t l_item_pkey_hash;
         dap_sign_get_pkey_hash(l_item_sign, &l_item_pkey_hash);
-        dap_chain_addr_fill(&l_item_addr, l_item_sign->header.type, &l_item_pkey_hash, l_net->pub.id);
+        dap_chain_addr_fill(&l_item_addr, l_item_sign->header.type, &l_item_pkey_hash, a_net->pub.id);
         // checking re-sign from one address
         if (memcmp(&l_addr, &l_item_addr, sizeof(l_item_addr)) == 0) {
-            log_it(L_WARNING, "Re-sign from addr: %s", l_addr_str);
+            log_it(L_DEBUG, "Sign from this addr exists: %s", l_addr_str);
+            DAP_DELETE(l_sign);
+            DAP_DELETE(l_addr_str);
             return NULL;
         }
         l_offset += l_sign_size;
     }
-    DAP_DELETE(l_addr_str);
-
-    dap_chain_cs_dag_event_t * l_event_new = DAP_REALLOC(a_event, l_event_size+l_sign_size);
+    // dap_chain_cs_dag_event_t * l_event_new = DAP_REALLOC(a_event, l_event_size+l_sign_size);
+    dap_chain_cs_dag_event_t * l_event_new = DAP_NEW_Z_SIZE(dap_chain_cs_dag_event_t, l_event_size+l_sign_size);
+    memcpy(l_event_new, a_event, l_event_size);
     memcpy(l_event_new->hashes_n_datum_n_signs+l_offset, l_sign, l_sign_size);
     *a_event_size_new = l_event_size+l_sign_size;
     l_event_new->header.signs_count++;
+    DAP_DELETE(l_sign);
+    DAP_DELETE(l_addr_str);
     return l_event_new;
 }
 
@@ -182,11 +186,12 @@ dap_sign_t * dap_chain_cs_dag_event_get_sign( dap_chain_cs_dag_event_t * a_event
         return NULL;
 }
 
-bool dap_chain_cs_dag_event_gdb_set(char *a_event_hash_str, dap_chain_cs_dag_event_t * a_event, uint32_t a_event_size,
+bool dap_chain_cs_dag_event_gdb_set(char *a_event_hash_str, dap_chain_cs_dag_event_t * a_event, size_t a_event_size,
                                     const char *a_group, dap_chain_cs_dag_event_round_cfg_t * a_event_round_cfg) {
     dap_chain_cs_dag_event_round_item_t * l_event_round_item = DAP_NEW_SIZE(dap_chain_cs_dag_event_round_item_t,
                                                                             sizeof(dap_chain_cs_dag_event_round_item_t)+a_event_size );
     l_event_round_item->event_size = a_event_size;
+    a_event_round_cfg->ts_update = (uint64_t)time(NULL);
     // l_event_round_item->event = DAP_DUP_SIZE(a_event, a_event_size);
     memcpy(&l_event_round_item->cfg, a_event_round_cfg, sizeof(dap_chain_cs_dag_event_round_cfg_t));
     memcpy(l_event_round_item->event, a_event, a_event_size);
diff --git a/modules/type/dag/include/dap_chain_cs_dag.h b/modules/type/dag/include/dap_chain_cs_dag.h
index 48d9cd9d15..dcfc83dd4c 100644
--- a/modules/type/dag/include/dap_chain_cs_dag.h
+++ b/modules/type/dag/include/dap_chain_cs_dag.h
@@ -39,6 +39,9 @@ typedef dap_chain_cs_dag_event_t * (*dap_chain_cs_dag_callback_event_create_t)(d
 typedef void (*dap_chain_cs_dag_callback_get_round_cfg_t)(dap_chain_cs_dag_t *, dap_chain_cs_dag_event_round_cfg_t *);
 typedef void (*dap_chain_cs_dag_callback_set_event_round_cfg_t)(dap_chain_cs_dag_t *, dap_chain_cs_dag_event_round_cfg_t *);
 
+typedef int (*dap_chain_cs_dag_callback_event_round_sync_t)(dap_chain_cs_dag_t * a_dag, const char a_op_code, const char *a_group,
+                                                const char *a_key, const void *a_value, const size_t a_value_size);
+
 typedef struct dap_chain_cs_dag_hal_item {
     dap_chain_hash_fast_t hash;
     UT_hash_handle hh;
@@ -65,6 +68,7 @@ typedef struct dap_chain_cs_dag
     dap_chain_cs_dag_callback_event_t callback_cs_verify;
     dap_chain_cs_dag_callback_get_round_cfg_t callback_cs_get_round_cfg;
     dap_chain_cs_dag_callback_set_event_round_cfg_t callback_cs_set_event_round_cfg;
+    dap_chain_cs_dag_callback_event_round_sync_t callback_cs_event_round_sync;
 
     void * _pvt;
     void * _inheritor;
diff --git a/modules/type/dag/include/dap_chain_cs_dag_event.h b/modules/type/dag/include/dap_chain_cs_dag_event.h
index 4bd7dba40d..66897d0a66 100644
--- a/modules/type/dag/include/dap_chain_cs_dag_event.h
+++ b/modules/type/dag/include/dap_chain_cs_dag_event.h
@@ -51,6 +51,8 @@ typedef struct dap_chain_cs_dag_event_round_cfg {
     uint16_t confirmations_minimum; // param auth_certs_count_verify in PoA
     uint32_t confirmations_timeout; // wait confirmations over minimum value (confirmations_minimum)
     uint64_t ts_confirmations_minimum_completed;
+    uint64_t ts_update;
+    dap_chain_hash_fast_t first_event_hash; // first event hash in round
 } DAP_ALIGN_PACKED dap_chain_cs_dag_event_round_cfg_t;
 
 typedef struct dap_chain_cs_dag_event_round_item {
@@ -80,7 +82,7 @@ dap_chain_cs_dag_event_t * dap_chain_cs_dag_event_copy(dap_chain_cs_dag_event_t
 // Important: returns new deep copy of event
 dap_chain_cs_dag_event_t * dap_chain_cs_dag_event_copy_with_sign_add( dap_chain_cs_dag_event_t * a_event, size_t a_event_size,
                                                 size_t * a_event_size_new,
-                                                dap_chain_net_t * l_net, dap_enc_key_t * l_key);
+                                                dap_chain_net_t * a_net, dap_enc_key_t * a_key);
 dap_sign_t * dap_chain_cs_dag_event_get_sign( dap_chain_cs_dag_event_t * a_event, size_t a_event_size, uint16_t a_sign_number);
 
 /**
@@ -138,11 +140,11 @@ static inline void dap_chain_cs_dag_event_calc_hash(dap_chain_cs_dag_event_t * a
     dap_hash_fast(a_event, a_event_size, a_event_hash);
 }
 
-static inline uint32_t dap_chain_cs_dag_event_round_item_get_size(dap_chain_cs_dag_event_round_item_t * a_event_round_item){
+static inline size_t dap_chain_cs_dag_event_round_item_get_size(dap_chain_cs_dag_event_round_item_t * a_event_round_item){
     return sizeof(dap_chain_cs_dag_event_round_item_t)+a_event_round_item->event_size;
 }
 
-bool dap_chain_cs_dag_event_gdb_set(char *a_event_hash_str, dap_chain_cs_dag_event_t * a_event, uint32_t a_event_size,
+bool dap_chain_cs_dag_event_gdb_set(char *a_event_hash_str, dap_chain_cs_dag_event_t * a_event, size_t a_event_size,
                                         const char *a_group, dap_chain_cs_dag_event_round_cfg_t * a_event_round_cfg);
 
 dap_chain_cs_dag_event_t* dap_chain_cs_dag_event_gdb_get(const char *a_event_hash_str, size_t *a_event_size,
-- 
GitLab