From 4cf7b626592594f02d79592a204c29e063879f6c Mon Sep 17 00:00:00 2001
From: Roman Khlopkov <roman.khlopkov@demlabs.net>
Date: Tue, 21 Feb 2023 09:53:10 +0000
Subject: [PATCH] features-7694

---
 .../chain-voting/dap_stream_ch_chain_voting.c |  18 +-
 .../include/dap_stream_ch_chain_voting.h      |  10 +-
 .../consensus/esbocs/dap_chain_cs_esbocs.c    | 456 +++++++++++-------
 .../esbocs/include/dap_chain_cs_esbocs.h      |   6 +-
 modules/type/blocks/dap_chain_cs_blocks.c     |   2 +-
 5 files changed, 318 insertions(+), 174 deletions(-)

diff --git a/modules/channel/chain-voting/dap_stream_ch_chain_voting.c b/modules/channel/chain-voting/dap_stream_ch_chain_voting.c
index ff82afac35..399f98c579 100644
--- a/modules/channel/chain-voting/dap_stream_ch_chain_voting.c
+++ b/modules/channel/chain-voting/dap_stream_ch_chain_voting.c
@@ -61,7 +61,7 @@ static bool s_callback_pkt_in_call_all(UNUSED_ARG dap_proc_thread_t *a_thread, v
     for (size_t i = 0; i < s_pkt_in_callback_count; i++) {
         struct voting_pkt_in_callback *l_callback = s_pkt_in_callback + i;
         if (l_callback->packet_in_callback) {
-            l_callback->packet_in_callback(l_callback->arg, &l_voting_pkt->hdr.sender_node_addr,
+            l_callback->packet_in_callback(l_callback->arg, &l_voting_pkt->hdr.sender_node_addr, &l_voting_pkt->hdr.receiver_node_addr,
                                            &l_voting_pkt->hdr.data_hash, l_voting_pkt->data, l_voting_pkt->hdr.data_size);
         }
     }
@@ -93,6 +93,7 @@ void dap_stream_ch_chain_voting_message_write(dap_chain_net_t *a_net, dap_chain_
                 return;
             }
             l_node_client->client->connect_on_demand = true;
+            l_node_client->client->always_reconnect = true;
 
             l_node_client_item = DAP_NEW_Z(struct voting_node_client_list);
             l_node_client_item->node_addr = *a_remote_node_addr;
@@ -153,13 +154,20 @@ static void s_stream_ch_packet_in(dap_stream_ch_t *a_ch, void *a_arg)
                                            l_voting_pkt->hdr.data_size, l_ch_chain_voting->callback_notify_arg);
 }
 
-dap_stream_ch_chain_voting_pkt_t *dap_stream_ch_chain_voting_pkt_new(uint64_t a_net_id, const void *a_data, size_t a_data_size)
+dap_stream_ch_chain_voting_pkt_t *dap_stream_ch_chain_voting_pkt_new(uint64_t a_net_id,
+                                                                     dap_chain_node_addr_t *a_sender_node_addr,
+                                                                     dap_chain_node_addr_t *a_receiver_node_addr,
+                                                                     const void *a_data, size_t a_data_size)
 {
     dap_stream_ch_chain_voting_pkt_t *l_voting_pkt = DAP_NEW_Z_SIZE(dap_stream_ch_chain_voting_pkt_t,
                                                                    sizeof(dap_stream_ch_chain_voting_pkt_t) + a_data_size);
     l_voting_pkt->hdr.data_size = a_data_size;
     l_voting_pkt->hdr.version = 1;
     l_voting_pkt->hdr.net_id.uint64 = a_net_id;
+    if (a_sender_node_addr)
+        l_voting_pkt->hdr.sender_node_addr = *a_sender_node_addr;
+    if (a_receiver_node_addr)
+        l_voting_pkt->hdr.receiver_node_addr = *a_receiver_node_addr;
     dap_hash_fast(a_data, a_data_size, &l_voting_pkt->hdr.data_hash);
     if (a_data_size && a_data)
         memcpy(l_voting_pkt->data, a_data, a_data_size);
@@ -167,9 +175,13 @@ dap_stream_ch_chain_voting_pkt_t *dap_stream_ch_chain_voting_pkt_new(uint64_t a_
 }
 
 size_t dap_stream_ch_chain_voting_pkt_write_unsafe(dap_stream_ch_t *a_ch, uint8_t a_type, uint64_t a_net_id,
+                                                   dap_chain_node_addr_t *a_sender_node_addr,
+                                                   dap_chain_node_addr_t *a_receiver_node_addr,
                                                    const void * a_data, size_t a_data_size)
 {
-    dap_stream_ch_chain_voting_pkt_t *l_voting_pkt = dap_stream_ch_chain_voting_pkt_new(a_net_id, a_data, a_data_size);
+    dap_stream_ch_chain_voting_pkt_t *l_voting_pkt = dap_stream_ch_chain_voting_pkt_new(a_net_id, a_sender_node_addr,
+                                                                                        a_receiver_node_addr, a_data,
+                                                                                        a_data_size);
     size_t l_ret  = dap_stream_ch_pkt_write_unsafe(a_ch, a_type, l_voting_pkt, sizeof(l_voting_pkt) + a_data_size);
     DAP_DELETE(l_voting_pkt);
     return l_ret;
diff --git a/modules/channel/chain-voting/include/dap_stream_ch_chain_voting.h b/modules/channel/chain-voting/include/dap_stream_ch_chain_voting.h
index 6c361312b6..530fbd064e 100644
--- a/modules/channel/chain-voting/include/dap_stream_ch_chain_voting.h
+++ b/modules/channel/chain-voting/include/dap_stream_ch_chain_voting.h
@@ -13,7 +13,8 @@
 #define DAP_STREAM_CH_CHAIN_VOTING_PKT_TYPE_DATA        0x01
 #define DAP_STREAM_CH_CHAIN_VOTING_PKT_TYPE_TEST        0x02
 
-typedef void (*dap_chain_voting_ch_callback_t)(void *a_arg, dap_chain_node_addr_t *a_addr, dap_chain_hash_fast_t *a_data_hash, uint8_t *a_data, size_t a_data_size);
+typedef void (*dap_chain_voting_ch_callback_t)(void *a_arg, dap_chain_node_addr_t *a_sender_addr, dap_chain_node_addr_t *a_receiver_addr,
+                                               dap_chain_hash_fast_t *a_data_hash, uint8_t *a_data, size_t a_data_size);
 
 typedef struct dap_stream_ch_chain_voting_pkt_hdr {
     uint8_t version;
@@ -51,9 +52,14 @@ void dap_stream_ch_chain_voting_in_callback_add(void* a_arg, dap_chain_voting_ch
 void dap_stream_ch_chain_voting_message_write(dap_chain_net_t * a_net, dap_chain_node_addr_t *a_remote_node_addr,
                                               dap_stream_ch_chain_voting_pkt_t *a_voting_pkt);
 
-dap_stream_ch_chain_voting_pkt_t *dap_stream_ch_chain_voting_pkt_new(uint64_t a_net_id, const void *a_data, size_t a_data_size);
+dap_stream_ch_chain_voting_pkt_t *dap_stream_ch_chain_voting_pkt_new(uint64_t a_net_id,
+                                                                     dap_chain_node_addr_t *a_sender_node_addr,
+                                                                     dap_chain_node_addr_t *a_receiver_node_addr,
+                                                                     const void *a_data, size_t a_data_size);
 
 size_t dap_stream_ch_chain_voting_pkt_write_unsafe(dap_stream_ch_t *a_ch, uint8_t a_type, uint64_t a_net_id,
+                                                   dap_chain_node_addr_t *a_sender_node_addr,
+                                                   dap_chain_node_addr_t *a_receiver_node_addr,
                                                    const void * a_data, size_t a_data_size);
 
 int dap_stream_ch_chain_voting_init();
diff --git a/modules/consensus/esbocs/dap_chain_cs_esbocs.c b/modules/consensus/esbocs/dap_chain_cs_esbocs.c
index b001d5c6bf..bce0e7dbba 100644
--- a/modules/consensus/esbocs/dap_chain_cs_esbocs.c
+++ b/modules/consensus/esbocs/dap_chain_cs_esbocs.c
@@ -1,3 +1,4 @@
+#include <pthread.h>
 #include "utlist.h"
 #include "dap_timerfd.h"
 #include "rand/dap_rand.h"
@@ -23,7 +24,7 @@ enum s_esbocs_session_state {
 static dap_list_t *s_validator_check(dap_chain_addr_t *a_addr, dap_list_t *a_validators);
 static void s_get_last_block_hash(dap_chain_t *a_chain, dap_chain_hash_fast_t *a_last_hash_ptr);
 static void s_session_state_change(dap_chain_esbocs_session_t *a_session, enum s_esbocs_session_state a_new_state, dap_time_t a_time);
-static void s_session_packet_in(void * a_arg, dap_chain_node_addr_t * a_sender_node_addr,
+static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_node_addr, dap_chain_node_addr_t *a_receiver_node_addr,
                                 dap_chain_hash_fast_t *a_data_hash, uint8_t *a_data, size_t a_data_size);
 static void s_session_round_clear(dap_chain_esbocs_session_t *a_session);
 static void s_session_round_new(dap_chain_esbocs_session_t *a_session);
@@ -37,8 +38,10 @@ static bool s_session_timer(void *a_arg);
 static void s_message_send(dap_chain_esbocs_session_t *a_session, uint8_t a_message_type, dap_hash_fast_t *a_block_hash,
                                     const void *a_data, size_t a_data_size, dap_list_t *a_validators);
 static void s_message_chain_add(dap_chain_esbocs_session_t * a_session,
-                                    dap_chain_esbocs_message_t * a_message,
-                                    size_t a_message_size, dap_chain_hash_fast_t *a_message_hash);
+                                dap_chain_esbocs_message_t * a_message,
+                                size_t a_message_size,
+                                dap_chain_hash_fast_t *a_message_hash,
+                                dap_chain_addr_t *a_signing_addr);
 
 static int s_callback_new(dap_chain_t *a_chain, dap_config_t *a_chain_cfg);
 static void s_callback_delete(dap_chain_cs_blocks_t *a_blocks);
@@ -46,7 +49,7 @@ static int s_callback_created(dap_chain_t *a_chain, dap_config_t *a_chain_net_cf
 static size_t s_callback_block_sign(dap_chain_cs_blocks_t *a_blocks, dap_chain_block_t **a_block_ptr, size_t a_block_size);
 static int s_callback_block_verify(dap_chain_cs_blocks_t *a_blocks, dap_chain_block_t *a_block, size_t a_block_size);
 
-DAP_STATIC_INLINE const char *s_VOTING_MSG_type_to_str(uint8_t a_type)
+DAP_STATIC_INLINE const char *s_voting_msg_type_to_str(uint8_t a_type)
 {
     switch (a_type) {
     case DAP_STREAM_CH_VOTING_MSG_TYPE_START_SYNC: return "START_SYNC";
@@ -98,20 +101,27 @@ static int s_callback_new(dap_chain_t *a_chain, dap_config_t *a_chain_cfg)
 {
     dap_chain_cs_blocks_new(a_chain, a_chain_cfg);
     dap_chain_cs_blocks_t *l_blocks = DAP_CHAIN_CS_BLOCKS(a_chain);
-    dap_chain_esbocs_t *l_esbocs = DAP_NEW_Z(dap_chain_esbocs_t);
-    l_blocks->_inheritor = l_esbocs;
     l_blocks->callback_delete = s_callback_delete;
     l_blocks->callback_block_verify = s_callback_block_verify;
     l_blocks->callback_block_sign = s_callback_block_sign;
-    l_esbocs->_pvt = DAP_NEW_Z(dap_chain_esbocs_pvt_t);
+    dap_chain_esbocs_t *l_esbocs = DAP_NEW_Z(dap_chain_esbocs_t);
+    l_esbocs->chain = a_chain;
+    l_esbocs->blocks = l_blocks;
+    dap_chain_esbocs_session_t *l_session = DAP_NEW_Z(dap_chain_esbocs_session_t);
+    l_session->chain = a_chain;
+    l_session->esbocs = l_esbocs;
+
+    l_esbocs->session = l_session;
+    l_blocks->_inheritor = l_esbocs;
 
+    l_esbocs->_pvt = DAP_NEW_Z(dap_chain_esbocs_pvt_t);
     dap_chain_esbocs_pvt_t *l_esbocs_pvt = PVT(l_esbocs);
 
     l_esbocs_pvt->debug = dap_config_get_item_bool_default(a_chain_cfg, "esbocs", "consensus_debug", false);
 
     l_esbocs_pvt->poa_mode = dap_config_get_item_bool_default(a_chain_cfg, "esbocs", "poa_mode", false);
     l_esbocs_pvt->round_start_sync_timeout = dap_config_get_item_uint16_default(a_chain_cfg, "esbocs", "round_start_sync_timeout", 15);
-    l_esbocs_pvt->new_round_delay = dap_config_get_item_uint16(a_chain_cfg, "esbocs", "new_round_delay");
+    l_esbocs_pvt->new_round_delay = dap_config_get_item_uint16_default(a_chain_cfg, "esbocs", "new_round_delay", 10);
     l_esbocs_pvt->round_attempts_max = dap_config_get_item_uint16_default(a_chain_cfg, "esbocs", "round_attempts_max", 4);
     l_esbocs_pvt->round_attempt_timeout = dap_config_get_item_uint16_default(a_chain_cfg, "esbocs", "round_attempt_timeout", 10);
 
@@ -173,16 +183,16 @@ lb_err:
     return l_ret;
 }
 
-static  void s_atom_notifier(void *a_arg, UNUSED_ARG dap_chain_t *a_chain, UNUSED_ARG dap_chain_cell_id_t a_id,
+static void s_new_atom_notifier(void *a_arg, UNUSED_ARG dap_chain_t *a_chain, UNUSED_ARG dap_chain_cell_id_t a_id,
                              UNUSED_ARG void* a_atom, UNUSED_ARG size_t a_atom_size)
 {
     dap_chain_esbocs_session_t *l_session = a_arg;
-    pthread_rwlock_wrlock(&l_session->rwlock);
+    pthread_mutex_lock(&l_session->mutex);
     dap_chain_hash_fast_t l_last_block_hash;
     s_get_last_block_hash(l_session->chain, &l_last_block_hash);
     if (!dap_hash_fast_compare(&l_last_block_hash, &l_session->cur_round.last_block_hash))
         s_session_round_new(l_session);
-    pthread_rwlock_unlock(&l_session->rwlock);
+    pthread_mutex_unlock(&l_session->mutex);
 }
 
 static int s_callback_created(dap_chain_t *a_chain, dap_config_t *a_chain_net_cfg) {
@@ -224,19 +234,22 @@ static int s_callback_created(dap_chain_t *a_chain, dap_config_t *a_chain_net_cf
             return 0;
         }
     } else {
-        if (s_validator_check(&l_my_signing_addr, l_esbocs_pvt->poa_validators)) {
+        if (!s_validator_check(&l_my_signing_addr, l_esbocs_pvt->poa_validators)) {
             log_it(L_WARNING, "Signing key is not present in PoA certs list. Switch off validator mode");
             return 0;
         }
     }
 
-    dap_chain_esbocs_session_t *l_session = DAP_NEW_Z(dap_chain_esbocs_session_t);
-    l_session->chain = a_chain;
-    l_session->esbocs = l_esbocs;
+    dap_chain_esbocs_session_t *l_session = l_esbocs->session;
     l_session->my_addr.uint64 = dap_chain_net_get_cur_addr_int(l_net);
-    pthread_rwlock_init(&l_session->rwlock, NULL);
+    l_session->my_signing_addr = l_my_signing_addr;
+    pthread_mutexattr_t l_mutex_attr;
+    pthread_mutexattr_init(&l_mutex_attr);
+    pthread_mutexattr_settype(&l_mutex_attr, PTHREAD_MUTEX_RECURSIVE);
+    pthread_mutex_init(&l_session->mutex, &l_mutex_attr);
+    pthread_mutexattr_destroy(&l_mutex_attr);
     dap_stream_ch_chain_voting_in_callback_add(l_session, s_session_packet_in);
-    dap_chain_add_callback_notify(a_chain, s_atom_notifier, l_session);
+    dap_chain_add_callback_notify(a_chain, s_new_atom_notifier, l_session);
     s_session_round_new(l_session);
 
     log_it(L_INFO, "ESBOCS: init session for net:%s, chain:%s", a_chain->net_name, a_chain->name);
@@ -252,7 +265,7 @@ static void s_callback_delete(dap_chain_cs_blocks_t *a_blocks)
 {
     dap_chain_esbocs_t *l_esbocs = DAP_CHAIN_ESBOCS(a_blocks);
     dap_chain_esbocs_session_t *l_session = l_esbocs->session;
-    pthread_rwlock_wrlock(&l_session->rwlock);
+    pthread_mutex_lock(&l_session->mutex);
     DL_DELETE(s_session_items, l_session);
     if (!s_session_items)
         dap_timerfd_delete(s_session_cs_timer);
@@ -263,7 +276,7 @@ static void s_callback_delete(dap_chain_cs_blocks_t *a_blocks)
         dap_list_free_full(l_item->messages, NULL);
         DAP_DELETE(l_item);
     }
-    pthread_rwlock_unlock(&l_session->rwlock);
+    pthread_mutex_unlock(&l_session->mutex);
     DAP_DELETE(l_session);
     if (l_esbocs->_pvt)
         DAP_DELETE(l_esbocs->_pvt);
@@ -353,42 +366,45 @@ static void s_get_last_block_hash(dap_chain_t *a_chain, dap_chain_hash_fast_t *a
     dap_chain_atom_iter_t *l_iter = a_chain->callback_atom_iter_create(a_chain, c_dap_chain_cell_id_null, false);
     dap_chain_atom_ptr_t *l_ptr_list = a_chain->callback_atom_iter_get_lasts(l_iter, NULL, NULL);
     DAP_DEL_Z(l_ptr_list);
-    *a_last_hash_ptr = *l_iter->cur_hash;
+    *a_last_hash_ptr = l_iter->cur_hash ? *l_iter->cur_hash : (dap_hash_fast_t){0};
     a_chain->callback_atom_iter_delete(l_iter);
 }
 
-static int s_addr_compare(const void *a_list_data, const void *a_user_data)
+static int s_callback_addr_compare(const void *a_list_data, const void *a_user_data)
 {
-    return dap_chain_addr_compare(&((dap_chain_esbocs_validator_t *)a_list_data)->signing_addr,
-                                  (dap_chain_addr_t *)a_user_data);
+    return memcmp(&((dap_chain_esbocs_validator_t *)a_list_data)->signing_addr,
+                  (dap_chain_addr_t *)a_user_data, sizeof(dap_chain_addr_t));
 }
 
 static dap_list_t *s_validator_check(dap_chain_addr_t *a_addr, dap_list_t *a_validators)
 {
-    return dap_list_find_custom(a_validators, a_addr, s_addr_compare);
+    return dap_list_find_custom(a_validators, a_addr, s_callback_addr_compare);
 }
 
 static void s_session_send_startsync(dap_chain_esbocs_session_t *a_session)
 {
     dap_chain_hash_fast_t l_last_block_hash;
     s_get_last_block_hash(a_session->chain, &l_last_block_hash);
-    a_session->ts_round_sync_start = dap_time_now();    
+    a_session->ts_round_sync_start = dap_time_now();
     if (!dap_hash_fast_compare(&l_last_block_hash, &a_session->cur_round.last_block_hash))
         return;     // My last block hash is different, so skip this round
     if (!s_validator_check(&a_session->my_signing_addr, a_session->cur_round.validators_list))
         return;     // I'm not a selected validator, just skip sync message
+    debug_if(PVT(a_session->esbocs)->debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U"."
+                                                   " Sent START_SYNC pkt, attempt %"DAP_UINT64_FORMAT_U,
+                    a_session->chain->net_name, a_session->chain->name, a_session->cur_round.id, a_session->cur_round.sync_attempt);
     s_message_send(a_session, DAP_STREAM_CH_VOTING_MSG_TYPE_START_SYNC, &l_last_block_hash,
-                   NULL, 0, a_session->cur_round.validators_list);
-    debug_if(PVT(a_session->esbocs)->debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U" Sent START_SYNC pkt",
-                    a_session->chain->net_name, a_session->chain->name, a_session->cur_round.id);
+                   &a_session->cur_round.sync_attempt, sizeof(uint64_t),
+                   a_session->cur_round.validators_list);
 }
 
 static bool s_session_send_startsync_on_timer(void *a_arg)
 {
     dap_chain_esbocs_session_t *l_session = a_arg;
-    pthread_rwlock_wrlock(&l_session->rwlock);
+    pthread_mutex_lock(&l_session->mutex);
     s_session_send_startsync(l_session);
-    pthread_rwlock_unlock(&l_session->rwlock);
+    l_session->sync_timer = NULL;
+    pthread_mutex_unlock(&l_session->mutex);
     return false;
 }
 
@@ -410,16 +426,24 @@ static void s_session_round_clear(dap_chain_esbocs_session_t *a_session)
 
     a_session->cur_round = (dap_chain_esbocs_round_t){
             .id = a_session->cur_round.id,
-            .last_block_hash = a_session->cur_round.last_block_hash
+            .attempt_num = 1,
+            .last_block_hash = a_session->cur_round.last_block_hash,
+            .sync_attempt = a_session->cur_round.sync_attempt
     };
-    dap_timerfd_delete(a_session->sync_timer);
 }
 
 static void s_session_round_new(dap_chain_esbocs_session_t *a_session)
 {
     s_session_round_clear(a_session);
     a_session->cur_round.id++;
+    a_session->cur_round.sync_attempt++;
+
+    dap_timerfd_delete(a_session->sync_timer);
+    a_session->sync_timer = NULL;
     a_session->state = DAP_CHAIN_ESBOCS_SESSION_STATE_WAIT_START;
+    a_session->ts_round_sync_start = 0;
+    a_session->ts_attempt_start = 0;
+
     dap_hash_fast_t *l_seed_hash = NULL;
     dap_hash_fast_t l_last_block_hash;
     s_get_last_block_hash(a_session->chain, &l_last_block_hash);
@@ -427,25 +451,26 @@ static void s_session_round_new(dap_chain_esbocs_session_t *a_session)
             !dap_hash_fast_compare(&l_last_block_hash, &a_session->cur_round.last_block_hash)) {
         l_seed_hash = &l_last_block_hash;
         a_session->cur_round.last_block_hash = l_last_block_hash;
+        a_session->cur_round.sync_attempt = 1;
     }
     a_session->cur_round.validators_list = s_get_validators_list(a_session, l_seed_hash);
 
+    bool l_round_already_started = a_session->round_fast_forward;
     if (s_validator_check(&a_session->my_signing_addr, a_session->cur_round.validators_list)) {
         //I am a current round validator
         dap_chain_esbocs_sync_item_t *l_item, *l_tmp;
-        bool l_round_already_started = false;
         HASH_FIND(hh, a_session->sync_items, &a_session->cur_round.last_block_hash, sizeof(dap_hash_fast_t), l_item);
         if (l_item) {
-            debug_if(PVT(a_session->esbocs)->debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U" already started. Process sync messages",
-                                                                a_session->chain->net_name, a_session->chain->name,
-                                                                    a_session->cur_round.id);
+            debug_if(PVT(a_session->esbocs)->debug,
+                     L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U" already started. Process sync messages",
+                                a_session->chain->net_name, a_session->chain->name, a_session->cur_round.id);
             l_round_already_started = true;
             for (dap_list_t *it = l_item->messages; it; it = it->next) {
                 dap_hash_fast_t l_msg_hash;
                 dap_chain_esbocs_message_t *l_msg = it->data;
                 size_t l_msg_size = sizeof(*l_msg) + l_msg->hdr.sign_size + l_msg->hdr.message_size;
                 dap_hash_fast(l_msg, l_msg_size, &l_msg_hash);
-                s_session_packet_in(a_session, NULL, &l_msg_hash, (uint8_t *)l_msg, l_msg_size);
+                s_session_packet_in(a_session, NULL, NULL, &l_msg_hash, (uint8_t *)l_msg, l_msg_size);
             }
         }
         HASH_ITER(hh, a_session->sync_items, l_item, l_tmp) {
@@ -453,26 +478,25 @@ static void s_session_round_new(dap_chain_esbocs_session_t *a_session)
             dap_list_free_full(l_item->messages, NULL);
             DAP_DELETE(l_item);
         }
-        if (l_round_already_started) {
-            s_session_send_startsync(a_session);
-            return;
-        }
 
-        debug_if(PVT(a_session->esbocs)->debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U" start. Syncing validators in %u seconds",
-                                                            a_session->chain->net_name, a_session->chain->name,
-                                                                a_session->cur_round.id, PVT(a_session->esbocs)->new_round_delay);
+        debug_if(PVT(a_session->esbocs)->debug, L_MSG,
+                 "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U" start. Syncing validators in %u seconds",
+                    a_session->chain->net_name, a_session->chain->name,
+                        a_session->cur_round.id, l_round_already_started ? 0 : PVT(a_session->esbocs)->new_round_delay);
     }
-    if (PVT(a_session->esbocs)->new_round_delay)
-        a_session->sync_timer = dap_timerfd_start(PVT(a_session->esbocs)->new_round_delay, s_session_send_startsync_on_timer, a_session);
+    if (PVT(a_session->esbocs)->new_round_delay && !l_round_already_started)
+        a_session->sync_timer = dap_timerfd_start(PVT(a_session->esbocs)->new_round_delay * 1000,
+                                                  s_session_send_startsync_on_timer, a_session);
     else
         s_session_send_startsync(a_session);
+    a_session->round_fast_forward = false;
 }
 
 static void s_session_attempt_new(dap_chain_esbocs_session_t *a_session)
 {
-    if (++a_session->cur_round.attempt_num > PVT(a_session->esbocs)->round_attempts_max ) {
+    if (a_session->cur_round.attempt_num++ > PVT(a_session->esbocs)->round_attempts_max ) {
         debug_if(PVT(a_session->esbocs)->debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U"."
-                                                        " Round finish by reason: attempts is out",
+                                                        " Round finished by reason: attempts is out",
                                                             a_session->chain->net_name, a_session->chain->name,
                                                                 a_session->cur_round.id);
         s_session_round_new(a_session);
@@ -482,14 +506,15 @@ static void s_session_attempt_new(dap_chain_esbocs_session_t *a_session)
         dap_chain_esbocs_validator_t *l_validator = it->data;
         if (l_validator->is_synced && !l_validator->is_chosen) {
             // We have synced validator with no submitted candidate
-            debug_if(PVT(a_session->esbocs)->debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U". Attempt:%hu is started"                                                     " Round finish by reason: attempts is out",
+            debug_if(PVT(a_session->esbocs)->debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U". Attempt:%hu is started",
                                                                 a_session->chain->net_name, a_session->chain->name,
                                                                     a_session->cur_round.id, a_session->cur_round.attempt_num);
             s_session_state_change(a_session, DAP_CHAIN_ESBOCS_SESSION_STATE_WAIT_PROC, dap_time_now());
+            return;
         }
     }
     debug_if(PVT(a_session->esbocs)->debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U"."
-                                                    " Round finish by reason: all synced validators already tryed its attempt",
+                                                    " Round finished by reason: all synced validators already tryed its attempt",
                                                         a_session->chain->net_name, a_session->chain->name,
                                                             a_session->cur_round.id);
     s_session_round_new(a_session);
@@ -627,33 +652,38 @@ static void s_session_state_change(dap_chain_esbocs_session_t *a_session, enum s
 
 static void s_session_proc_state(dap_chain_esbocs_session_t *a_session)
 {
-    if (pthread_rwlock_trywrlock(&a_session->rwlock) != 0)
+    if (pthread_mutex_trylock(&a_session->mutex) != 0)
         return; // Session is busy
     bool l_cs_debug = PVT(a_session->esbocs)->debug;
     dap_time_t l_time = dap_time_now();
     switch (a_session->state) {
-    case DAP_CHAIN_ESBOCS_SESSION_STATE_WAIT_START:
-        if (l_time - a_session->ts_round_sync_start >= PVT(a_session->esbocs)->round_start_sync_timeout) {
+    case DAP_CHAIN_ESBOCS_SESSION_STATE_WAIT_START: {
+        dap_time_t l_round_timeout = PVT(a_session->esbocs)->round_start_sync_timeout;
+        bool l_round_skip = !s_validator_check(&a_session->my_signing_addr, a_session->cur_round.validators_list);
+        if (l_round_skip)
+            l_round_timeout += PVT(a_session->esbocs)->round_attempt_timeout * PVT(a_session->esbocs)->round_attempts_max;
+        if (a_session->ts_round_sync_start && l_time - a_session->ts_round_sync_start >= l_round_timeout) {
             if (a_session->cur_round.validators_synced_count * 3 >= dap_list_length(a_session->cur_round.validators_list) * 2) {
                 a_session->cur_round.id = s_session_calc_current_round_id(a_session);
                 debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
-                                            " More than 2/3 of the validators are synchronized, wait to submit candidate",
+                                            " Minimum count of validators are synchronized, wait to submit candidate",
                                                 a_session->chain->net_name, a_session->chain->name,
                                                     a_session->cur_round.id, a_session->cur_round.attempt_num);
                 s_session_state_change(a_session, DAP_CHAIN_ESBOCS_SESSION_STATE_WAIT_PROC, l_time);
             } else { // timeout start sync
                 debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
-                                            " Round finish by reason: can't synchronize 2/3 of the validators",
+                                            " Round finished by reason: %s",
                                                 a_session->chain->net_name, a_session->chain->name,
-                                                    a_session->cur_round.id, a_session->cur_round.attempt_num);
+                                                    a_session->cur_round.id, a_session->cur_round.attempt_num,
+                                                        l_round_skip ? "skipped" : "can't synchronize minimum number of validators");
                 s_session_round_new(a_session);
             }
         }
-        break;
+    } break;
     case DAP_CHAIN_ESBOCS_SESSION_STATE_WAIT_PROC:
         if (l_time - a_session->ts_attempt_start >= PVT(a_session->esbocs)->round_attempt_timeout) {
             debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
-                                        " Attempt finish by reason: haven't cantidate submitted",
+                                        " Attempt finished by reason: haven't cantidate submitted",
                                             a_session->chain->net_name, a_session->chain->name,
                                                 a_session->cur_round.id, a_session->cur_round.attempt_num);
             s_session_attempt_new(a_session);
@@ -672,7 +702,7 @@ static void s_session_proc_state(dap_chain_esbocs_session_t *a_session)
                 if(l_cs_debug) {
                     char *l_candidate_hash_str = dap_chain_hash_fast_to_str_new(&a_session->cur_round.attempt_candidate_hash);
                     log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu"
-                                            " Candidate:%s collected sings of more than 2/3 of the validators, so to sent a PRE_COMMIT",
+                                            " Candidate:%s collected sings of minimum number of validators, so to sent PRE_COMMIT",
                                                 a_session->chain->net_name, a_session->chain->name, a_session->cur_round.id,
                                                     a_session->cur_round.attempt_num, l_candidate_hash_str);
                     DAP_DELETE(l_candidate_hash_str);
@@ -681,7 +711,7 @@ static void s_session_proc_state(dap_chain_esbocs_session_t *a_session)
                 break;
             }
             debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
-                                        " Attempt finish by reason: cant't collect 2/3 validator signs",
+                                        " Attempt finish by reason: cant't collect minimum number of validator's signs",
                                             a_session->chain->net_name, a_session->chain->name,
                                                 a_session->cur_round.id, a_session->cur_round.attempt_num);
             s_session_attempt_new(a_session);
@@ -690,7 +720,7 @@ static void s_session_proc_state(dap_chain_esbocs_session_t *a_session)
     case DAP_CHAIN_ESBOCS_SESSION_STATE_WAIT_FINISH:
         if (l_time - a_session->ts_attempt_start >= PVT(a_session->esbocs)->round_attempt_timeout) {
             debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
-                                        " Attempt finish by reason: cant't collect 2/3 validator precommits with same final hash",
+                                        " Attempt finisedh by reason: cant't collect minimum number of validator's precommits with same final hash",
                                             a_session->chain->net_name, a_session->chain->name,
                                                 a_session->cur_round.id, a_session->cur_round.attempt_num);
             s_session_attempt_new(a_session);
@@ -700,7 +730,7 @@ static void s_session_proc_state(dap_chain_esbocs_session_t *a_session)
         break;
     }
 
-    pthread_rwlock_unlock(&a_session->rwlock);
+    pthread_mutex_unlock(&a_session->mutex);
 }
 
 static bool s_session_timer(void *a_arg)
@@ -714,8 +744,10 @@ static bool s_session_timer(void *a_arg)
 }
 
 static void s_message_chain_add(dap_chain_esbocs_session_t *a_session,
-                                    dap_chain_esbocs_message_t *a_message,
-                                    size_t a_message_size, dap_chain_hash_fast_t *a_message_hash)
+                                dap_chain_esbocs_message_t *a_message,
+                                size_t a_message_size,
+                                dap_chain_hash_fast_t *a_message_hash,
+                                dap_chain_addr_t *a_signing_addr)
 {
     dap_chain_esbocs_round_t *l_round = &a_session->cur_round;
     dap_chain_esbocs_message_item_t *l_message_item = DAP_NEW_Z(dap_chain_esbocs_message_item_t);
@@ -725,6 +757,7 @@ static void s_message_chain_add(dap_chain_esbocs_session_t *a_session,
         l_message_item->message_hash = l_message_hash;
     } else
         l_message_item->message_hash = *a_message_hash;
+    l_message_item->signing_addr = *a_signing_addr;
     l_message_item->message = DAP_DUP_SIZE(a_message, a_message_size);
     HASH_ADD(hh, l_round->message_items, message_hash, sizeof(l_message_item->message_hash), l_message_item);
 }
@@ -742,16 +775,17 @@ static void s_session_candidate_submit(dap_chain_esbocs_session_t *a_session)
         dap_hash_fast(l_candidate, l_candidate_size, &l_candidate_hash);
         if (PVT(a_session->esbocs)->debug) {
             char *l_candidate_hash_str = dap_chain_hash_fast_to_str_new(&l_candidate_hash);
-            log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U" Submit my candidate:%s",
+            log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu. Submit my candidate:%s",
                     a_session->chain->net_name, a_session->chain->name,
-                        a_session->cur_round.id, l_candidate_hash_str);
+                        a_session->cur_round.id, a_session->cur_round.attempt_num, l_candidate_hash_str);
             DAP_DELETE(l_candidate_hash_str);
         }
     } else { // there is no my candidate, send null hash
         if (PVT(a_session->esbocs)->debug)
-            log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu I don't have a candidate. I submit a null candidate.",
-                        a_session->chain->net_name, a_session->chain->name,
-                            a_session->cur_round.id, a_session->cur_round.attempt_num);
+            log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                          " I don't have a candidate. I submit a null candidate.",
+                                a_session->chain->net_name, a_session->chain->name,
+                                    a_session->cur_round.id, a_session->cur_round.attempt_num);
     }
     s_message_send(a_session, DAP_STREAM_CH_VOTING_MSG_TYPE_SUBMIT, &l_candidate_hash,
                     l_candidate, l_candidate_size, a_session->cur_round.validators_list);
@@ -760,6 +794,7 @@ static void s_session_candidate_submit(dap_chain_esbocs_session_t *a_session)
 static void s_session_candidate_verify(dap_chain_esbocs_session_t *a_session, dap_chain_block_t *a_candidate,
                                        size_t a_candidate_size, dap_hash_fast_t *a_candidate_hash)
 {
+    a_session->processing_candidate = a_candidate;
     dap_chain_cs_blocks_t *l_blocks = DAP_CHAIN_CS_BLOCKS(a_session->chain);
     if (l_blocks->chain->callback_atom_verify(l_blocks->chain, a_candidate, a_candidate_size) == ATOM_ACCEPT) {
         // validation - OK, gen event Approve
@@ -784,6 +819,7 @@ static void s_session_candidate_verify(dap_chain_esbocs_session_t *a_session, da
             DAP_DELETE(l_candidate_hash_str);
         }
     }
+    a_session->processing_candidate = NULL;
 }
 
 static void s_session_candidate_to_chain(dap_chain_esbocs_session_t *a_session, dap_chain_hash_fast_t *a_candidate_hash,
@@ -864,7 +900,7 @@ static void s_session_round_finish(dap_chain_esbocs_session_t *a_session, dap_ch
     if (l_cs_debug) {
         char *l_finish_candidate_hash_str = dap_chain_hash_fast_to_str_new(&l_store->candidate_hash);
         char *l_finish_block_hash_str = dap_chain_hash_fast_to_str_new(&l_store->precommit_candidate_hash);
-        log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu My candidate:%s passed the consensus!\n"
+        log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu Candidate:%s passed the consensus!\n"
                       "Move block %s to chains",
                         a_session->chain->net_name, a_session->chain->name, a_session->cur_round.id,
                             a_session->cur_round.attempt_num, l_finish_candidate_hash_str, l_finish_block_hash_str);
@@ -874,6 +910,18 @@ static void s_session_round_finish(dap_chain_esbocs_session_t *a_session, dap_ch
     s_session_candidate_to_chain(a_session, &l_store->precommit_candidate_hash, l_store->candidate, l_store->candidate_size);
 }
 
+void s_session_sync_queue_add(dap_chain_esbocs_session_t *a_session, dap_chain_esbocs_message_t *a_message, size_t a_message_size)
+{
+    dap_chain_esbocs_sync_item_t *l_sync_item;
+    HASH_FIND(hh, a_session->sync_items, &a_message->hdr.candidate_hash, sizeof(dap_hash_fast_t), l_sync_item);
+    if (!l_sync_item) {
+        l_sync_item = DAP_NEW_Z(dap_chain_esbocs_sync_item_t);
+        l_sync_item->last_block_hash = a_message->hdr.candidate_hash;
+        HASH_ADD(hh, a_session->sync_items, last_block_hash, sizeof(dap_hash_fast_t), l_sync_item);
+    }
+    l_sync_item->messages = dap_list_append(l_sync_item->messages, DAP_DUP_SIZE(a_message, a_message_size));
+}
+
 /**
  * @brief s_session_packet_in
  * @param a_arg
@@ -882,7 +930,7 @@ static void s_session_round_finish(dap_chain_esbocs_session_t *a_session, dap_ch
  * @param a_data
  * @param a_data_size
  */
-static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_node_addr,
+static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_node_addr, dap_chain_node_addr_t *a_receiver_node_addr,
                                 dap_chain_hash_fast_t *a_data_hash, uint8_t *a_data, size_t a_data_size)
 {
     dap_chain_esbocs_session_t *l_session = a_arg;
@@ -894,12 +942,17 @@ static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_nod
     dap_sign_t *l_sign = (dap_sign_t *)(l_message_data + l_message_data_size);
     size_t l_sign_size = l_message->hdr.sign_size;
 
-    pthread_rwlock_wrlock(&l_session->rwlock);
     if (a_sender_node_addr) { //Process network message
-        debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu Receive pkt type:%x from addr:"NODE_ADDR_FP_STR", my_addr:"NODE_ADDR_FP_STR"",
-                    l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
-                        l_session->cur_round.attempt_num, l_message->hdr.type,
-                            NODE_ADDR_FP_ARGS(a_sender_node_addr), NODE_ADDR_FP_ARGS_S(l_session->my_addr));
+        pthread_mutex_lock(&l_session->mutex);
+        debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                                    " Receive pkt type:%x from addr:"NODE_ADDR_FP_STR", my_addr:"NODE_ADDR_FP_STR"",
+                                        l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
+                                            l_session->cur_round.attempt_num, l_message->hdr.type,
+                                                NODE_ADDR_FP_ARGS(a_sender_node_addr), NODE_ADDR_FP_ARGS_S(l_session->my_addr));
+        if (a_receiver_node_addr->uint64 != l_session->my_addr.uint64) {
+            debug_if(l_cs_debug, L_MSG, "ESBOCS: Wrong packet destination address");
+            goto session_unlock;
+        }
 
         if (sizeof(*l_message) + l_message->hdr.sign_size + l_message->hdr.message_size != a_data_size) {
             log_it(L_WARNING, "ESBOCS: incorrect message size in header is %zu when data size is only %zu and header size is %zu",
@@ -915,18 +968,19 @@ static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_nod
         dap_chain_hash_fast_t l_data_hash = {};
         dap_hash_fast(l_message, a_data_size, &l_data_hash);
         if (!dap_hash_fast_compare(a_data_hash, &l_data_hash)) {
-            debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu Message rejected: message hash does not match",
-                        l_session->chain->net_name, l_session->chain->name,
-                            l_session->cur_round.id, l_session->cur_round.attempt_num);
+            debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                                        " Message rejected: message hash does not match",
+                                            l_session->chain->net_name, l_session->chain->name,
+                                                l_session->cur_round.id, l_session->cur_round.attempt_num);
             goto session_unlock;
         }
 
         l_message->hdr.sign_size = 0;   // restore header on signing time
-        bool l_verify_passed = dap_sign_verify_all(l_sign, l_sign_size, l_message, l_message_data_size + sizeof(l_message->hdr)) == 1;
-        if (!l_verify_passed) {
-            debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu Message rejected from addr:"NODE_ADDR_FP_STR" not passed verification",
-                        l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
-                            l_session->cur_round.attempt_num, NODE_ADDR_FP_ARGS(a_sender_node_addr));
+        if (dap_sign_verify_all(l_sign, l_sign_size, l_message, l_message_data_size + sizeof(l_message->hdr))) {
+            debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                                        " Message rejected from addr:"NODE_ADDR_FP_STR" not passed verification",
+                                            l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
+                                                l_session->cur_round.attempt_num, NODE_ADDR_FP_ARGS(a_sender_node_addr));
             goto session_unlock;
         }
         l_message->hdr.sign_size = l_sign_size; // restore original header
@@ -934,21 +988,16 @@ static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_nod
         // consensus round start sync
         if (l_message->hdr.type == DAP_STREAM_CH_VOTING_MSG_TYPE_START_SYNC) {
             if (!dap_hash_fast_compare(&l_message->hdr.candidate_hash, &l_session->cur_round.last_block_hash)) {
-                dap_chain_esbocs_sync_item_t *l_sync_item;
-                HASH_FIND(hh, l_session->sync_items, &l_message->hdr.candidate_hash, sizeof(dap_hash_fast_t), l_sync_item);
-                if (!l_sync_item) {
-                    l_sync_item = DAP_NEW(dap_chain_esbocs_sync_item_t);
-                    l_sync_item->last_block_hash = l_message->hdr.candidate_hash;
-                }
-                l_sync_item->messages = dap_list_append(l_sync_item->messages, DAP_DUP(l_message));
+                s_session_sync_queue_add(l_session, l_message, a_data_size);
                 goto session_unlock;
             }
         } else if (l_message->hdr.round_id != l_session->cur_round.id ||
                    l_message->hdr.attempt_num < l_session->cur_round.attempt_num) {
             // round check
-            debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu Message rejected: round or attempt in message does not match",
-                        l_session->chain->net_name, l_session->chain->name,
-                            l_session->cur_round.id, l_session->cur_round.attempt_num);
+            debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                                        " Message rejected: round or attempt in message does not match",
+                                            l_session->chain->net_name, l_session->chain->name,
+                                                l_session->cur_round.id, l_session->cur_round.attempt_num);
             goto session_unlock;
         }
     }
@@ -959,9 +1008,10 @@ static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_nod
     if (l_cs_debug)
         l_validator_addr_str = dap_chain_addr_to_str(&l_signing_addr);
     if (!s_validator_check(&l_signing_addr, l_session->cur_round.validators_list)) {
-        debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu Message rejected: validator addr:%s not in the list.",
-                l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
-                    l_session->cur_round.attempt_num, l_validator_addr_str);
+        debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                                    " Message rejected: validator addr:%s not in the list.",
+                                        l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
+                                            l_session->cur_round.attempt_num, l_validator_addr_str);
         goto session_unlock;
     }
 
@@ -971,9 +1021,10 @@ static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_nod
     dap_chain_esbocs_message_item_t *l_message_item_temp = NULL;
     HASH_FIND(hh, l_round->message_items, a_data_hash, sizeof(dap_chain_hash_fast_t), l_message_item_temp);
     if (l_message_item_temp) {
-        debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu Message rejected: message hash is exists in chain (duplicate?)",
-                    l_session->chain->net_name, l_session->chain->name,
-                        l_session->cur_round.id, l_session->cur_round.attempt_num);
+        debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                                    " Message rejected: message hash is exists in chain (duplicate)",
+                                        l_session->chain->net_name, l_session->chain->name,
+                                            l_session->cur_round.id, l_session->cur_round.attempt_num);
         goto session_unlock;
     }
 
@@ -987,21 +1038,58 @@ static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_nod
                  l_message->hdr.type == DAP_STREAM_CH_VOTING_MSG_TYPE_APPROVE);
         if (l_same_type && dap_chain_addr_compare(&l_chain_message->signing_addr, &l_signing_addr) &&
                 dap_hash_fast_compare(&l_chain_message->message->hdr.candidate_hash, &l_message->hdr.candidate_hash)) {
-            debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu. Message rejected: duplicate message %s",
+            debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                                        " Message rejected: duplicate message %s",
                                             l_session->chain->net_name, l_session->chain->name,
                                                 l_session->cur_round.id, l_session->cur_round.attempt_num,
-                                                    s_VOTING_MSG_type_to_str(l_message->hdr.type));
+                                                    s_voting_msg_type_to_str(l_message->hdr.type));
             goto session_unlock;
         }
     }
 
     dap_chain_hash_fast_t *l_candidate_hash = &l_message->hdr.candidate_hash;
     switch (l_message->hdr.type) {
-    case DAP_STREAM_CH_VOTING_MSG_TYPE_START_SYNC:
-        debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu. Receive START_SYNC: from validator:%s",
-                    l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
-                        l_session->cur_round.attempt_num, l_validator_addr_str);
-        s_message_chain_add(l_session, l_message, a_data_size, a_data_hash);
+    case DAP_STREAM_CH_VOTING_MSG_TYPE_START_SYNC: {
+        uint64_t l_sync_attempt = *(uint64_t *)l_message_data;
+        debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U
+                                    " Receive START_SYNC: from validator:%s, sync attempt %"DAP_UINT64_FORMAT_U,
+                                        l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
+                                            l_validator_addr_str, l_sync_attempt);
+        if (l_sync_attempt != l_session->cur_round.sync_attempt) {
+            if (l_sync_attempt < l_session->cur_round.sync_attempt) {
+                 debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U
+                                             " SYNC message is rejected because current sync attempt %"DAP_UINT64_FORMAT_U
+                                             " is greater than meassage sync attempt %"DAP_UINT64_FORMAT_U,
+                                                l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
+                                                    l_session->cur_round.sync_attempt, l_sync_attempt);
+                 break;
+            } else {
+                uint64_t l_attempts_miss = l_sync_attempt - l_session->cur_round.sync_attempt;
+                if (l_attempts_miss > UINT16_MAX) {
+                    debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U
+                                                " SYNC message is rejected - too much sync attempt difference %"DAP_UINT64_FORMAT_U,
+                                                   l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
+                                                       l_attempts_miss);
+                    break;
+                }
+                debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U
+                                            " SYNC message sync attempt %"DAP_UINT64_FORMAT_U" is greater than"
+                                            " current round sync attempt %"DAP_UINT64_FORMAT_U" so fast-forward this round",
+                                               l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
+                                                   l_sync_attempt, l_session->cur_round.sync_attempt);
+                for (uint64_t i = 0; i < l_attempts_miss - 1; i++) {
+                    // Fast-forward current sync attempt
+                    s_get_validators_list(l_session, NULL);
+                    l_session->cur_round.sync_attempt++;
+                }
+                // Process this message in new round
+                s_session_sync_queue_add(l_session, l_message, a_data_size);
+                l_session->round_fast_forward = true;
+                s_session_round_new(l_session);
+                break;
+            }
+        }
+        s_message_chain_add(l_session, l_message, a_data_size, a_data_hash, &l_signing_addr);
         for (dap_list_t *it = l_session->cur_round.validators_list; it; it = it->next) {
             dap_chain_esbocs_validator_t *l_validator = it->data;
             if (dap_chain_addr_compare(&l_validator->signing_addr, &l_signing_addr))
@@ -1015,12 +1103,13 @@ static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_nod
                                                 l_session->cur_round.id, l_session->cur_round.attempt_num);
             s_session_state_change(l_session, DAP_CHAIN_ESBOCS_SESSION_STATE_WAIT_PROC, dap_time_now());
         }
-        break;
+    } break;
     case DAP_STREAM_CH_VOTING_MSG_TYPE_SUBMIT: {
         uint8_t *l_candidate = l_message->msg_n_sign;
         size_t l_candidate_size = l_message->hdr.message_size;
         if (!l_candidate_size || dap_hash_fast_is_blank(&l_message->hdr.candidate_hash)) {
-            debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu. Receive SUBMIT candidate NULL",
+            debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                                        " Receive SUBMIT candidate NULL",
                                             l_session->chain->net_name, l_session->chain->name,
                                                 l_session->cur_round.id, l_session->cur_round.attempt_num);
             if (dap_chain_addr_compare(&l_session->cur_round.attempt_submit_validator, &l_signing_addr))
@@ -1031,19 +1120,21 @@ static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_nod
         dap_chain_hash_fast_t l_check_hash;
         dap_hash_fast(l_candidate, l_candidate_size, &l_check_hash);
         if (!dap_hash_fast_compare(&l_check_hash, l_candidate_hash)) {
-            debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu. Receive SUBMIT candidate hash broken",
+            debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                                        " Receive SUBMIT candidate hash broken",
                                             l_session->chain->net_name, l_session->chain->name,
                                                 l_session->cur_round.id, l_session->cur_round.attempt_num);
             break;
         }
 
-        s_message_chain_add(l_session, l_message, a_data_size, a_data_hash);
+        s_message_chain_add(l_session, l_message, a_data_size, a_data_hash, &l_signing_addr);
 
         if (l_cs_debug) {
             char *l_candidate_hash_str = dap_chain_hash_fast_to_str_new(l_candidate_hash);
-            log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu. Receive SUBMIT candidate %s, size %zu",
-                    l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
-                        l_session->cur_round.attempt_num, l_candidate_hash_str, l_candidate_size);
+            log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                            " Receive SUBMIT candidate %s, size %zu",
+                                l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
+                                    l_session->cur_round.attempt_num, l_candidate_hash_str, l_candidate_size);
             DAP_DELETE(l_candidate_hash_str);
         }
 
@@ -1078,27 +1169,29 @@ static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_nod
         HASH_FIND(hh, l_session->cur_round.store_items, l_candidate_hash, sizeof(dap_chain_hash_fast_t), l_store);
         if (!l_store) {
             l_candidate_hash_str = dap_chain_hash_fast_to_str_new(l_candidate_hash);
-            log_it(L_WARNING, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu, Receive REJECT message for unknown candidate:%s",
-                       l_session->chain->net_name, l_session->chain->name,
-                           l_session->cur_round.id, l_session->cur_round.attempt_num,
-                                l_candidate_hash_str);
+            log_it(L_WARNING, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                                " Receive REJECT message for unknown candidate:%s",
+                                   l_session->chain->net_name, l_session->chain->name,
+                                       l_session->cur_round.id, l_session->cur_round.attempt_num,
+                                            l_candidate_hash_str);
             DAP_DELETE(l_candidate_hash_str);
             break;
         }
 
-        s_message_chain_add(l_session, l_message, a_data_size, a_data_hash);
+        s_message_chain_add(l_session, l_message, a_data_size, a_data_hash, &l_signing_addr);
 
         if (l_cs_debug) {
             l_candidate_hash_str = dap_chain_hash_fast_to_str_new(l_candidate_hash);
-            log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu Receive REJECT: candidate:%s",
-                    l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
-                        l_session->cur_round.attempt_num, l_candidate_hash_str);
+            log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                            " Receive REJECT: candidate:%s",
+                                l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
+                                    l_session->cur_round.attempt_num, l_candidate_hash_str);
         }
         if (++l_store->reject_count >= l_cs_level && !l_store->decide_reject &&
                 dap_hash_fast_compare(&l_session->cur_round.attempt_candidate_hash, l_candidate_hash)) {
             l_store->decide_reject = true;
             debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
-                                        " Candidate:%s rejected by more than 2/3 of the validators",
+                                        " Candidate:%s rejected by minimum number of validators, attempt failed",
                         l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
                             l_session->cur_round.attempt_num, l_candidate_hash_str);
             s_session_attempt_new(l_session);
@@ -1112,27 +1205,29 @@ static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_nod
         HASH_FIND(hh, l_session->cur_round.store_items, l_candidate_hash, sizeof(dap_chain_hash_fast_t), l_store);
         if (!l_store) {
             l_candidate_hash_str = dap_chain_hash_fast_to_str_new(l_candidate_hash);
-            log_it(L_WARNING, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu, Receive APPROVE message for unknown candidate:%s",
-                       l_session->chain->net_name, l_session->chain->name,
-                           l_session->cur_round.id, l_session->cur_round.attempt_num,
-                                l_candidate_hash_str);
+            log_it(L_WARNING, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                                " Receive APPROVE message for unknown candidate:%s",
+                                   l_session->chain->net_name, l_session->chain->name,
+                                       l_session->cur_round.id, l_session->cur_round.attempt_num,
+                                            l_candidate_hash_str);
             DAP_DELETE(l_candidate_hash_str);
             break;
         }
 
-        s_message_chain_add(l_session, l_message, a_data_size, a_data_hash);
+        s_message_chain_add(l_session, l_message, a_data_size, a_data_hash, &l_signing_addr);
 
         if (l_cs_debug) {
             l_candidate_hash_str = dap_chain_hash_fast_to_str_new(l_candidate_hash);
-            log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu Receive APPROVE: candidate:%s",
-                    l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
-                        l_session->cur_round.attempt_num, l_candidate_hash_str);
+            log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                            " Receive APPROVE: candidate:%s",
+                                l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
+                                    l_session->cur_round.attempt_num, l_candidate_hash_str);
         }
         if (++l_store->approve_count >= l_cs_level && !l_store->decide_approve &&
                 dap_hash_fast_compare(&l_session->cur_round.attempt_candidate_hash, l_candidate_hash)) {
             l_store->decide_approve = true;
             debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
-                                        " Candidate:%s approved by more than 2/3 of the validators, let's sign it",
+                                        " Candidate:%s approved by minimum number of validators, let's sign it",
                         l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
                             l_session->cur_round.attempt_num, l_candidate_hash_str);
             size_t l_offset = dap_chain_block_get_sign_offset(l_store->candidate, l_store->candidate_size);
@@ -1150,8 +1245,9 @@ static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_nod
         dap_sign_t *l_candidate_sign = (dap_sign_t *)l_message_data;
         size_t l_candidate_sign_size = dap_sign_get_size(l_candidate_sign);
         if (l_candidate_sign_size != l_message_data_size) {
-            log_it(L_WARNING, "Wrong commit_sign message size, have %zu bytes for candidate sign section when requires %zu bytes",
-                              l_candidate_sign_size, l_message_data_size);
+            log_it(L_WARNING, "Wrong commit_sign message size, have %zu bytes for candidate sign section"
+                                " when requires %zu bytes",
+                                  l_candidate_sign_size, l_message_data_size);
             break;
         }
 
@@ -1160,19 +1256,21 @@ static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_nod
         HASH_FIND(hh, l_session->cur_round.store_items, l_candidate_hash, sizeof(dap_chain_hash_fast_t), l_store);
         if (!l_store) {
             l_candidate_hash_str = dap_chain_hash_fast_to_str_new(l_candidate_hash);
-            log_it(L_WARNING, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu, Receive COMMIT_SIGN message for unknown candidate:%s",
-                       l_session->chain->net_name, l_session->chain->name,
-                           l_session->cur_round.id, l_session->cur_round.attempt_num,
-                                l_candidate_hash_str);
+            log_it(L_WARNING, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                                " Receive COMMIT_SIGN message for unknown candidate:%s",
+                                    l_session->chain->net_name, l_session->chain->name,
+                                        l_session->cur_round.id, l_session->cur_round.attempt_num,
+                                            l_candidate_hash_str);
             DAP_DELETE(l_candidate_hash_str);
             break;
         }
 
         if (l_cs_debug) {
             l_candidate_hash_str = dap_chain_hash_fast_to_str_new(l_candidate_hash);
-            log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu Receive COMMIT_SIGN: candidate:%s",
-                    l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
-                        l_session->cur_round.attempt_num, l_candidate_hash_str);
+            log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                            " Receive COMMIT_SIGN: candidate:%s",
+                                l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
+                                    l_session->cur_round.attempt_num, l_candidate_hash_str);
         }
 
         size_t l_offset = dap_chain_block_get_sign_offset(l_store->candidate, l_store->candidate_size);
@@ -1180,14 +1278,15 @@ static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_nod
                                                 l_offset + sizeof(l_store->candidate->hdr)) == 1;
         // check candidate's sign
         if (l_sign_verified) {
-            s_message_chain_add(l_session, l_message, a_data_size, a_data_hash);
+            s_message_chain_add(l_session, l_message, a_data_size, a_data_hash, &l_signing_addr);
             l_store->candidate_signs = dap_list_append(l_store->candidate_signs,
                                                        DAP_DUP_SIZE(l_candidate_sign, l_candidate_sign_size));
             if (dap_list_length(l_store->candidate_signs) == l_round->validators_synced_count) {
                 if (PVT(l_session->esbocs)->debug)
-                    log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu Candidate:%s collected signs of all synced validators",
-                            l_session->chain->net_name, l_session->chain->name, l_round->id,
-                                l_session->cur_round.attempt_num, l_candidate_hash_str);
+                    log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                                  " Candidate:%s collected signs of all synced validators",
+                                        l_session->chain->net_name, l_session->chain->name, l_round->id,
+                                            l_session->cur_round.attempt_num, l_candidate_hash_str);
                 s_session_state_change(l_session, DAP_CHAIN_ESBOCS_SESSION_STATE_WAIT_FINISH, dap_time_now());
             }
         } else {
@@ -1204,10 +1303,11 @@ static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_nod
         HASH_FIND(hh, l_session->cur_round.store_items, l_candidate_hash, sizeof(dap_chain_hash_fast_t), l_store);
         if (!l_store) {
             l_candidate_hash_str = dap_chain_hash_fast_to_str_new(l_candidate_hash);
-            log_it(L_WARNING, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu, Receive PRE_COMMIT message for unknown candidate:%s",
-                       l_session->chain->net_name, l_session->chain->name,
-                           l_session->cur_round.id, l_session->cur_round.attempt_num,
-                                l_candidate_hash_str);
+            log_it(L_WARNING, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                              " Receive PRE_COMMIT message for unknown candidate:%s",
+                                l_session->chain->net_name, l_session->chain->name,
+                                    l_session->cur_round.id, l_session->cur_round.attempt_num,
+                                        l_candidate_hash_str);
             DAP_DELETE(l_candidate_hash_str);
             break;
         }
@@ -1218,11 +1318,12 @@ static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_nod
                 l_candidate_hash_str = dap_chain_hash_fast_to_str_new(l_candidate_hash);
                 char *l_my_precommit_hash_str = dap_chain_hash_fast_to_str_new(&l_store->precommit_candidate_hash);
                 char *l_remote_precommit_hash_str = dap_chain_hash_fast_to_str_new(l_precommit_hash);
-                log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu Candidate:%s has different final hash of local and remote validators\n"
-                               "(%s and %s)",
-                            l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
-                                l_session->cur_round.attempt_num, l_candidate_hash_str,
-                                    l_my_precommit_hash_str, l_remote_precommit_hash_str);
+                log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                              " Candidate:%s has different final hash of local and remote validators\n"
+                              "(%s and %s)",
+                                    l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
+                                        l_session->cur_round.attempt_num, l_candidate_hash_str,
+                                            l_my_precommit_hash_str, l_remote_precommit_hash_str);
                 DAP_DELETE(l_candidate_hash_str);
                 DAP_DELETE(l_my_precommit_hash_str);
                 DAP_DELETE(l_remote_precommit_hash_str);
@@ -1230,22 +1331,24 @@ static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_nod
             break;
         }
 
-        s_message_chain_add(l_session, l_message, a_data_size, a_data_hash);
+        s_message_chain_add(l_session, l_message, a_data_size, a_data_hash, &l_signing_addr);
 
         if (l_cs_debug) {
             l_candidate_hash_str = dap_chain_hash_fast_to_str_new(l_candidate_hash);
-            log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu Receive PRE_COMMIT: candidate:%s",
-                    l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
-                        l_session->cur_round.attempt_num, l_candidate_hash_str);
+            log_it(L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                            " Receive PRE_COMMIT: candidate:%s",
+                                l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
+                                    l_session->cur_round.attempt_num, l_candidate_hash_str);
         }
         if (++l_store->precommit_count >= l_cs_level && !l_store->decide_commit &&
                 dap_hash_fast_compare(&l_session->cur_round.attempt_candidate_hash, l_candidate_hash)) {
             l_store->decide_commit = true;
-            debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu Candidate:%s rejected by more than 2/3 of the validators, remove it",
-                        l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
-                            l_session->cur_round.attempt_num, l_candidate_hash_str);
+            debug_if(l_cs_debug, L_MSG, "ESBOCS: net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hu."
+                                        " Candidate:%s precommted by minimum number of validators, try to finish this round",
+                                            l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id,
+                                                l_session->cur_round.attempt_num, l_candidate_hash_str);
             s_session_round_finish(l_session, l_store);
-            s_session_round_new(l_session);
+            // ATTENTION: New round will be started by incoming atom notifier event
         }
         DAP_DEL_Z(l_candidate_hash_str);
     } break;
@@ -1253,7 +1356,8 @@ static void s_session_packet_in(void *a_arg, dap_chain_node_addr_t *a_sender_nod
         break;
     }
 session_unlock:
-    pthread_rwlock_unlock(&l_session->rwlock);
+    if (a_sender_node_addr) //Process network message
+        pthread_mutex_unlock(&l_session->mutex);
 }
 
 static void s_message_send(dap_chain_esbocs_session_t *a_session, uint8_t a_message_type, dap_hash_fast_t *a_block_hash,
@@ -1282,13 +1386,17 @@ static void s_message_send(dap_chain_esbocs_session_t *a_session, uint8_t a_mess
     DAP_DELETE(l_sign);
     l_message->hdr.sign_size = l_sign_size;
 
-    dap_stream_ch_chain_voting_pkt_t *l_voting_pkt = dap_stream_ch_chain_voting_pkt_new(l_net->pub.id.uint64, l_message, l_message_size);
+    dap_stream_ch_chain_voting_pkt_t *l_voting_pkt =
+            dap_stream_ch_chain_voting_pkt_new(l_net->pub.id.uint64, &a_session->my_addr,
+                                               NULL, l_message, l_message_size);
     DAP_DELETE(l_message);
 
     for (dap_list_t *it = a_validators; it; it = it->next) {
         dap_chain_esbocs_validator_t *l_validator = it->data;
-        if (l_validator->is_synced)
+        if (l_validator->is_synced || a_message_type == DAP_STREAM_CH_VOTING_MSG_TYPE_START_SYNC) {
+            l_voting_pkt->hdr.receiver_node_addr = l_validator->node_addr;
             dap_stream_ch_chain_voting_message_write(l_net, &l_validator->node_addr, l_voting_pkt);
+        }
     }
     DAP_DELETE(l_voting_pkt);
 }
@@ -1319,10 +1427,19 @@ static int s_callback_block_verify(dap_chain_cs_blocks_t *a_blocks, dap_chain_bl
         return -3;
     }
     if (sizeof(a_block->hdr) >= a_block_size) {
-        log_it(L_WARNING,"Incorrect size with block %p on chain %s", a_block, a_blocks->chain->name);
+        log_it(L_WARNING, "Incorrect header size with block %p on chain %s", a_block, a_blocks->chain->name);
         return  -7;
     }
 
+    if (a_block->hdr.meta_n_datum_n_signs_size != a_block_size - sizeof(a_block->hdr)) {
+        log_it(L_WARNING, "Incorrect size with block %p on chain %s", a_block, a_blocks->chain->name);
+        return -8;
+    }
+
+    if (l_esbocs->session->processing_candidate == a_block)
+        // It's a block candidate, don't check signs
+        return 0;
+
     size_t l_offset = dap_chain_block_get_sign_offset(a_block, a_block_size);
     size_t l_signs_count = 0;
     dap_sign_t **l_signs = dap_sign_get_unique_signs(a_block->meta_n_datum_n_sign+l_offset,
@@ -1342,7 +1459,9 @@ static int s_callback_block_verify(dap_chain_cs_blocks_t *a_blocks, dap_chain_bl
     // Parse the rest signs
     int l_ret = 0;
     uint16_t l_signs_verified_count = 0;
-    size_t l_block_excl_sign_size = dap_chain_block_get_sign_offset(a_block, a_block_size)+sizeof(a_block->hdr);
+    size_t l_block_excl_sign_size = dap_chain_block_get_sign_offset(a_block, a_block_size) + sizeof(a_block->hdr);
+    // Get the header on signing operation time
+    a_block->hdr.meta_n_datum_n_signs_size = l_block_excl_sign_size - sizeof(a_block->hdr);
     for (size_t i=0; i< l_signs_count; i++) {
         dap_sign_t *l_sign = (dap_sign_t *)l_signs[i];
         if (!dap_sign_verify_size(l_sign, a_block_size - l_block_excl_sign_size + sizeof(a_block->hdr))) {
@@ -1366,6 +1485,9 @@ static int s_callback_block_verify(dap_chain_cs_blocks_t *a_blocks, dap_chain_bl
             l_signs_verified_count++;
     }
     DAP_DELETE(l_signs);
+    // Restore the original header
+    a_block->hdr.meta_n_datum_n_signs_size = a_block_size - sizeof(a_block->hdr);
+
     if ( l_ret != 0 ) {
         return l_ret;
     }
diff --git a/modules/consensus/esbocs/include/dap_chain_cs_esbocs.h b/modules/consensus/esbocs/include/dap_chain_cs_esbocs.h
index b0828c54b0..77778ec4b6 100644
--- a/modules/consensus/esbocs/include/dap_chain_cs_esbocs.h
+++ b/modules/consensus/esbocs/include/dap_chain_cs_esbocs.h
@@ -19,6 +19,7 @@
 typedef struct dap_chain_esbocs_session dap_chain_esbocs_session_t;
 
 /* consensus messages
+• Sync(round, last block, sync attempt) - try to synchronize validators before first round attempt start
 • Submit(round, candidate, body) — suggest a new block candidate *** candiate body in data section
 • Approve(round, candidate) — a block candidate has passed local validation
 • Reject(round, candidate) — a block candidate has failed local validation
@@ -95,6 +96,7 @@ typedef struct dap_chain_esbocs_round {
     // Validators section
     uint16_t validators_synced_count;
     dap_list_t *validators_list;
+    uint64_t sync_attempt;
 } dap_chain_esbocs_round_t;
 
 typedef struct dap_chain_esbocs_validator {
@@ -106,7 +108,8 @@ typedef struct dap_chain_esbocs_validator {
 } dap_chain_esbocs_validator_t;
 
 typedef struct dap_chain_esbocs_session {
-    pthread_rwlock_t rwlock;
+    pthread_mutex_t mutex;
+    dap_chain_block_t *processing_candidate;
 
     dap_chain_t *chain;
     dap_chain_esbocs_t *esbocs;
@@ -114,6 +117,7 @@ typedef struct dap_chain_esbocs_session {
     dap_chain_node_addr_t my_addr;
     uint8_t state; // session state
     dap_chain_esbocs_round_t cur_round;
+    bool round_fast_forward;
 
     dap_time_t ts_round_sync_start; // time of start sync
     dap_time_t ts_attempt_start; // time of current attempt start
diff --git a/modules/type/blocks/dap_chain_cs_blocks.c b/modules/type/blocks/dap_chain_cs_blocks.c
index 6e2dca0267..05f962ea7d 100644
--- a/modules/type/blocks/dap_chain_cs_blocks.c
+++ b/modules/type/blocks/dap_chain_cs_blocks.c
@@ -158,7 +158,7 @@ int dap_chain_cs_blocks_init()
                 "\t\tList block sections and show their datums hashes\n\n"
 
             "block -net <net_name> -chain <chain_name> new_datum\n\n"
-                "\t\tComplete the current new round, verify it and if everything is ok - publish new events in chain\n\n"
+                "\t\tComplete the current new round, verify it and if everything is ok - publish new blocks in chain\n\n"
 
         "Blockchain explorer:"
             "block -net <net_name> -chain <chain_name> dump <block_hash>\n"
-- 
GitLab