From 346043584be741fd8eca5d5eab34fdea7be9e4a9 Mon Sep 17 00:00:00 2001
From: "roman.khlopkov" <roman.khlopkov@demlabs.net>
Date: Tue, 20 Aug 2024 18:17:44 +0300
Subject: [PATCH] [*] Voting callback partially refactoring

---
 dap-sdk                                       |   2 +-
 modules/common/dap_chain_datum_tx_voting.c    |   8 +-
 .../include/dap_chain_datum_tx_voting.h       |   2 +-
 modules/net/dap_chain_ledger.c                |  18 +-
 modules/net/include/dap_chain_ledger.h        |   2 +-
 .../service/voting/dap_chain_net_srv_voting.c | 548 +++++++++---------
 6 files changed, 291 insertions(+), 289 deletions(-)

diff --git a/dap-sdk b/dap-sdk
index 31ae342434..e00cf1f1df 160000
--- a/dap-sdk
+++ b/dap-sdk
@@ -1 +1 @@
-Subproject commit 31ae342434501e6097f1c44712e092d47b2d582d
+Subproject commit e00cf1f1df55e3c5b4e23dacd4d5dd9388b4bc89
diff --git a/modules/common/dap_chain_datum_tx_voting.c b/modules/common/dap_chain_datum_tx_voting.c
index 1823d1add0..2a161dd3a9 100644
--- a/modules/common/dap_chain_datum_tx_voting.c
+++ b/modules/common/dap_chain_datum_tx_voting.c
@@ -108,16 +108,18 @@ dap_chain_tx_tsd_t* dap_chain_datum_voting_max_votes_count_tsd_create(uint64_t a
     return l_tsd;
 }
 
-dap_chain_tx_tsd_t* dap_chain_datum_voting_delegated_key_required_tsd_create(bool a_delegate_key_required)
+dap_chain_tx_tsd_t* dap_chain_datum_voting_delegated_key_required_tsd_create(bool a_delegated_key_required)
 {
-    dap_chain_tx_tsd_t* l_tsd = dap_chain_datum_tx_item_tsd_create(&a_delegate_key_required, VOTING_TSD_TYPE_DELEGATED_KEY_REQUIRED, sizeof(bool));
+    byte_t l_value = a_delegated_key_required;
+    dap_chain_tx_tsd_t* l_tsd = dap_chain_datum_tx_item_tsd_create(&l_value, VOTING_TSD_TYPE_DELEGATED_KEY_REQUIRED, sizeof(byte_t));
 
     return l_tsd;
 }
 
 dap_chain_tx_tsd_t* dap_chain_datum_voting_vote_changing_allowed_tsd_create(bool a_vote_changing_allowed)
 {
-    dap_chain_tx_tsd_t* l_tsd = dap_chain_datum_tx_item_tsd_create(&a_vote_changing_allowed, VOTING_TSD_TYPE_VOTE_CHANGING_ALLOWED, sizeof(bool));
+    byte_t l_value = a_vote_changing_allowed;
+    dap_chain_tx_tsd_t* l_tsd = dap_chain_datum_tx_item_tsd_create(&l_value, VOTING_TSD_TYPE_VOTE_CHANGING_ALLOWED, sizeof(byte_t));
 
     return l_tsd;
 }
diff --git a/modules/common/include/dap_chain_datum_tx_voting.h b/modules/common/include/dap_chain_datum_tx_voting.h
index f51628e59c..d9a991a839 100644
--- a/modules/common/include/dap_chain_datum_tx_voting.h
+++ b/modules/common/include/dap_chain_datum_tx_voting.h
@@ -80,7 +80,7 @@ dap_chain_tx_tsd_t* dap_chain_datum_voting_question_tsd_create(const char* a_que
 dap_chain_tx_tsd_t* dap_chain_datum_voting_answer_tsd_create(const char* a_answer_str, size_t str_len);
 dap_chain_tx_tsd_t* dap_chain_datum_voting_expire_tsd_create(dap_time_t a_expire);
 dap_chain_tx_tsd_t* dap_chain_datum_voting_max_votes_count_tsd_create(uint64_t a_max_count);
-dap_chain_tx_tsd_t* dap_chain_datum_voting_delegated_key_required_tsd_create(bool a_delegate_key_required);
+dap_chain_tx_tsd_t *dap_chain_datum_voting_delegated_key_required_tsd_create(bool a_delegated_key_required);
 dap_chain_tx_tsd_t* dap_chain_datum_voting_vote_changing_allowed_tsd_create(bool a_vote_changing_allowed);
 dap_chain_tx_tsd_t* dap_chain_datum_voting_vote_tx_cond_tsd_create(dap_chain_hash_fast_t a_tx_hash, int a_out_idx);
 
diff --git a/modules/net/dap_chain_ledger.c b/modules/net/dap_chain_ledger.c
index d10b144a15..5fb52c7c73 100644
--- a/modules/net/dap_chain_ledger.c
+++ b/modules/net/dap_chain_ledger.c
@@ -4151,9 +4151,9 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
     if (!l_err_num) {
         // TODO move it to service tag deduction
         if ( dap_chain_datum_tx_item_get(a_tx, NULL, NULL, TX_ITEM_TYPE_VOTING, NULL ) ) {
-            if (s_voting_callbacks.voting_callback){
-                if (!s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTING, a_tx, false)){
-                    debug_if(s_debug_more, L_WARNING, "Verificator check error for voting.");
+            if (s_voting_callbacks.voting_callback) {
+                if ((l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTING, a_tx, a_tx_hash, false))) {
+                    debug_if(s_debug_more, L_WARNING, "Verificator check error %d for voting", l_err_num);
                     l_err_num = DAP_LEDGER_TX_CHECK_VERIFICATOR_CHECK_FAILURE;
                 }
             } else {
@@ -4163,9 +4163,9 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
             if (a_tag)
                 a_tag->uint64 = DAP_CHAIN_TX_TAG_ACTION_VOTING;
         } else if ( dap_chain_datum_tx_item_get(a_tx, NULL, NULL, TX_ITEM_TYPE_VOTE, NULL) ) {
-           if (s_voting_callbacks.voting_callback){
-               if (!s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTE, a_tx, false)){
-                   debug_if(s_debug_more, L_WARNING, "Verificator check error for vote.");
+           if (s_voting_callbacks.voting_callback) {
+               if ((l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTE, a_tx, false, a_tx_hash))) {
+                   debug_if(s_debug_more, L_WARNING, "Verificator check error %d for vote", l_err_num);
                    l_err_num = DAP_LEDGER_TX_CHECK_VERIFICATOR_CHECK_FAILURE;
                }
            } else {
@@ -4566,12 +4566,14 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha
             s_balance_cache_update(a_ledger, wallet_balance);
         }
     }
+    int l_err_num = 0;
     if (s_voting_callbacks.voting_callback) {
         if (l_tag.uint64 == DAP_CHAIN_TX_TAG_ACTION_VOTING)
-            s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTING, a_tx, true);
+            l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTING, a_tx, a_tx_hash, true);
         else if (l_tag.uint64 == DAP_CHAIN_TX_TAG_ACTION_VOTE)
-            s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTE, a_tx, true);
+            l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTE, a_tx, a_tx_hash, true);
     }
+    assert(!l_err_num);
 
     // add transaction to the cache list
     dap_ledger_tx_item_t *l_tx_item = DAP_NEW_Z(dap_ledger_tx_item_t);
diff --git a/modules/net/include/dap_chain_ledger.h b/modules/net/include/dap_chain_ledger.h
index 29a030219b..7aafc5faef 100644
--- a/modules/net/include/dap_chain_ledger.h
+++ b/modules/net/include/dap_chain_ledger.h
@@ -204,7 +204,7 @@ typedef void (* dap_ledger_tx_add_notify_t)(void *a_arg, dap_ledger_t *a_ledger,
 typedef void (* dap_ledger_bridged_tx_notify_t)(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash, void *a_arg, dap_chan_ledger_notify_opcodes_t a_opcode);
 typedef bool (*dap_ledger_cache_tx_check_callback_t)(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_hash);
 typedef struct dap_chain_net dap_chain_net_t;
-typedef bool (*dap_chain_ledger_voting_callback_t)(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx, bool a_apply);
+typedef int (*dap_chain_ledger_voting_callback_t)(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash, bool a_apply);
 typedef bool (*dap_chain_ledger_voting_delete_callback_t)(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx);
 typedef bool (*dap_ledger_tag_check_callback_t)(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_chain_datum_tx_item_groups_t *a_items_grp, dap_chain_tx_tag_action_type_t *a_action);
 
diff --git a/modules/service/voting/dap_chain_net_srv_voting.c b/modules/service/voting/dap_chain_net_srv_voting.c
index ae204b2990..8ac00de3f8 100644
--- a/modules/service/voting/dap_chain_net_srv_voting.c
+++ b/modules/service/voting/dap_chain_net_srv_voting.c
@@ -44,19 +44,13 @@ typedef struct dap_chain_net_voting_params_offsets{
     dap_chain_datum_tx_t* voting_tx;
     size_t voting_question_offset;
     size_t voting_question_length;
-    dap_list_t* option_offsets_list;
-    size_t voting_expire_offset;
-    size_t votes_max_count_offset;
-    size_t delegate_key_required_offset;
-    size_t vote_changing_allowed_offset;
+    dap_list_t *option_offsets_list;
+    dap_time_t voting_expire;
+    uint64_t votes_max_count;
+    bool delegate_key_required;
+    bool vote_changing_allowed;
 } dap_chain_net_voting_params_offsets_t;
 
-
-typedef struct dap_chain_net_voting{
-    dap_hash_fast_t hash_vote;
-    dap_chain_net_id_t net_id;
-}dap_chain_net_voting_t;
-
 typedef struct dap_chain_net_vote_option {
     size_t vote_option_offset;
     size_t vote_option_length;
@@ -94,7 +88,7 @@ static pthread_rwlock_t s_votings_rwlock;
 static int s_datum_tx_voting_coin_check_cond_out(dap_chain_net_t *a_net, dap_hash_fast_t a_voting_hash, dap_hash_fast_t a_tx_cond_hash, int a_cond_out_idx);
 /// -1 error, 0 - unspent, 1 - spent
 static int s_datum_tx_voting_coin_check_spent(dap_chain_net_t *a_net, dap_hash_fast_t a_voting_hash, dap_hash_fast_t a_tx_prev_hash, int a_out_idx);
-static bool s_datum_tx_voting_verification_callback(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in, bool a_apply);
+static int s_datum_tx_voting_verification_callback(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, bool a_apply);
 static bool s_datum_tx_voting_verification_delete_callback(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in);
 static int s_cli_voting(int argc, char **argv, void **a_str_reply);
 
@@ -178,319 +172,323 @@ uint64_t* dap_chain_net_voting_get_result(dap_ledger_t* a_ledger, dap_chain_hash
 }
 
 
-
-bool s_datum_tx_voting_verification_callback(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in, bool a_apply)
+static int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, bool a_apply)
 {
-
-    dap_hash_fast_t l_hash = {};
-    dap_hash_fast(a_tx_in, dap_chain_datum_tx_get_size(a_tx_in), &l_hash);
-
-    if (a_type == TX_ITEM_TYPE_VOTING){
-        if (!a_apply){
-            dap_chain_net_votings_t * l_voting = NULL;
-            pthread_rwlock_rdlock(&s_votings_rwlock);
-            HASH_FIND(hh, s_votings, &l_hash, sizeof(dap_hash_fast_t), l_voting);
-            pthread_rwlock_unlock(&s_votings_rwlock);
-            if(l_voting && l_voting->net_id.uint64 == a_ledger->net->pub.id.uint64){
-                char* l_hash_str = dap_hash_fast_to_str_new(&l_hash);
-                log_it(L_ERROR, "Voting with hash %s is already presents in net %s", l_hash_str, a_ledger->net->pub.name);
-                DAP_DEL_Z(l_hash_str);
-                return false;
-            }
-
-            dap_list_t* l_tsd_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_TSD, NULL);
-            dap_list_t* l_temp = l_tsd_list;
-            size_t l_question_len = 0;
-            size_t l_options_count = 0;
-            while (l_temp){
-                dap_tsd_t* l_tsd = (dap_tsd_t*)((dap_chain_tx_tsd_t*)l_temp->data)->tsd;
-                dap_chain_net_vote_option_t *l_vote_option = NULL;
-                switch(l_tsd->type){
-                case VOTING_TSD_TYPE_QUESTION:
-                    l_question_len = l_tsd->size;
-                    break;
-                case VOTING_TSD_TYPE_ANSWER:
-                    l_options_count++;
-                    break;
-                default:
-                    break;
-                }
-                l_temp = l_temp->next;
-            }
-            dap_list_free(l_tsd_list);
-
-            if (!l_question_len || !l_options_count)
-                return false;
-
-            return true;
+    if (!a_apply) {
+        dap_chain_net_votings_t * l_voting = NULL;
+        pthread_rwlock_rdlock(&s_votings_rwlock);
+        HASH_FIND(hh, s_votings, a_tx_hash, sizeof(dap_hash_fast_t), l_voting);
+        pthread_rwlock_unlock(&s_votings_rwlock);
+        if (l_voting && l_voting->net_id.uint64 == a_ledger->net->pub.id.uint64) {
+            log_it(L_DEBUG, "Voting with hash %s is already presents in net %s",  dap_hash_fast_to_str_static(a_tx_hash), a_ledger->net->pub.name);
+            return -1;
         }
 
-        dap_chain_net_votings_t *l_item = DAP_NEW_Z_SIZE(dap_chain_net_votings_t, sizeof(dap_chain_net_votings_t));
-        l_item->voting_hash = l_hash;
-        l_item->voting_params.voting_tx = a_tx_in;
-        l_item->net_id = a_ledger->net->pub.id;
-        pthread_rwlock_init(&l_item->s_tx_outs_rwlock, NULL);
-
         dap_list_t* l_tsd_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_TSD, NULL);
-        dap_list_t* l_temp = l_tsd_list;
-        while (l_temp){
-            dap_tsd_t* l_tsd = (dap_tsd_t*)((dap_chain_tx_tsd_t*)l_temp->data)->tsd;
-            dap_chain_net_vote_option_t *l_vote_option = NULL;
-            switch(l_tsd->type){
+        size_t l_question_len = 0;
+        size_t l_options_count = 0;
+        for (dap_list_t *it = l_tsd_list; it; it = it->next) {
+            dap_tsd_t *l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t *)it->data)->tsd;
+            switch (l_tsd->type) {
             case VOTING_TSD_TYPE_QUESTION:
-                l_item->voting_params.voting_question_offset = (size_t)(l_tsd->data - (byte_t*)l_item->voting_params.voting_tx);
-                l_item->voting_params.voting_question_length = l_tsd->size;
+                l_question_len = l_tsd->size;
                 break;
             case VOTING_TSD_TYPE_ANSWER:
-                l_vote_option = DAP_NEW_Z(dap_chain_net_vote_option_t);
-                l_vote_option->vote_option_offset = (size_t)(l_tsd->data - (byte_t*)l_item->voting_params.voting_tx);
-                l_vote_option->vote_option_length = l_tsd->size;
-                l_item->voting_params.option_offsets_list = dap_list_append(l_item->voting_params.option_offsets_list, l_vote_option);
-                break;
-            case VOTING_TSD_TYPE_EXPIRE:
-                l_item->voting_params.voting_expire_offset = (size_t)(l_tsd->data - (byte_t*)l_item->voting_params.voting_tx);
-                break;
-            case VOTING_TSD_TYPE_MAX_VOTES_COUNT:
-                l_item->voting_params.votes_max_count_offset = (size_t)(l_tsd->data - (byte_t*)l_item->voting_params.voting_tx);
-                break;
-            case VOTING_TSD_TYPE_DELEGATED_KEY_REQUIRED:
-                l_item->voting_params.delegate_key_required_offset = (size_t)(l_tsd->data - (byte_t*)l_item->voting_params.voting_tx);
-                break;
-            case VOTING_TSD_TYPE_VOTE_CHANGING_ALLOWED:
-                l_item->voting_params.vote_changing_allowed_offset = (size_t)(l_tsd->data - (byte_t*)l_item->voting_params.voting_tx);
+                l_options_count++;
                 break;
             default:
                 break;
             }
-            l_temp = l_temp->next;
         }
         dap_list_free(l_tsd_list);
 
-        pthread_rwlock_wrlock(&s_votings_rwlock);
-        HASH_ADD(hh, s_votings, voting_hash, sizeof(dap_hash_fast_t), l_item);
-        pthread_rwlock_unlock(&s_votings_rwlock);
-        return true;
-    } else if (a_type == TX_ITEM_TYPE_VOTE){
-        dap_chain_tx_vote_t *l_vote_tx_item = (dap_chain_tx_vote_t*)dap_chain_datum_tx_item_get(a_tx_in, NULL, NULL, TX_ITEM_TYPE_VOTE, NULL);
-        if(!l_vote_tx_item){
-            log_it(L_ERROR, "Can't find vote item");
-            return false;
+        if (!l_question_len || !l_options_count) {
+            log_it(L_DEBUG, "Voting with hash %s contain no question or answer options", dap_hash_fast_to_str_static(a_tx_hash));
+            return -2;
         }
 
-        dap_chain_net_votings_t * l_voting = NULL;
-        pthread_rwlock_wrlock(&s_votings_rwlock);
-        HASH_FIND(hh, s_votings, &l_vote_tx_item->voting_hash, sizeof(dap_hash_fast_t), l_voting);
-        pthread_rwlock_unlock(&s_votings_rwlock);
-        if(!l_voting || l_voting->net_id.uint64 != a_ledger->net->pub.id.uint64) {
-            log_it(L_ERROR, "Can't find voting with hash %s in net %s",
-                   dap_chain_hash_fast_to_str_static(&l_hash), a_ledger->net->pub.name);
-            return false;
-        }
+        return DAP_LEDGER_CHECK_OK;
+    }
 
-        if (!a_apply){
-            if (l_vote_tx_item->answer_idx > dap_list_length(l_voting->voting_params.option_offsets_list)){
-                log_it(L_ERROR, "Invalid vote option index.");
-                return false;
+    dap_chain_net_votings_t *l_item = DAP_NEW_Z_SIZE(dap_chain_net_votings_t, sizeof(dap_chain_net_votings_t));
+    l_item->voting_hash = *a_tx_hash;
+    l_item->voting_params.voting_tx = a_tx_in;
+    l_item->net_id = a_ledger->net->pub.id;
+    pthread_rwlock_init(&l_item->s_tx_outs_rwlock, NULL);
+
+    dap_list_t* l_tsd_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_TSD, NULL);
+    for (dap_list_t *it = l_tsd_list; it; it = it->next) {
+        dap_tsd_t* l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t*)it->data)->tsd;
+        dap_chain_net_vote_option_t *l_vote_option = NULL;
+        switch(l_tsd->type){
+        case VOTING_TSD_TYPE_QUESTION:
+            l_item->voting_params.voting_question_offset = (size_t)(l_tsd->data - (byte_t*)l_item->voting_params.voting_tx);
+            l_item->voting_params.voting_question_length = l_tsd->size;
+            break;
+        case VOTING_TSD_TYPE_ANSWER:
+            l_vote_option = DAP_NEW_Z(dap_chain_net_vote_option_t);
+            l_vote_option->vote_option_offset = (size_t)(l_tsd->data - (byte_t*)l_item->voting_params.voting_tx);
+            l_vote_option->vote_option_length = l_tsd->size;
+            l_item->voting_params.option_offsets_list = dap_list_append(l_item->voting_params.option_offsets_list, l_vote_option);
+            break;
+        case VOTING_TSD_TYPE_EXPIRE:
+            if (l_tsd->size != sizeof(dap_time_t)) {
+                log_it(L_WARNING, "Incorrect size of TSD section EXPIRE vot voting %s", dap_hash_fast_to_str_static(a_tx_hash));
+                return -DAP_LEDGER_CHECK_INVALID_SIZE;
             }
+            l_item->voting_params.voting_expire = *(dap_time_t *)l_tsd->data;
+            break;
+        case VOTING_TSD_TYPE_MAX_VOTES_COUNT:
+            l_item->voting_params.votes_max_count_offset = (size_t)(l_tsd->data - (byte_t*)l_item->voting_params.voting_tx);
+            break;
+        case VOTING_TSD_TYPE_DELEGATED_KEY_REQUIRED:
+            l_item->voting_params.delegate_key_required_offset = (size_t)(l_tsd->data - (byte_t*)l_item->voting_params.voting_tx);
+            break;
+        case VOTING_TSD_TYPE_VOTE_CHANGING_ALLOWED:
+            l_item->voting_params.vote_changing_allowed_offset = (size_t)(l_tsd->data - (byte_t*)l_item->voting_params.voting_tx);
+            break;
+        default:
+            break;
+        }
+    }
+    dap_list_free(l_tsd_list);
 
-            if(l_voting->voting_params.votes_max_count_offset){
-                uint64_t l_votes_max_count = *(uint64_t*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.votes_max_count_offset);
-                if (l_votes_max_count && dap_list_length(l_voting->votes) >= l_votes_max_count){
-                    log_it(L_ERROR, "The required number of votes has been collected.");
-                    return false;
-                }
+    pthread_rwlock_wrlock(&s_votings_rwlock);
+    HASH_ADD(hh, s_votings, voting_hash, sizeof(dap_hash_fast_t), l_item);
+    pthread_rwlock_unlock(&s_votings_rwlock);
+    return DAP_LEDGER_CHECK_OK;
+}
+
+static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, bool a_apply)
+{
+    dap_chain_tx_vote_t *l_vote_tx_item = (dap_chain_tx_vote_t*)dap_chain_datum_tx_item_get(a_tx_in, NULL, NULL, TX_ITEM_TYPE_VOTE, NULL);
+    if (!l_vote_tx_item) {
+        log_it(L_ERROR, "Can't find vote item for tx %s", dap_chain_hash_fast_to_str_static(a_tx_hash));
+        return -4;
+    }
+
+    dap_chain_net_votings_t *l_voting = NULL;
+    pthread_rwlock_wrlock(&s_votings_rwlock);
+    HASH_FIND(hh, s_votings, &l_vote_tx_item->voting_hash, sizeof(dap_hash_fast_t), l_voting);
+    pthread_rwlock_unlock(&s_votings_rwlock);
+    if(!l_voting || l_voting->net_id.uint64 != a_ledger->net->pub.id.uint64) {
+        log_it(L_ERROR, "Can't find voting with hash %s in net %s",
+               dap_chain_hash_fast_to_str_static(&l_vote_tx_item->voting_hash), a_ledger->net->pub.name);
+        return -5;
+    }
+
+    if (!a_apply) {
+        if (l_vote_tx_item->answer_idx > dap_list_length(l_voting->voting_params.option_offsets_list)) {
+            log_it(L_ERROR, "Invalid vote option index %" DAP_UINT64_FORMAT_U " for vote tx %s",
+                                                    l_vote_tx_item->answer_idx, dap_chain_hash_fast_to_str_static(a_tx_hash));
+            return -6;
+        }
+
+        if (l_voting->voting_params.votes_max_count_offset) {
+            uint64_t l_votes_max_count = *(uint64_t*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.votes_max_count_offset);
+            if (l_votes_max_count && dap_list_length(l_voting->votes) >= l_votes_max_count){
+                log_it(L_ERROR, "The required number of votes has been collected for voting %s", dap_chain_hash_fast_to_str_static(&l_voting->voting_hash));
+                return -7;
             }
+        }
 
-            if(l_voting->voting_params.voting_expire_offset){
-                dap_time_t l_expire = *(dap_time_t*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.voting_expire_offset);
-                if( l_expire && l_expire <= a_tx_in->header.ts_created){
-                    log_it(L_ERROR, "The voting has been expired.");
-                    return false;
-                }
+        if (l_voting->voting_params.voting_expire_offset) {
+            dap_time_t l_expire = *(dap_time_t*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.voting_expire_offset);
+            if( l_expire && l_expire <= a_tx_in->header.ts_created){
+                log_it(L_ERROR, "The voting %s has been expired", dap_chain_hash_fast_to_str_static(&l_voting->voting_hash));
+                return -8;
             }
+        }
 
-            dap_hash_fast_t pkey_hash = {};
-            dap_chain_tx_sig_t *l_vote_sig = NULL;
-            int l_item_cnt = 0;
-            dap_list_t* l_signs_list = NULL;
-            l_signs_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_SIG, &l_item_cnt);
+        dap_hash_fast_t pkey_hash = {};
+        dap_chain_tx_sig_t *l_vote_sig = NULL;
+        int l_item_cnt = 0;
+        dap_list_t* l_signs_list = NULL;
+        l_signs_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_SIG, &l_item_cnt);
 
-            if(!l_signs_list){
-                log_it(L_ERROR, "Can't get sign.");
-                return false;
-            }
-            l_vote_sig = (dap_chain_tx_sig_t *)(dap_list_last(l_signs_list)->data);
-            dap_sign_get_pkey_hash((dap_sign_t*)l_vote_sig->sig, &pkey_hash);
-            if (l_voting->voting_params.delegate_key_required_offset &&
-                *(bool*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.delegate_key_required_offset)){
-                if (!dap_chain_net_srv_stake_check_pkey_hash(a_ledger->net->pub.id, &pkey_hash)){
-                    log_it(L_ERROR, "The voting required a delegated key.");
-                    dap_list_free(l_signs_list);
-                    return false;
-                }
+        if (!l_signs_list) {
+            log_it(L_ERROR, "Can't get signs from tx %s", dap_chain_hash_fast_to_str_static(a_tx_hash));
+            return -9;
+        }
+        l_vote_sig = (dap_chain_tx_sig_t *)(dap_list_last(l_signs_list)->data);
+        dap_sign_get_pkey_hash((dap_sign_t*)l_vote_sig->sig, &pkey_hash);
+        if (l_voting->voting_params.delegate_key_required_offset &&
+            *(bool*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.delegate_key_required_offset)){
+            if (!dap_chain_net_srv_stake_check_pkey_hash(a_ledger->net->pub.id, &pkey_hash)){
+                log_it(L_ERROR, "Voting %s required a delegated key", dap_chain_hash_fast_to_str_static(&l_voting->voting_hash));
+                dap_list_free(l_signs_list);
+                return -10;
             }
+        }
 
-            dap_list_t *l_temp = l_voting->votes;
-            while(l_temp){
-                if (dap_hash_fast_compare(&((dap_chain_net_vote_t *)l_temp->data)->pkey_hash, &pkey_hash)){
-                    if(l_voting->voting_params.vote_changing_allowed_offset &&
-                        *(bool*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.vote_changing_allowed_offset)){
-                        //delete conditional outputs
-                        dap_chain_datum_tx_t *l_old_tx = dap_ledger_tx_find_by_hash(a_ledger, &((dap_chain_net_vote_t *)l_temp->data)->vote_hash);
-
-                        dap_list_t* l_tsd_list = dap_chain_datum_tx_items_get(l_old_tx, TX_ITEM_TYPE_TSD, NULL);
-                        dap_list_t* l_tsd_temp = l_tsd_list;
-                        while (l_tsd_temp){
-                            dap_tsd_t* l_tsd = (dap_tsd_t*)((dap_chain_tx_tsd_t*)l_tsd_temp->data)->tsd;
-                            dap_hash_fast_t l_hash = ((dap_chain_tx_voting_tx_cond_t*)l_tsd->data)->tx_hash;
-                            if(l_tsd->type == VOTING_TSD_TYPE_VOTE_TX_COND){
-                                dap_chain_net_voting_cond_outs_t *l_tx_outs = NULL;
-                                pthread_rwlock_wrlock(&l_voting->s_tx_outs_rwlock);
-                                HASH_FIND(hh, l_voting->voting_spent_cond_outs, &l_hash, sizeof(dap_hash_fast_t), l_tx_outs);
-                                if(l_tx_outs)
-                                    HASH_DELETE(hh, l_voting->voting_spent_cond_outs, l_tx_outs);
-                                pthread_rwlock_unlock(&l_voting->s_tx_outs_rwlock);
-                            }
-                            l_tsd_temp = l_tsd_temp->next;
+        dap_list_t *l_temp = l_voting->votes;
+        while(l_temp){
+            if (dap_hash_fast_compare(&((dap_chain_net_vote_t *)l_temp->data)->pkey_hash, &pkey_hash)){
+                if(l_voting->voting_params.vote_changing_allowed_offset &&
+                    *(bool*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.vote_changing_allowed_offset)){
+                    //delete conditional outputs
+                    dap_chain_datum_tx_t *l_old_tx = dap_ledger_tx_find_by_hash(a_ledger, &((dap_chain_net_vote_t *)l_temp->data)->vote_hash);
+
+                    dap_list_t* l_tsd_list = dap_chain_datum_tx_items_get(l_old_tx, TX_ITEM_TYPE_TSD, NULL);
+                    dap_list_t* l_tsd_temp = l_tsd_list;
+                    while (l_tsd_temp){
+                        dap_tsd_t* l_tsd = (dap_tsd_t*)((dap_chain_tx_tsd_t*)l_tsd_temp->data)->tsd;
+                        dap_hash_fast_t l_hash = ((dap_chain_tx_voting_tx_cond_t*)l_tsd->data)->tx_hash;
+                        if(l_tsd->type == VOTING_TSD_TYPE_VOTE_TX_COND){
+                            dap_chain_net_voting_cond_outs_t *l_tx_outs = NULL;
+                            pthread_rwlock_wrlock(&l_voting->s_tx_outs_rwlock);
+                            HASH_FIND(hh, l_voting->voting_spent_cond_outs, &l_hash, sizeof(dap_hash_fast_t), l_tx_outs);
+                            if(l_tx_outs)
+                                HASH_DELETE(hh, l_voting->voting_spent_cond_outs, l_tx_outs);
+                            pthread_rwlock_unlock(&l_voting->s_tx_outs_rwlock);
                         }
-                        dap_list_free(l_tsd_list);
+                        l_tsd_temp = l_tsd_temp->next;
+                    }
+                    dap_list_free(l_tsd_list);
 
 
-                        //delete vote
-                        l_voting->votes = dap_list_remove(l_voting->votes, l_temp->data);
-                        break;
-                    } else {
-                        log_it(L_ERROR, "The voting don't allow change your vote.");
-                        return false;
-                    }
+                    //delete vote
+                    l_voting->votes = dap_list_remove(l_voting->votes, l_temp->data);
+                    break;
+                } else {
+                    log_it(L_ERROR, "The voting don't allow change your vote.");
+                    return false;
                 }
-                l_temp = l_temp->next;
             }
-            dap_list_free(l_signs_list);
+            l_temp = l_temp->next;
         }
+        dap_list_free(l_signs_list);
+    }
 
-        uint256_t l_weight = {};
-
-        // check out conds
-        dap_list_t* l_tsd_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_TSD, NULL);
-        dap_list_t* l_tsd_temp = l_tsd_list;
-        while (l_tsd_temp){
-            dap_tsd_t* l_tsd = (dap_tsd_t*)((dap_chain_tx_tsd_t*)l_tsd_temp->data)->tsd;
-            dap_hash_fast_t l_hash = ((dap_chain_tx_voting_tx_cond_t*)l_tsd->data)->tx_hash;
-            int l_out_idx = ((dap_chain_tx_voting_tx_cond_t*)l_tsd->data)->out_idx;
-            if(l_tsd->type == VOTING_TSD_TYPE_VOTE_TX_COND){
-                if (s_datum_tx_voting_coin_check_cond_out(a_ledger->net, l_vote_tx_item->voting_hash,
-                                                          l_hash, l_out_idx) != 0){
-                    l_tsd_temp = l_tsd_temp->next;
-                    continue;
-                }
-                dap_chain_datum_tx_t *l_tx_prev_temp = dap_ledger_tx_find_by_hash(a_ledger, &l_hash);
-                dap_chain_tx_out_cond_t *l_prev_out = (dap_chain_tx_out_cond_t*)dap_chain_datum_tx_item_get(l_tx_prev_temp, &l_out_idx, NULL, TX_ITEM_TYPE_OUT_COND, NULL);
-                if(!l_prev_out || l_prev_out->header.subtype != DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK){
-                    l_tsd_temp = l_tsd_temp->next;
-                    continue;
-                }
-                SUM_256_256(l_weight, l_prev_out->header.value, &l_weight);
-
-                dap_chain_net_voting_cond_outs_t *l_item = DAP_NEW_Z_SIZE(dap_chain_net_voting_cond_outs_t, sizeof(dap_chain_net_voting_cond_outs_t));
-                l_item->tx_hash = l_hash;
-                l_item->out_idx = l_out_idx;
-                pthread_rwlock_wrlock(&l_voting->s_tx_outs_rwlock);
-                HASH_ADD(hh, l_voting->voting_spent_cond_outs, tx_hash, sizeof(dap_hash_fast_t), l_item);
-                pthread_rwlock_unlock(&l_voting->s_tx_outs_rwlock);
+    uint256_t l_weight = {};
+
+    // check out conds
+    dap_list_t* l_tsd_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_TSD, NULL);
+    dap_list_t* l_tsd_temp = l_tsd_list;
+    while (l_tsd_temp){
+        dap_tsd_t* l_tsd = (dap_tsd_t*)((dap_chain_tx_tsd_t*)l_tsd_temp->data)->tsd;
+        dap_hash_fast_t l_hash = ((dap_chain_tx_voting_tx_cond_t*)l_tsd->data)->tx_hash;
+        int l_out_idx = ((dap_chain_tx_voting_tx_cond_t*)l_tsd->data)->out_idx;
+        if(l_tsd->type == VOTING_TSD_TYPE_VOTE_TX_COND){
+            if (s_datum_tx_voting_coin_check_cond_out(a_ledger->net, l_vote_tx_item->voting_hash,
+                                                      l_hash, l_out_idx) != 0){
+                l_tsd_temp = l_tsd_temp->next;
+                continue;
             }
-            l_tsd_temp = l_tsd_temp->next;
-        }
-        dap_list_free(l_tsd_list);
-        // check inputs
-        dap_list_t *l_ins_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_IN, NULL);
-        if (!l_ins_list){
-            log_it(L_ERROR, "Can't get inputs from tx");
-            return -1;
+            dap_chain_datum_tx_t *l_tx_prev_temp = dap_ledger_tx_find_by_hash(a_ledger, &l_hash);
+            dap_chain_tx_out_cond_t *l_prev_out = (dap_chain_tx_out_cond_t*)dap_chain_datum_tx_item_get(l_tx_prev_temp, &l_out_idx, NULL, TX_ITEM_TYPE_OUT_COND, NULL);
+            if(!l_prev_out || l_prev_out->header.subtype != DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK){
+                l_tsd_temp = l_tsd_temp->next;
+                continue;
+            }
+            SUM_256_256(l_weight, l_prev_out->header.value, &l_weight);
+
+            dap_chain_net_voting_cond_outs_t *l_item = DAP_NEW_Z_SIZE(dap_chain_net_voting_cond_outs_t, sizeof(dap_chain_net_voting_cond_outs_t));
+            l_item->tx_hash = l_hash;
+            l_item->out_idx = l_out_idx;
+            pthread_rwlock_wrlock(&l_voting->s_tx_outs_rwlock);
+            HASH_ADD(hh, l_voting->voting_spent_cond_outs, tx_hash, sizeof(dap_hash_fast_t), l_item);
+            pthread_rwlock_unlock(&l_voting->s_tx_outs_rwlock);
         }
-        dap_list_t *l_in_temp = l_ins_list;
-        while(l_in_temp){
-            dap_chain_tx_in_t *l_tx_in = (dap_chain_tx_in_t *)l_in_temp->data;
-            if (s_datum_tx_voting_coin_check_spent(a_ledger->net, l_vote_tx_item->voting_hash, l_tx_in->header.tx_prev_hash, l_tx_in->header.tx_out_prev_idx) == 0){
-                dap_chain_datum_tx_t *l_tx_prev_temp = dap_ledger_tx_find_by_hash(a_ledger, &l_tx_in->header.tx_prev_hash);
-                int l_out_prev_idx = (int)l_tx_in->header.tx_out_prev_idx;
-                dap_chain_tx_out_t *l_prev_out_union = (dap_chain_tx_out_t *)dap_chain_datum_tx_out_get_by_out_idx(l_tx_prev_temp, l_out_prev_idx);
-                if (!l_prev_out_union){
-                    l_in_temp = l_in_temp->next;
-                    continue;
-                }
+        l_tsd_temp = l_tsd_temp->next;
+    }
+    dap_list_free(l_tsd_list);
+    // check inputs
+    dap_list_t *l_ins_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_IN, NULL);
+    if (!l_ins_list){
+        log_it(L_ERROR, "Can't get inputs from tx");
+        return -1;
+    }
+    dap_list_t *l_in_temp = l_ins_list;
+    while(l_in_temp){
+        dap_chain_tx_in_t *l_tx_in = (dap_chain_tx_in_t *)l_in_temp->data;
+        if (s_datum_tx_voting_coin_check_spent(a_ledger->net, l_vote_tx_item->voting_hash, l_tx_in->header.tx_prev_hash, l_tx_in->header.tx_out_prev_idx) == 0){
+            dap_chain_datum_tx_t *l_tx_prev_temp = dap_ledger_tx_find_by_hash(a_ledger, &l_tx_in->header.tx_prev_hash);
+            int l_out_prev_idx = (int)l_tx_in->header.tx_out_prev_idx;
+            dap_chain_tx_out_t *l_prev_out_union = (dap_chain_tx_out_t *)dap_chain_datum_tx_out_get_by_out_idx(l_tx_prev_temp, l_out_prev_idx);
+            if (!l_prev_out_union){
+                l_in_temp = l_in_temp->next;
+                continue;
+            }
 
-                switch (l_prev_out_union->header.type) {
-                case TX_ITEM_TYPE_OUT:
-                case TX_ITEM_TYPE_OUT_EXT:
-                    SUM_256_256(l_weight, l_prev_out_union->header.value, &l_weight);
-                }
+            switch (l_prev_out_union->header.type) {
+            case TX_ITEM_TYPE_OUT:
+            case TX_ITEM_TYPE_OUT_EXT:
+                SUM_256_256(l_weight, l_prev_out_union->header.value, &l_weight);
             }
-            l_in_temp = l_in_temp->next;
         }
-        dap_list_free(l_ins_list);
+        l_in_temp = l_in_temp->next;
+    }
+    dap_list_free(l_ins_list);
 
-        if (IS_ZERO_256(l_weight)){
-            log_it(L_ERROR, "No unspent coins");
+    if (IS_ZERO_256(l_weight)){
+        log_it(L_ERROR, "No unspent coins");
+        return false;
+    }
+
+
+    if (a_apply){
+        dap_hash_fast_t pkey_hash = {};
+        dap_chain_tx_sig_t *l_vote_sig = NULL;
+        int l_item_cnt = 0;
+        dap_list_t* l_signs_list = NULL;
+        l_signs_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_SIG, &l_item_cnt);
+
+        if(!l_signs_list){
+            log_it(L_ERROR, "Can't get sign.");
             return false;
         }
+        l_vote_sig = (dap_chain_tx_sig_t *)(dap_list_last(l_signs_list)->data);
+        dap_sign_get_pkey_hash((dap_sign_t*)l_vote_sig->sig, &pkey_hash);
 
+        dap_chain_net_vote_t *l_vote_item = DAP_NEW_Z(dap_chain_net_vote_t);
+        if (!l_vote_item){
+            log_it(L_CRITICAL, "Memory allocate_error!");
+            dap_list_free(l_signs_list);
+            return false;
+        }
+        l_vote_item->vote_hash = *a_tx_hash;
+        l_vote_item->pkey_hash = pkey_hash;
+        l_vote_item->answer_idx = l_vote_tx_item->answer_idx;
+        l_vote_item->weight = l_weight;
 
-        if (a_apply){
-            dap_hash_fast_t pkey_hash = {};
-            dap_chain_tx_sig_t *l_vote_sig = NULL;
-            int l_item_cnt = 0;
-            dap_list_t* l_signs_list = NULL;
-            l_signs_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_SIG, &l_item_cnt);
+        dap_list_t *l_temp = l_voting->votes;
+        while(l_temp){
+            if (dap_hash_fast_compare(&((dap_chain_net_vote_t *)l_temp->data)->pkey_hash, &pkey_hash)){
+                if(l_voting->voting_params.vote_changing_allowed_offset &&
+                    *(bool*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.vote_changing_allowed_offset)){
 
-            if(!l_signs_list){
-                log_it(L_ERROR, "Can't get sign.");
-                return false;
-            }
-            l_vote_sig = (dap_chain_tx_sig_t *)(dap_list_last(l_signs_list)->data);
-            dap_sign_get_pkey_hash((dap_sign_t*)l_vote_sig->sig, &pkey_hash);
+                    l_voting->votes = dap_list_append(l_voting->votes, l_vote_item);
 
-            dap_chain_net_vote_t *l_vote_item = DAP_NEW_Z(dap_chain_net_vote_t);
-            if (!l_vote_item){
-                log_it(L_CRITICAL, "Memory allocate_error!");
-                dap_list_free(l_signs_list);
-                return false;
-            }
-            l_vote_item->vote_hash = l_hash;
-            l_vote_item->pkey_hash = pkey_hash;
-            l_vote_item->answer_idx = l_vote_tx_item->answer_idx;
-            l_vote_item->weight = l_weight;
-
-            dap_list_t *l_temp = l_voting->votes;
-            while(l_temp){
-                if (dap_hash_fast_compare(&((dap_chain_net_vote_t *)l_temp->data)->pkey_hash, &pkey_hash)){
-                    if(l_voting->voting_params.vote_changing_allowed_offset &&
-                        *(bool*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.vote_changing_allowed_offset)){
-
-                        l_voting->votes = dap_list_append(l_voting->votes, l_vote_item);
-
-                        log_it(L_ERROR, "Vote is changed.");
-                        dap_list_free(l_signs_list);
-                        return true;
-                    } else {
-                        log_it(L_ERROR, "The voting don't allow change your vote.");
-                        dap_list_free(l_signs_list);
-                        DAP_DELETE(l_vote_item);
-                        return false;
-                    }
+                    log_it(L_ERROR, "Vote is changed.");
+                    dap_list_free(l_signs_list);
+                    return true;
+                } else {
+                    log_it(L_ERROR, "The voting don't allow change your vote.");
+                    dap_list_free(l_signs_list);
+                    DAP_DELETE(l_vote_item);
+                    return false;
                 }
-                l_temp = l_temp->next;
             }
-            dap_list_free(l_signs_list);
-            log_it(L_INFO, "Vote is accepted.");
-            l_voting->votes = dap_list_append(l_voting->votes, l_vote_item);
+            l_temp = l_temp->next;
         }
-        return true;
-    } else {
-        log_it(L_ERROR, "Item is not supported in votings.");
+        dap_list_free(l_signs_list);
+        log_it(L_INFO, "Vote is accepted.");
+        l_voting->votes = dap_list_append(l_voting->votes, l_vote_item);
     }
+    return true;
+}
 
-    return false;
+int s_datum_tx_voting_verification_callback(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, bool a_apply)
+{
+    if (a_type == TX_ITEM_TYPE_VOTING)
+        s_voting_verificator(a_ledger, a_type, a_tx_in, a_tx_hash, a_apply);
+    else if (a_type == TX_ITEM_TYPE_VOTE)
+        s_vote_verificator(a_ledger, a_type, a_tx_in, a_tx_hash, a_apply);
+    else
+        log_it(L_ERROR, "Item %d is not supported in votings", a_type);
+
+    return -3;
 }
 
 static bool s_datum_tx_voting_verification_delete_callback(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in)
-- 
GitLab