From bd31210f55efd023cb67af834a3468ea48e2e933 Mon Sep 17 00:00:00 2001 From: Roman Khlopkov <roman.khlopkov@demlabs.net> Date: Wed, 12 Mar 2025 04:58:46 +0000 Subject: [PATCH] port-15147 --- modules/datum/dap_chain_datum_tx_voting.c | 33 +- .../datum/include/dap_chain_datum_tx_tsd.h | 1 + .../datum/include/dap_chain_datum_tx_voting.h | 9 +- modules/ledger/dap_chain_ledger.c | 290 ++++----- modules/ledger/dap_chain_ledger_tx.c | 575 +++++++++++------- modules/ledger/include/dap_chain_ledger.h | 48 +- modules/net/dap_chain_net_tx.c | 12 +- modules/net/dap_chain_node.c | 100 ++- modules/node-cli/dap_chain_node_cli_cmd_tx.c | 22 +- .../dap_chain_net_srv_stake_pos_delegate.c | 9 +- .../service/voting/dap_chain_net_srv_voting.c | 387 ++++++------ .../voting/include/dap_chain_net_srv_voting.h | 11 +- 12 files changed, 832 insertions(+), 665 deletions(-) diff --git a/modules/datum/dap_chain_datum_tx_voting.c b/modules/datum/dap_chain_datum_tx_voting.c index cc09e07e1e..901a1cac67 100644 --- a/modules/datum/dap_chain_datum_tx_voting.c +++ b/modules/datum/dap_chain_datum_tx_voting.c @@ -31,7 +31,7 @@ dap_chain_datum_tx_voting_params_t *dap_chain_datum_tx_voting_parse_tsd(dap_chai { if (!a_tx) return NULL; - dap_chain_datum_tx_voting_params_t *l_voting_parms = DAP_NEW_Z(dap_chain_datum_tx_voting_params_t); + dap_chain_datum_tx_voting_params_t *l_voting_params = DAP_NEW_Z(dap_chain_datum_tx_voting_params_t); char *l_buf_string; byte_t *l_item; size_t l_tx_item_size; TX_ITEM_ITER_TX(l_item, l_tx_item_size, a_tx) { @@ -41,29 +41,31 @@ dap_chain_datum_tx_voting_params_t *dap_chain_datum_tx_voting_parse_tsd(dap_chai switch(l_tsd->type){ case VOTING_TSD_TYPE_QUESTION: l_buf_string = DAP_NEW_Z_SIZE(char, l_tsd->size + 1); - l_voting_parms->question = memcpy(l_buf_string, l_tsd->data, l_tsd->size); + l_voting_params->question = memcpy(l_buf_string, l_tsd->data, l_tsd->size); break; case VOTING_TSD_TYPE_OPTION: l_buf_string = DAP_NEW_Z_SIZE(char, l_tsd->size + 1); - l_voting_parms->options = dap_list_append(l_voting_parms->options, memcpy(l_buf_string, l_tsd->data, l_tsd->size)); + l_voting_params->options = dap_list_append(l_voting_params->options, memcpy(l_buf_string, l_tsd->data, l_tsd->size)); break; case VOTING_TSD_TYPE_EXPIRE: - l_voting_parms->voting_expire = *(dap_time_t*)l_tsd->data; + l_voting_params->voting_expire = *(dap_time_t*)l_tsd->data; break; case VOTING_TSD_TYPE_MAX_VOTES_COUNT: - l_voting_parms->votes_max_count = *(uint64_t*)l_tsd->data; + l_voting_params->votes_max_count = *(uint64_t*)l_tsd->data; break; case VOTING_TSD_TYPE_DELEGATED_KEY_REQUIRED: - l_voting_parms->delegate_key_required = *l_tsd->data; + l_voting_params->delegate_key_required = *l_tsd->data; break; case VOTING_TSD_TYPE_VOTE_CHANGING_ALLOWED: - l_voting_parms->vote_changing_allowed = *l_tsd->data; + l_voting_params->vote_changing_allowed = *l_tsd->data; break; + case VOTING_TSD_TYPE_TOKEN: + strcpy(l_voting_params->token_ticker, (char *)l_tsd->data); default: break; } } - return l_voting_parms; + return l_voting_params; } void dap_chain_datum_tx_voting_params_delete(dap_chain_datum_tx_voting_params_t *a_params) @@ -131,6 +133,18 @@ dap_chain_tx_tsd_t* dap_chain_datum_voting_vote_changing_allowed_tsd_create(bool return l_tsd; } +dap_chain_tx_tsd_t *dap_chain_datum_voting_token_tsd_create(const char *a_token_ticker) +{ + dap_return_val_if_fail(a_token_ticker && *a_token_ticker, NULL); + size_t l_ticker_len = strlen(a_token_ticker); + if (l_ticker_len >= DAP_CHAIN_TICKER_SIZE_MAX) { + log_it(L_ERROR, "Ticker len %zu is too big", l_ticker_len); + return NULL; + } + dap_chain_tx_tsd_t *l_tsd = dap_chain_datum_tx_item_tsd_create((char *)a_token_ticker, VOTING_TSD_TYPE_TOKEN, l_ticker_len); + return l_tsd; +} + 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) { dap_chain_tx_voting_tx_cond_t l_temp = { @@ -184,6 +198,9 @@ json_object *dap_chain_datum_tx_item_voting_tsd_to_json(dap_chain_datum_tx_t* a_ case VOTING_TSD_TYPE_OPTION: json_object_array_add(l_answer_array_object, json_object_new_string_len((char*)l_tsd->data, l_tsd->size)); break; + case VOTING_TSD_TYPE_TOKEN: + json_object_object_add(l_object, "token", json_object_new_string_len((char*)l_tsd->data, l_tsd->size)); + break; case VOTING_TSD_TYPE_EXPIRE: json_object_object_add(l_object, "exired", json_object_new_uint64(*(uint64_t*)l_tsd->data)); break; diff --git a/modules/datum/include/dap_chain_datum_tx_tsd.h b/modules/datum/include/dap_chain_datum_tx_tsd.h index d63d96d244..f1faa200d6 100644 --- a/modules/datum/include/dap_chain_datum_tx_tsd.h +++ b/modules/datum/include/dap_chain_datum_tx_tsd.h @@ -7,6 +7,7 @@ #define DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TICKER 0xf001 #define DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TX_HASH 0xf002 #define DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TRACKER 0xf0fa +#define DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_VOTING_HASH 0xf0fe typedef struct dap_chain_tx_tsd { struct { diff --git a/modules/datum/include/dap_chain_datum_tx_voting.h b/modules/datum/include/dap_chain_datum_tx_voting.h index e2376921e2..264b536140 100644 --- a/modules/datum/include/dap_chain_datum_tx_voting.h +++ b/modules/datum/include/dap_chain_datum_tx_voting.h @@ -39,7 +39,8 @@ typedef enum dap_chain_datum_voting_tsd_type { VOTING_TSD_TYPE_DELEGATED_KEY_REQUIRED, VOTING_TSD_TYPE_VOTE_CHANGING_ALLOWED, VOTING_TSD_TYPE_VOTE_TX_COND, - VOTING_TSD_TYPE_VOTE + VOTING_TSD_TYPE_TOKEN, + VOTING_TSD_TYPE_VOTE = 0xfa // hardfork related } dap_chain_datum_voting_tsd_type_t; typedef struct dap_chain_tx_voting { @@ -58,12 +59,13 @@ typedef struct dap_chain_tx_vote { } DAP_ALIGN_PACKED dap_chain_tx_vote_t; typedef struct dap_chain_datum_tx_voting_params { - char *question; + char *question; dap_list_t *options; dap_time_t voting_expire; uint64_t votes_max_count; bool delegate_key_required; bool vote_changing_allowed; + char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX]; } dap_chain_datum_tx_voting_params_t; #ifdef __cplusplus @@ -80,6 +82,7 @@ dap_chain_tx_tsd_t* dap_chain_datum_voting_max_votes_count_tsd_create(uint64_t a 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); +dap_chain_tx_tsd_t *dap_chain_datum_voting_token_tsd_create(const char *a_token_ticker); dap_chain_tx_voting_t *dap_chain_datum_tx_item_voting_create(void); json_object *dap_chain_datum_tx_item_voting_tsd_to_json(dap_chain_datum_tx_t* a_tx); @@ -91,4 +94,4 @@ char *dap_chain_datum_tx_voting_get_answer_text_by_idx(dap_chain_datum_tx_t *a_t #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/modules/ledger/dap_chain_ledger.c b/modules/ledger/dap_chain_ledger.c index fe17bfa4da..2249b14b5e 100644 --- a/modules/ledger/dap_chain_ledger.c +++ b/modules/ledger/dap_chain_ledger.c @@ -1171,51 +1171,6 @@ uint256_t dap_ledger_calc_balance(dap_ledger_t *a_ledger, const dap_chain_addr_t return false; } -/** - * Get the transaction in the cache by the public key that signed the transaction, - * starting from the next hash after a_tx_first_hash - * - * a_public_key[in] public key that signed the transaction - * a_public_key_size[in] public key size - * a_tx_first_hash [in/out] hash of the initial transaction/ found transaction, if 0 start from the beginning - */ -const dap_chain_datum_tx_t* dap_ledger_tx_find_by_pkey(dap_ledger_t *a_ledger, - char *a_public_key, size_t a_public_key_size, dap_chain_hash_fast_t *a_tx_first_hash) -{ - if(!a_public_key || !a_tx_first_hash) - return NULL; - dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); - dap_chain_datum_tx_t *l_cur_tx = NULL; - bool is_null_hash = dap_hash_fast_is_blank(a_tx_first_hash); - bool is_search_enable = is_null_hash; - dap_ledger_tx_item_t *l_iter_current, *l_item_tmp; - pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); - HASH_ITER(hh, l_ledger_pvt->ledger_items , l_iter_current, l_item_tmp) { - dap_chain_datum_tx_t *l_tx_tmp = l_iter_current->tx; - dap_chain_hash_fast_t *l_tx_hash_tmp = &l_iter_current->tx_hash_fast; - // start searching from the next hash after a_tx_first_hash - if (!is_search_enable) { - is_search_enable = dap_hash_fast_compare(l_tx_hash_tmp, a_tx_first_hash); - continue; - } - // Get sign item from transaction - dap_chain_tx_sig_t *l_tx_sig = (dap_chain_tx_sig_t*) dap_chain_datum_tx_item_get(l_tx_tmp, NULL, - NULL, TX_ITEM_TYPE_SIG, NULL); - // Get dap_sign_t from item - dap_sign_t *l_sig = dap_chain_datum_tx_item_sig_get_sign(l_tx_sig); - if(l_sig) { - // compare public key in transaction with a_public_key - if(a_public_key_size == l_sig->header.sign_pkey_size && - !memcmp(a_public_key, l_sig->pkey_n_sign, a_public_key_size)) { - l_cur_tx = l_tx_tmp; - memcpy(a_tx_first_hash, l_tx_hash_tmp, sizeof(dap_chain_hash_fast_t)); - break; - } - } - } - pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); - return l_cur_tx; -} /** * @brief dap_ledger_find_pkey_by_hash * @param a_ledger to search @@ -1274,92 +1229,6 @@ dap_list_t* dap_ledger_tx_cache_find_out_cond_all(dap_ledger_t *a_ledger, dap_ch return l_ret; } -/** - * Get the transaction in the cache with the out_cond item - * - * a_addr[in] wallet address, whose owner can use the service - */ -dap_chain_datum_tx_t* dap_ledger_tx_cache_find_out_cond(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_cond_type, - dap_chain_hash_fast_t *a_tx_first_hash, dap_chain_tx_out_cond_t **a_out_cond, int *a_out_cond_idx, char *a_token_ticker) -{ - if (!a_tx_first_hash) - return NULL; - dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); - dap_chain_datum_tx_t *l_cur_tx = NULL; - bool is_null_hash = dap_hash_fast_is_blank(a_tx_first_hash); - bool is_search_enable = is_null_hash; - dap_ledger_tx_item_t *l_iter_current = NULL, *l_item_tmp = NULL; - dap_chain_tx_out_cond_t *l_tx_out_cond = NULL; - int l_tx_out_cond_idx = 0; - pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); - HASH_ITER(hh, l_ledger_pvt->ledger_items, l_iter_current, l_item_tmp) { - dap_chain_datum_tx_t *l_tx_tmp = l_iter_current->tx; - dap_chain_hash_fast_t *l_tx_hash_tmp = &l_iter_current->tx_hash_fast; - // start searching from the next hash after a_tx_first_hash - if (!is_search_enable) { - is_search_enable = dap_hash_fast_compare(l_tx_hash_tmp, a_tx_first_hash); - continue; - } - // Get out_cond item from transaction - l_tx_out_cond = dap_chain_datum_tx_out_cond_get(l_tx_tmp, a_cond_type, &l_tx_out_cond_idx); - - if(l_tx_out_cond) { - l_cur_tx = l_tx_tmp; - memcpy(a_tx_first_hash, l_tx_hash_tmp, sizeof(dap_chain_hash_fast_t)); - if (a_token_ticker) { - strcpy(a_token_ticker, l_iter_current->cache_data.token_ticker); - } - break; - } - } - pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); - if (a_out_cond) { - *a_out_cond = l_tx_out_cond; - } - if (a_out_cond_idx) { - *a_out_cond_idx = l_tx_out_cond_idx; - } - return l_cur_tx; -} - -/** - * Get the value from all transactions in the cache with out_cond item - * - * a_addr[in] wallet address, whose owner can use the service - * a_sign [in] signature of a_addr hash for check valid key - * a_sign_size [in] signature size - * - * a_public_key[in] public key that signed the transaction - * a_public_key_size[in] public key size - */ -uint256_t dap_ledger_tx_cache_get_out_cond_value(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_cond_type, - dap_chain_addr_t *a_addr, dap_chain_tx_out_cond_t **tx_out_cond) - -{ - uint256_t l_ret_value = {}; - - dap_chain_datum_tx_t *l_tx_tmp; - dap_chain_hash_fast_t l_tx_first_hash = { 0 }; // start hash - /* size_t l_pub_key_size = a_key_from->pub_key_data_size; - uint8_t *l_pub_key = dap_enc_key_serialize_pub_key(a_key_from, &l_pub_key_size);*/ - dap_chain_tx_out_cond_t *l_tx_out_cond; - // Find all transactions - do { - l_tx_tmp = dap_ledger_tx_cache_find_out_cond(a_ledger, a_cond_type, &l_tx_first_hash, &l_tx_out_cond, NULL, NULL); - // Get out_cond item from transaction - if(l_tx_tmp) { - UNUSED(a_addr); - // TODO check relations a_addr with cond_data and public key - if(l_tx_out_cond) { - l_ret_value = l_tx_out_cond->header.value; - if(tx_out_cond) - *tx_out_cond = l_tx_out_cond; - } - } - } while(l_tx_tmp); - return l_ret_value; -} - dap_list_t *dap_ledger_get_list_tx_outs(dap_ledger_t *a_ledger, const char *a_token_ticker, const dap_chain_addr_t *a_addr_from, uint256_t *a_value_transfer) { @@ -1411,7 +1280,8 @@ dap_list_t *dap_ledger_get_list_tx_outs(dap_ledger_t *a_ledger, const char *a_to } } } - if (a_value_transfer) *a_value_transfer = l_value_transfer; + if (a_value_transfer) + *a_value_transfer = l_value_transfer; return l_list_used_out; } @@ -1498,7 +1368,7 @@ dap_chain_datum_tx_t *dap_ledger_datum_iter_get_last(dap_ledger_datum_iter_t *a_ { dap_ledger_private_t *l_ledger_pvt = PVT(a_iter->net->pub.ledger); pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); - a_iter->cur_ledger_tx_item = l_ledger_pvt->ledger_items->hh.prev; + a_iter->cur_ledger_tx_item = HASH_LAST(l_ledger_pvt->ledger_items); a_iter->cur = ((dap_ledger_tx_item_t *)(a_iter->cur_ledger_tx_item))->tx; a_iter->cur_hash = ((dap_ledger_tx_item_t *)(a_iter->cur_ledger_tx_item))->tx_hash_fast; a_iter->is_unspent = ((dap_ledger_tx_item_t *)(a_iter->cur_ledger_tx_item))->cache_data.ts_spent ? false : true; @@ -1531,69 +1401,125 @@ dap_chain_tx_used_out_item_t *dap_ledger_get_tx_cond_out(dap_ledger_t *a_ledger, /** - * @brief dap_ledger_get_list_tx_cond_outs_with_val - * @param a_ledger - * @param a_token_ticker - * @param a_addr_from - * @param a_subtype - * @param a_value_need - * @param a_value_transfer - * @return + * Get the transaction in the cache by the addr in sig item + * + * a_addr[in] public key that signed the transaction + * a_tx_first_hash [in/out] hash of the initial transaction/ found transaction, if 0 start from the beginning */ -dap_list_t *dap_ledger_get_list_tx_cond_outs_with_val(dap_ledger_t *a_ledger, const char *a_token_ticker, const dap_chain_addr_t *a_addr_from, - dap_chain_tx_out_cond_subtype_t a_subtype, uint256_t a_value_need, uint256_t *a_value_transfer) + +dap_chain_tx_out_cond_t *dap_ledger_out_cond_unspent_find_by_addr(dap_ledger_t *a_ledger, const char *a_token, dap_chain_tx_out_cond_subtype_t a_subtype, + const dap_chain_addr_t *a_addr, dap_chain_hash_fast_t *a_tx_first_hash, int *a_out_idx) { - dap_list_t *l_list_used_out = NULL; // list of transaction with 'out' items - dap_chain_hash_fast_t l_tx_cur_hash = { 0 }; - uint256_t l_value_transfer = { }; - dap_chain_datum_tx_t *l_tx; - while( compare256(l_value_transfer, a_value_need) == -1 - && ( l_tx = dap_ledger_tx_find_by_addr(a_ledger, a_token_ticker, a_addr_from, &l_tx_cur_hash)) ) - { - byte_t *it; size_t l_size; int i, l_out_idx_tmp = -1; - TX_ITEM_ITER_TX_TYPE(it, TX_ITEM_TYPE_OUT_COND, l_size, i, l_tx) { - ++l_out_idx_tmp; - dap_chain_tx_out_cond_t *l_out_cond = (dap_chain_tx_out_cond_t*) it; - if (a_subtype != l_out_cond->header.subtype || IS_ZERO_256(l_out_cond->header.value) ) - continue; - dap_chain_tx_used_out_item_t *l_item = DAP_NEW_Z(dap_chain_tx_used_out_item_t); - *l_item = (dap_chain_tx_used_out_item_t) { l_tx_cur_hash, (uint32_t)l_out_idx_tmp, l_out_cond->header.value }; - l_list_used_out = dap_list_append(l_list_used_out, l_item); - SUM_256_256(l_value_transfer, l_item->value, &l_value_transfer); - // already accumulated the required value, finish the search for 'out' items - if ( compare256(l_value_transfer, a_value_need) != -1 ) { - break; + if (!a_addr || !a_token) + return NULL; + dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); + dap_chain_tx_out_cond_t *ret = NULL; + dap_ledger_tx_item_t *l_iter_start = NULL, *it; + pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); + if (a_tx_first_hash && !dap_hash_fast_is_blank(a_tx_first_hash)) { + HASH_FIND(hh, l_ledger_pvt->ledger_items, a_tx_first_hash, sizeof(dap_hash_t), l_iter_start); + if (!l_iter_start || !l_iter_start->hh.next) { + pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); + return NULL; + } + // start searching from the next hash after a_tx_first_hash + l_iter_start = l_iter_start->hh.next; + } else + l_iter_start = l_ledger_pvt->ledger_items; + for (it = l_iter_start; it; it = it->hh.next, ret = NULL) { + // If a_token is setup we check if its not our token - miss it + if (*it->cache_data.token_ticker && dap_strcmp(it->cache_data.token_ticker, a_token)) + continue; + ret = NULL; + // Get 'out_cond' item from transaction + byte_t *l_item; size_t l_size; int i, l_out_idx = 0; + TX_ITEM_ITER_TX_TYPE(l_item, TX_ITEM_TYPE_OUT_ALL, l_size, i, it->tx) { + if (*l_item == TX_ITEM_TYPE_OUT_COND) { + dap_chain_tx_out_cond_subtype_t l_subtype = ((dap_chain_tx_out_cond_t *)l_item)->header.subtype; + if (l_subtype == a_subtype || + (a_subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_ALL && l_subtype != DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE)) { + ret = (dap_chain_tx_out_cond_t *)l_item; + break; + } } + l_out_idx++; + } + // Don't return regular tx or spent conditions + if (!ret || !dap_hash_fast_is_blank(&it->out_metadata[l_out_idx].tx_spent_hash_fast)) + continue; + dap_hash_fast_t l_owner_tx_hash = dap_ledger_get_first_chain_tx_hash(a_ledger, a_subtype, &it->tx_hash_fast); + dap_chain_datum_tx_t *l_tx = dap_hash_fast_is_blank(&l_owner_tx_hash) ? it->tx + : dap_ledger_tx_find_by_hash(a_ledger, &l_owner_tx_hash); + if (!l_tx) { + log_it(L_ERROR, "Can't find owner for tx %s", dap_hash_fast_to_str_static(&it->tx_hash_fast)); + continue; + } + // Get sign item from transaction + dap_chain_tx_sig_t *l_tx_sig = (dap_chain_tx_sig_t*) dap_chain_datum_tx_item_get(l_tx, NULL, NULL, TX_ITEM_TYPE_SIG, NULL); + // Get dap_sign_t from item + dap_sign_t *l_sign = dap_chain_datum_tx_item_sig_get_sign(l_tx_sig); + // compare public key in transaction with a_public_key + dap_chain_hash_fast_t l_sign_hash = {}; + dap_sign_get_pkey_hash(l_sign, &l_sign_hash); + if (dap_hash_fast_compare(&l_sign_hash, &a_addr->data.hash_fast)) { + if (a_tx_first_hash) + *a_tx_first_hash = it->tx_hash_fast; + if (a_out_idx) + *a_out_idx = l_out_idx; + break; } } - return compare256(l_value_transfer, a_value_need) > -1 && l_list_used_out - ? ({ if (a_value_transfer) *a_value_transfer = l_value_transfer; l_list_used_out; }) - : ( dap_list_free_full(l_list_used_out, NULL), NULL ); + pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); + return ret; } -dap_list_t *dap_ledger_get_list_tx_cond_outs(dap_ledger_t *a_ledger, const dap_chain_addr_t *a_addr_from) +dap_list_t *dap_ledger_get_list_tx_cond_outs(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_subtype, + const char *a_token_ticker, const dap_chain_addr_t *a_addr_from) { dap_list_t *l_list_used_out = NULL; // list of transaction with 'out' items dap_chain_hash_fast_t l_tx_cur_hash = { }; - dap_chain_datum_tx_t *l_tx; - while(( l_tx = dap_ledger_tx_find_by_addr(a_ledger, a_ledger->net->pub.native_ticker, a_addr_from, &l_tx_cur_hash) )) { - byte_t *it; size_t l_size; int i, l_out_idx_tmp = -1; - TX_ITEM_ITER_TX_TYPE(it, TX_ITEM_TYPE_OUT_COND, l_size, i, l_tx) { - ++l_out_idx_tmp; - dap_chain_tx_out_cond_t *l_out_cond = (dap_chain_tx_out_cond_t *)it; - if ( DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE == l_out_cond->header.subtype || IS_ZERO_256(l_out_cond->header.value) ) - continue; - if (dap_ledger_tx_hash_is_used_out_item(a_ledger, &l_tx_cur_hash, l_out_idx_tmp, NULL)) - continue; // TODO Move this check to dap_ledger_tx_find_by_addr() to avoid double search - dap_chain_tx_used_out_item_t *l_item = DAP_NEW_Z(dap_chain_tx_used_out_item_t); - *l_item = (dap_chain_tx_used_out_item_t) { l_tx_cur_hash, (uint32_t)l_out_idx_tmp, l_out_cond->header.value }; - l_list_used_out = dap_list_append(l_list_used_out, l_item); - } + int l_out_idx; + dap_chain_tx_out_cond_t *l_cond; + while ( (l_cond = dap_ledger_out_cond_unspent_find_by_addr(a_ledger, a_token_ticker, a_subtype, a_addr_from, &l_tx_cur_hash, &l_out_idx)) ) { + dap_chain_tx_used_out_item_t *l_item = DAP_NEW_Z(dap_chain_tx_used_out_item_t); + *l_item = (dap_chain_tx_used_out_item_t) { l_tx_cur_hash, (uint32_t)l_out_idx, l_cond->header.value }; + l_list_used_out = dap_list_append(l_list_used_out, l_item); } - return l_list_used_out; } +bool dap_ledger_check_condition_owner(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_hash, dap_chain_tx_out_cond_subtype_t a_cond_subtype, + int a_out_idx, dap_sign_t *a_owner_sign) +{ + dap_return_val_if_fail(a_ledger && a_tx_hash && a_owner_sign, false); + // Get first tx + dap_chain_datum_tx_t *l_check_tx = dap_ledger_tx_find_by_hash(a_ledger, a_tx_hash); + if (!l_check_tx) { + log_it(L_ERROR, "Can't find tx %s", dap_hash_fast_to_str_static(a_tx_hash)); + return false; + } + if (!dap_chain_datum_tx_out_cond_get(l_check_tx, a_cond_subtype, NULL)) { + log_it(L_ERROR, "Can't find owner for tx %s", dap_hash_fast_to_str_static(a_tx_hash)); + return false; + } + dap_hash_fast_t l_first_tx_hash = dap_ledger_get_first_chain_tx_hash(a_ledger, a_cond_subtype, a_tx_hash); + dap_chain_datum_tx_t *l_first_tx = dap_hash_fast_is_blank(&l_first_tx_hash) ? l_check_tx + : dap_ledger_tx_find_by_hash(a_ledger, &l_first_tx_hash); + if (!l_first_tx) { + log_it(L_ERROR, "Can't find owner tx %s", dap_hash_fast_to_str_static(&l_first_tx_hash)); + return false; + } + dap_chain_tx_sig_t *l_first_tx_sig = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get(l_first_tx, NULL, NULL, TX_ITEM_TYPE_SIG, NULL); + dap_sign_t *l_sign = dap_chain_datum_tx_item_sig_get_sign((dap_chain_tx_sig_t *)l_first_tx_sig); + if (!l_sign) { + log_it(L_ERROR, "Can't find signature for tx %s", dap_hash_fast_to_str_static(&l_first_tx_hash)); + return false; + } + return dap_sign_compare_pkeys(a_owner_sign, l_sign); +} + + + bool dap_ledger_cache_enabled(dap_ledger_t *a_ledger) { return is_ledger_cached(PVT(a_ledger)); diff --git a/modules/ledger/dap_chain_ledger_tx.c b/modules/ledger/dap_chain_ledger_tx.c index 1706654159..bdfbb6d2d2 100644 --- a/modules/ledger/dap_chain_ledger_tx.c +++ b/modules/ledger/dap_chain_ledger_tx.c @@ -44,6 +44,7 @@ typedef struct dap_ledger_verificator { typedef struct dap_chain_ledger_votings_callbacks { dap_ledger_voting_callback_t voting_callback; + dap_ledger_vote_callback_t vote_callback; dap_ledger_voting_delete_callback_t voting_delete_callback; dap_ledger_voting_expire_callback_t voting_expire_callback; } dap_chain_ledger_votings_callbacks_t; @@ -57,7 +58,7 @@ static dap_ledger_tax_callback_t s_tax_callback = NULL; typedef struct dap_ledger_tokenizer { char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX]; uint256_t sum; - UT_hash_handle hh; + struct dap_ledger_tokenizer *next; } dap_ledger_tokenizer_t; typedef struct dap_ledger_tx_bound { @@ -105,10 +106,9 @@ typedef struct dap_ledger_cache_gdb_record { uint8_t data[]; } DAP_ALIGN_PACKED dap_ledger_cache_gdb_record_t; -static dap_ledger_tx_item_t *s_tx_item_find_by_addr(dap_ledger_t *a_ledger, const dap_chain_addr_t *a_addr, const char * a_token, dap_chain_hash_fast_t *a_tx_first_hash); static bool s_ledger_tx_hash_is_used_out_item(dap_ledger_tx_item_t *a_item, uint32_t a_idx_out, dap_hash_fast_t *a_out_spender_hash); static dap_ledger_stake_lock_item_t *s_emissions_for_stake_lock_item_find(dap_ledger_t *a_ledger, const dap_chain_hash_fast_t *a_token_emission_hash); - +void dap_ledger_colour_clear_callback(void *a_list_data); static dap_chain_datum_tx_t *s_tx_find_by_hash(dap_ledger_t *a_ledger, const dap_chain_hash_fast_t *a_tx_hash, dap_ledger_tx_item_t **a_item_out, bool a_unspent_only); static struct json_object *s_wallet_info_json_collect(dap_ledger_t *a_ledger, dap_ledger_wallet_balance_t* a_bal); @@ -214,6 +214,22 @@ bool dap_ledger_is_used_reward(dap_ledger_t *a_ledger, dap_hash_fast_t *a_block_ return s_find_reward(a_ledger, &l_search_key); } +dap_ledger_tokenizer_t *s_tokenizer_find(dap_ledger_tokenizer_t *a_list, const char *a_ticker) +{ + for (dap_ledger_tokenizer_t *it = a_list; it; it = it->next) + if (!dap_strcmp(it->token_ticker, a_ticker)) + return it; + return NULL; +} + +size_t s_tokenizer_count(dap_ledger_tokenizer_t *a_list) +{ + size_t ret = 0; + for (dap_ledger_tokenizer_t *it = a_list; it; it = it->next) + ret++; + return ret; +} + /** * Checking a new transaction before adding to the cache * @@ -227,6 +243,7 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger, dap_list_t **a_list_bound_items, dap_list_t **a_list_tx_out, char *a_main_ticker, + dap_ledger_tokenizer_t **a_values_from_cur_tx, dap_chain_srv_uid_t *a_tag, dap_chain_tx_tag_action_type_t *a_action, bool a_check_for_removing) @@ -775,7 +792,7 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger, break; } } - HASH_FIND_STR(l_values_from_prev_tx, l_token, l_value_cur); + l_value_cur = s_tokenizer_find(l_values_from_prev_tx, l_token); if (!l_value_cur) { l_value_cur = DAP_NEW_Z(dap_ledger_tokenizer_t); if ( !l_value_cur ) { @@ -783,8 +800,8 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger, l_err_num = DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; break; } - strcpy(l_value_cur->token_ticker, l_token); - HASH_ADD_STR(l_values_from_prev_tx, token_ticker, l_value_cur); + dap_strncpy(l_value_cur->token_ticker, l_token, DAP_CHAIN_TICKER_SIZE_MAX); + LL_APPEND(l_values_from_prev_tx, l_value_cur); } // calculate from previous transactions per each token if (SUM_256_256(l_value_cur->sum, l_value, &l_value_cur->sum)) { @@ -800,32 +817,28 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger, if (l_err_num) { if ( l_list_bound_items ) dap_list_free_full(l_list_bound_items, NULL); - HASH_ITER(hh, l_values_from_prev_tx, l_value_cur, l_tmp) { - HASH_DEL(l_values_from_prev_tx, l_value_cur); + LL_FOREACH_SAFE(l_values_from_prev_tx, l_value_cur, l_tmp) DAP_DELETE(l_value_cur); - } return l_err_num; } // 6. Compare sum of values in 'out' items if ( !l_main_ticker ) - switch ( HASH_COUNT(l_values_from_prev_tx) ) { + switch ( s_tokenizer_count(l_values_from_prev_tx) ) { case 1: l_main_ticker = l_value_cur->token_ticker; break; case 2: - HASH_FIND_STR(l_values_from_prev_tx, a_ledger->net->pub.native_ticker, l_value_cur); + l_value_cur = s_tokenizer_find(l_values_from_prev_tx, a_ledger->net->pub.native_ticker); if (l_value_cur) { - l_value_cur = l_value_cur->hh.next ? l_value_cur->hh.next : l_value_cur->hh.prev; + l_value_cur = l_value_cur->next ? l_value_cur->next : l_values_from_prev_tx; l_main_ticker = l_value_cur->token_ticker; } break; default: dap_list_free_full(l_list_bound_items, NULL); - HASH_ITER(hh, l_values_from_prev_tx, l_value_cur, l_tmp) { - HASH_DEL(l_values_from_prev_tx, l_value_cur); + LL_FOREACH_SAFE(l_values_from_prev_tx, l_value_cur, l_tmp) DAP_DELETE(l_value_cur); - } return DAP_LEDGER_TX_CHECK_NO_MAIN_TICKER; } @@ -916,7 +929,7 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger, if (l_err_num) break; - HASH_FIND_STR(l_values_from_cur_tx, l_token, l_value_cur); + l_value_cur = s_tokenizer_find(l_values_from_cur_tx, l_token); if (!l_value_cur) { l_value_cur = DAP_NEW_Z(dap_ledger_tokenizer_t); if ( !l_value_cur ) { @@ -925,7 +938,7 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger, break; } dap_strncpy(l_value_cur->token_ticker, l_token, sizeof(l_value_cur->token_ticker)); - HASH_ADD_STR(l_values_from_cur_tx, token_ticker, l_value_cur); + LL_APPEND(l_values_from_cur_tx, l_value_cur); } if (SUM_256_256(l_value_cur->sum, l_value, &l_value_cur->sum)) { debug_if(g_debug_ledger, L_WARNING, "Sum result overflow for tx_add_check with ticker %s", @@ -958,13 +971,13 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger, // Check for transaction consistency (sum(ins) == sum(outs)) if ( !l_err_num && !dap_ledger_datum_is_enforced(a_ledger, a_tx_hash, true) ) { - if ( HASH_COUNT(l_values_from_prev_tx) != HASH_COUNT(l_values_from_cur_tx) ) { - log_it(L_ERROR, "Token tickers IN and OUT mismatch: %u != %u", - HASH_COUNT(l_values_from_prev_tx), HASH_COUNT(l_values_from_cur_tx)); + if ( s_tokenizer_count(l_values_from_prev_tx) != s_tokenizer_count(l_values_from_cur_tx) ) { + log_it(L_ERROR, "Token tickers IN and OUT mismatch: %zu != %zu", + s_tokenizer_count(l_values_from_prev_tx), s_tokenizer_count(l_values_from_cur_tx)); l_err_num = DAP_LEDGER_TX_CHECK_SUM_INS_NOT_EQUAL_SUM_OUTS; } else { - HASH_ITER(hh, l_values_from_prev_tx, l_value_cur, l_tmp) { - HASH_FIND_STR(l_values_from_cur_tx, l_value_cur->token_ticker, l_res); + LL_FOREACH(l_values_from_prev_tx, l_value_cur) { + l_res = s_tokenizer_find(l_values_from_cur_tx, l_value_cur->token_ticker); if ( !l_res || !EQUAL_256(l_res->sum, l_value_cur->sum) ) { if (g_debug_ledger) { char *l_balance = dap_chain_balance_coins_print(l_res ? l_res->sum : uint256_0), @@ -1016,7 +1029,7 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger, // 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 ((l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTING, a_tx, a_tx_hash, false))) { + if ((l_err_num = s_voting_callbacks.voting_callback(a_ledger, a_tx, a_tx_hash, false))) { debug_if(g_debug_ledger, L_WARNING, "Verificator check error %d for voting", l_err_num); l_err_num = DAP_LEDGER_TX_CHECK_VERIFICATOR_CHECK_FAILURE; } @@ -1028,7 +1041,7 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger, *a_action = 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 ((l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTE, a_tx, a_tx_hash, false))) { + if ((l_err_num = s_voting_callbacks.vote_callback(a_ledger, a_tx, a_tx_hash, NULL, false))) { debug_if(g_debug_ledger, L_WARNING, "Verificator check error %d for vote", l_err_num); l_err_num = DAP_LEDGER_TX_CHECK_VERIFICATOR_CHECK_FAILURE; } @@ -1044,14 +1057,15 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger, if (a_main_ticker && !l_err_num && (a_main_ticker != l_main_ticker)) dap_strncpy(a_main_ticker, l_main_ticker, DAP_CHAIN_TICKER_SIZE_MAX); - HASH_ITER(hh, l_values_from_prev_tx, l_value_cur, l_tmp) { - HASH_DEL(l_values_from_prev_tx, l_value_cur); - DAP_DELETE(l_value_cur); - } - HASH_ITER(hh, l_values_from_cur_tx, l_value_cur, l_tmp) { - HASH_DEL(l_values_from_cur_tx, l_value_cur); + LL_FOREACH_SAFE(l_values_from_prev_tx, l_value_cur, l_tmp) DAP_DELETE(l_value_cur); - } + + if (!a_values_from_cur_tx || l_err_num) { + LL_FOREACH_SAFE(l_values_from_cur_tx, l_value_cur, l_tmp) + DAP_DELETE(l_value_cur); + } else + *a_values_from_cur_tx = l_values_from_cur_tx; + if (!a_list_bound_items || l_err_num) { dap_list_free_full(l_list_bound_items, NULL); } else { @@ -1082,7 +1096,7 @@ int dap_ledger_tx_add_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, log_it (L_WARNING, "Inconsistent datum TX: datum size %zu != tx size %zu", a_datum_size, l_tx_size); return DAP_LEDGER_CHECK_INVALID_SIZE; } - int l_ret_check = s_tx_cache_check(a_ledger, a_tx, a_datum_hash, false, NULL, NULL, NULL, NULL, NULL, false); + int l_ret_check = s_tx_cache_check(a_ledger, a_tx, a_datum_hash, false, NULL, NULL, NULL, NULL, NULL, NULL, false); if(g_debug_ledger) { if (l_ret_check) log_it(L_NOTICE, "Ledger TX adding check not passed for TX %s: error %s", @@ -1144,19 +1158,33 @@ static int s_balance_cache_update(dap_ledger_t *a_ledger, dap_ledger_wallet_bala return 0; } +struct tracker_mover_item { + dap_hash_fast_t pkey_hash; + uint256_t coloured_value; + uint256_t cur_value; + struct tracker_mover_item *prev, *next; +}; + struct tracker_mover { dap_hash_fast_t voting_hash; - uint256_t colored_value; - uint256_t cur_value; + char ticker[DAP_CHAIN_TICKER_SIZE_MAX]; + struct tracker_mover_item *items; }; +int s_compare_tracker_items(struct tracker_mover_item *a_item1, struct tracker_mover_item *a_item2) +{ + return memcmp(&a_item1->pkey_hash, &a_item1->pkey_hash, sizeof(dap_hash_fast_t)); +} + int s_compare_trackers(dap_list_t *a_tracker1, dap_list_t *a_tracker2) { struct tracker_mover *l_tracker1 = a_tracker1->data, *l_tracker2 = a_tracker2->data; return memcmp(&l_tracker1->voting_hash, &l_tracker2->voting_hash, sizeof(dap_hash_fast_t)); } -dap_list_t *s_trackers_aggregate(dap_ledger_t *a_ledger, dap_list_t *a_trackers, dap_list_t **a_added, dap_time_t a_ts_creation_time) +dap_list_t *s_trackers_aggregate(dap_ledger_t *a_ledger, dap_list_t *a_trackers, const char *a_ticker, + dap_hash_fast_t *a_voting_hash, dap_hash_fast_t *a_pkey_hash, + dap_list_t **a_added, dap_time_t a_ts_creation_time) { dap_return_val_if_fail(s_voting_callbacks.voting_expire_callback, a_trackers); dap_list_t *it, *tmp; @@ -1165,24 +1193,37 @@ dap_list_t *s_trackers_aggregate(dap_ledger_t *a_ledger, dap_list_t *a_trackers, dap_time_t l_exp_time = s_voting_callbacks.voting_expire_callback(a_ledger, &l_new_tracker->voting_hash); if (a_ts_creation_time > l_exp_time) { DL_DELETE(*a_added, it); - DAP_DEL_MULTY(it->data, it); + dap_ledger_colour_clear_callback(it->data); + DAP_DELETE(it); continue; // Remove expired colour } dap_list_t *l_exists = dap_list_find(a_trackers, &l_new_tracker->voting_hash, s_compare_trackers); struct tracker_mover *l_exists_tracker = NULL; - if (l_exists) - l_exists_tracker = l_exists->data; - else { + if (!l_exists) { l_exists_tracker = DAP_NEW_Z_RET_VAL_IF_FAIL(struct tracker_mover, a_trackers); l_exists_tracker->voting_hash = l_new_tracker->voting_hash; - } - if (SUM_256_256(l_exists_tracker->colored_value, l_new_tracker->colored_value, &l_exists_tracker->colored_value)) { - log_it(L_ERROR, "Tracking value overflow, can't track voting %s anymore", dap_hash_fast_to_str_static(&l_new_tracker->voting_hash)); - return a_trackers; - } - l_exists_tracker->cur_value = l_exists_tracker->colored_value; - if (!l_exists) + dap_strncpy(l_exists_tracker->ticker, a_ticker, DAP_CHAIN_TICKER_SIZE_MAX); a_trackers = dap_list_append(a_trackers, l_exists_tracker); + } else + l_exists_tracker = l_exists->data; + dap_ledger_tracker_item_t *l_item; + DL_FOREACH(l_new_tracker->items, l_item) { + if (a_voting_hash && dap_hash_fast_compare(a_voting_hash, &l_exists_tracker->voting_hash) && + dap_hash_fast_compare(a_pkey_hash, &l_item->pkey_hash)) + continue; + struct tracker_mover_item *l_exists_item, l_sought = { .pkey_hash = l_item->pkey_hash }; + DL_SEARCH(l_exists_tracker->items, l_exists_item, &l_sought, s_compare_tracker_items); + if (!l_exists_item) { + l_exists_item = DAP_NEW_Z_RET_VAL_IF_FAIL(struct tracker_mover_item, a_trackers); + l_exists_item->pkey_hash = l_item->pkey_hash; + DL_APPEND(l_exists_tracker->items, l_exists_item); + } + if (SUM_256_256(l_exists_item->coloured_value, l_item->coloured_value, &l_exists_item->coloured_value)) { + log_it(L_ERROR, "Tracking value overflow, can't track voting %s anymore", dap_hash_fast_to_str_static(&l_new_tracker->voting_hash)); + return a_trackers; + } + l_exists_item->cur_value = l_exists_item->coloured_value; + } } return a_trackers; } @@ -1245,10 +1286,11 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha int l_ret_check; dap_chain_srv_uid_t l_tag = { .uint64 = 0 }; dap_chain_tx_tag_action_type_t l_action = DAP_CHAIN_TX_TAG_ACTION_UNKNOWN; - + dap_ledger_tokenizer_t *l_values_from_cur_tx = NULL; if( (l_ret_check = s_tx_cache_check(a_ledger, a_tx, &l_tx_hash, a_from_threshold, - &l_list_bound_items, &l_list_tx_out, - l_main_token_ticker, &l_tag, &l_action, false))) { + &l_list_bound_items, &l_list_tx_out, + l_main_token_ticker, &l_values_from_cur_tx, + &l_tag, &l_action, false))) { if ((l_ret_check == DAP_CHAIN_CS_VERIFY_CODE_TX_NO_PREVIOUS || l_ret_check == DAP_CHAIN_CS_VERIFY_CODE_TX_NO_EMISSION) && is_ledger_threshld(l_ledger_pvt) && !dap_chain_net_get_load_mode(a_ledger->net)) { @@ -1287,11 +1329,16 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha } int l_err_num = 0; + dap_hash_fast_t l_vote_pkey_hash = { }; + dap_chain_tx_vote_t *l_vote_tx_item = NULL; if (s_voting_callbacks.voting_callback) { if (l_action == DAP_CHAIN_TX_TAG_ACTION_VOTING) - l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTING, a_tx, &l_tx_hash, true); - else if (l_action == DAP_CHAIN_TX_TAG_ACTION_VOTE) - l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTE, a_tx, &l_tx_hash, true); + l_err_num = s_voting_callbacks.voting_callback(a_ledger, a_tx, &l_tx_hash, true); + else if (l_action == DAP_CHAIN_TX_TAG_ACTION_VOTE) { + l_err_num = s_voting_callbacks.vote_callback(a_ledger, a_tx, &l_tx_hash, &l_vote_pkey_hash, true); + l_vote_tx_item = (dap_chain_tx_vote_t *)dap_chain_datum_tx_item_get(a_tx, NULL, NULL, TX_ITEM_TYPE_VOTE, NULL); + assert(l_vote_tx_item); + } } assert(!l_err_num); @@ -1389,10 +1436,13 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha // Gather colour information from previous outputs dap_ledger_tx_item_t *l_prev_item_out = l_bound_item->prev_item; l_prev_item_out->out_metadata[l_bound_item->prev_out_idx].tx_spent_hash_fast = l_tx_hash; - l_trackers_mover = s_trackers_aggregate(a_ledger, l_trackers_mover, - &l_prev_item_out->out_metadata[l_bound_item->prev_out_idx].trackers, a_tx->header.ts_created); - if (l_prev_item_out->cache_data.flags & LEDGER_PVT_TX_META_FLAG_IMMUTABLE) // Clear trackers info for immutable tx's - dap_list_free_full(l_prev_item_out->out_metadata[l_bound_item->prev_out_idx].trackers, NULL); + // Clear trackers info for immutable tx's + if (l_prev_item_out->cache_data.flags & LEDGER_PVT_TX_META_FLAG_IMMUTABLE) + dap_list_free_full(l_prev_item_out->out_metadata[l_bound_item->prev_out_idx].trackers, dap_ledger_colour_clear_callback); + else + l_trackers_mover = s_trackers_aggregate(a_ledger, l_trackers_mover, l_bound_item->in.token_ticker, + l_vote_tx_item ? &l_vote_tx_item->voting_hash : NULL, &l_vote_pkey_hash, + &l_prev_item_out->out_metadata[l_bound_item->prev_out_idx].trackers, a_tx->header.ts_created); // add a used output l_prev_item_out->cache_data.n_outs_used++; if ( is_ledger_cached(l_ledger_pvt) ) { @@ -1418,94 +1468,7 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha l_prev_item_out->cache_data.ts_spent = a_tx->header.ts_created; } - //Update balance : raise - bool l_cross_network = false; - uint32_t l_outs_count = 0; - uint256_t l_native_value = {}; - for (dap_list_t *l_tx_out = l_list_tx_out; l_tx_out; l_tx_out = l_tx_out->next, l_outs_count++) { - assert(l_tx_out->data); - dap_chain_tx_item_type_t l_type = *(uint8_t *)l_tx_out->data; - if (l_type == TX_ITEM_TYPE_OUT_COND) { - // Update service items if any - dap_chain_tx_out_cond_t *l_cond = (dap_chain_tx_out_cond_t *)l_tx_out->data; - dap_ledger_verificator_t *l_verificator = NULL; - int l_tmp = l_cond->header.subtype; - pthread_rwlock_rdlock(&s_verificators_rwlock); - HASH_FIND_INT(s_verificators, &l_tmp, l_verificator); - pthread_rwlock_unlock(&s_verificators_rwlock); - if (l_verificator && l_verificator->callback_out_add) - l_verificator->callback_out_add(a_ledger, a_tx, &l_tx_hash, l_cond); - if (!dap_strcmp(l_main_token_ticker, a_ledger->net->pub.native_ticker)) - SUM_256_256(l_native_value, l_cond->header.value, &l_native_value); - continue; // balance raise will be with next conditional transaction - } - - dap_chain_addr_t *l_addr = NULL; - uint256_t l_value = {}; - switch (l_type) { - case TX_ITEM_TYPE_OUT: { - dap_chain_tx_out_t *l_out_item_256 = (dap_chain_tx_out_t *)l_tx_out->data; - l_addr = &l_out_item_256->addr; - l_value = l_out_item_256->header.value; - l_cur_token_ticker = l_main_token_ticker; - } break; - case TX_ITEM_TYPE_OUT_OLD: { - dap_chain_tx_out_old_t *l_out_item = (dap_chain_tx_out_old_t *)l_tx_out->data; - l_addr = &l_out_item->addr; - l_value = GET_256_FROM_64(l_out_item->header.value); - l_cur_token_ticker = l_main_token_ticker; - } break; - case TX_ITEM_TYPE_OUT_EXT: { - dap_chain_tx_out_ext_t *l_out_item_ext_256 = (dap_chain_tx_out_ext_t *)l_tx_out->data; - l_addr = &l_out_item_ext_256->addr; - l_value = l_out_item_ext_256->header.value; - l_cur_token_ticker = l_out_item_ext_256->token; - } break; - default: - log_it(L_ERROR, "Unknown item type %d", l_type); - break; - } - if (!dap_strcmp(a_ledger->net->pub.native_ticker, l_cur_token_ticker)) - SUM_256_256(l_native_value, l_value, &l_native_value); - assert(l_addr); - if (l_addr->net_id.uint64 != a_ledger->net->pub.id.uint64 && - !dap_chain_addr_is_blank(l_addr)) - l_cross_network = true; - const char *l_addr_str = dap_chain_addr_to_str_static(l_addr); - dap_ledger_wallet_balance_t *wallet_balance = NULL; - char *l_wallet_balance_key = dap_strjoin(" ", l_addr_str, l_cur_token_ticker, (char*)NULL); - debug_if(g_debug_ledger, L_DEBUG, "GOT %s to addr: %s", - dap_uint256_to_char(l_value, NULL), l_wallet_balance_key); - pthread_rwlock_rdlock(&l_ledger_pvt->balance_accounts_rwlock); - HASH_FIND_STR(PVT(a_ledger)->balance_accounts, l_wallet_balance_key, wallet_balance); - pthread_rwlock_unlock(&l_ledger_pvt->balance_accounts_rwlock); - if (wallet_balance) { - SUM_256_256(wallet_balance->balance, l_value, &wallet_balance->balance); - DAP_DELETE (l_wallet_balance_key); - // Update the cache - s_balance_cache_update(a_ledger, wallet_balance); - } else { - wallet_balance = DAP_NEW_Z(dap_ledger_wallet_balance_t); - if (!wallet_balance) { - log_it(L_CRITICAL, "Memory allocation error in s_load_cache_gdb_loaded_txs_callback"); - l_ret = -1; - goto FIN; - } - wallet_balance->key = l_wallet_balance_key; - strcpy(wallet_balance->token_ticker, l_cur_token_ticker); - SUM_256_256(wallet_balance->balance, l_value, &wallet_balance->balance); - if(g_debug_ledger) - log_it(L_DEBUG, "Create new balance item: %s %s", l_addr_str, l_cur_token_ticker); - pthread_rwlock_wrlock(&l_ledger_pvt->balance_accounts_rwlock); - HASH_ADD_KEYPTR(hh, PVT(a_ledger)->balance_accounts, wallet_balance->key, - strlen(l_wallet_balance_key), wallet_balance); - pthread_rwlock_unlock(&l_ledger_pvt->balance_accounts_rwlock); - // Add it to cache - s_balance_cache_update(a_ledger, wallet_balance); - } - } - - // add transaction to the cache list + uint32_t l_outs_count = dap_list_length(l_list_tx_out); dap_ledger_tx_item_t *l_tx_item = DAP_NEW_Z_SIZE(dap_ledger_tx_item_t, sizeof(dap_ledger_tx_item_t) + l_outs_count * sizeof(dap_ledger_tx_out_metadata_t)); if ( !l_tx_item ) { log_it(L_CRITICAL, "%s", c_error_memory_alloc); @@ -1520,85 +1483,165 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha l_tx_item->cache_data.action = l_action; dap_strncpy(l_tx_item->cache_data.token_ticker, l_main_token_ticker, sizeof(l_tx_item->cache_data.token_ticker)); - // Moving colour to new outputs - dap_chain_tx_vote_t *l_vote_tx_item; - if (l_action == DAP_CHAIN_TX_TAG_ACTION_VOTE) { - l_vote_tx_item = (dap_chain_tx_vote_t *)dap_chain_datum_tx_item_get(a_tx, NULL, NULL, TX_ITEM_TYPE_VOTE, NULL); - assert(l_vote_tx_item); - } - bool l_multichannel = false; - size_t i = 0; - for (dap_list_t *it = l_list_tx_out; it; it = it->next, i++) { - dap_chain_tx_item_type_t l_type = *(uint8_t *)it->data; + //Update balance : raise + bool l_cross_network = false, l_multichannel = false; + uint32_t i = 0; + for (dap_list_t *l_tx_out = l_list_tx_out; l_tx_out; l_tx_out = l_tx_out->next, i++) { + assert(l_tx_out->data); + dap_chain_tx_item_type_t l_type = *(uint8_t *)l_tx_out->data; + + dap_chain_addr_t *l_addr = NULL; uint256_t l_value = {}; switch (l_type) { case TX_ITEM_TYPE_OUT: { - dap_chain_tx_out_t *l_out_item_256 = (dap_chain_tx_out_t *)it->data; + dap_chain_tx_out_t *l_out_item_256 = (dap_chain_tx_out_t *)l_tx_out->data; + l_addr = &l_out_item_256->addr; l_value = l_out_item_256->header.value; l_cur_token_ticker = l_main_token_ticker; } break; case TX_ITEM_TYPE_OUT_OLD: { - dap_chain_tx_out_old_t *l_out_item = (dap_chain_tx_out_old_t *)it->data; + dap_chain_tx_out_old_t *l_out_item = (dap_chain_tx_out_old_t *)l_tx_out->data; + l_addr = &l_out_item->addr; l_value = GET_256_FROM_64(l_out_item->header.value); l_cur_token_ticker = l_main_token_ticker; } break; case TX_ITEM_TYPE_OUT_EXT: { - dap_chain_tx_out_ext_t *l_out_item_ext_256 = (dap_chain_tx_out_ext_t *)it->data; + dap_chain_tx_out_ext_t *l_out_item_ext_256 = (dap_chain_tx_out_ext_t *)l_tx_out->data; + l_addr = &l_out_item_ext_256->addr; l_value = l_out_item_ext_256->header.value; l_cur_token_ticker = l_out_item_ext_256->token; } break; case TX_ITEM_TYPE_OUT_COND: { // Update service items if any - dap_chain_tx_out_cond_t *l_cond = (dap_chain_tx_out_cond_t *)it->data; - l_value = l_cond->header.value; - l_cur_token_ticker = l_main_token_ticker; + dap_chain_tx_out_cond_t *l_cond = (dap_chain_tx_out_cond_t *)l_tx_out->data; + dap_ledger_verificator_t *l_verificator = NULL; + int l_tmp = l_cond->header.subtype; + pthread_rwlock_rdlock(&s_verificators_rwlock); + HASH_FIND_INT(s_verificators, &l_tmp, l_verificator); + pthread_rwlock_unlock(&s_verificators_rwlock); + if (l_verificator && l_verificator->callback_out_add) + l_verificator->callback_out_add(a_ledger, a_tx, &l_tx_hash, l_cond); } break; default: log_it(L_ERROR, "Unknown item type %d", l_type); - break; + goto FIN; } - if (!l_multichannel && dap_strcmp(l_main_token_ticker, l_cur_token_ticker)) - l_multichannel = true; - if (dap_strcmp(a_ledger->net->pub.native_ticker, l_cur_token_ticker)) - continue; - if (l_action == DAP_CHAIN_TX_TAG_ACTION_VOTE) { - dap_ledger_tracker_t *l_new_tracker = DAP_NEW_Z(dap_ledger_tracker_t); - if (!l_new_tracker) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - l_ret = DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; - goto FIN; + // for conditional out balance isn't raised + if (l_type != TX_ITEM_TYPE_OUT_COND) { + assert(l_addr); + if (l_addr->net_id.uint64 != a_ledger->net->pub.id.uint64 && + !dap_chain_addr_is_blank(l_addr)) + l_cross_network = true; + if (!l_multichannel && dap_strcmp(l_main_token_ticker, l_cur_token_ticker)) + l_multichannel = true; + const char *l_addr_str = dap_chain_addr_to_str_static(l_addr); + dap_ledger_wallet_balance_t *wallet_balance = NULL; + char *l_wallet_balance_key = dap_strjoin(" ", l_addr_str, l_cur_token_ticker, (char*)NULL); + debug_if(g_debug_ledger, L_DEBUG, "GOT %s to addr: %s", + dap_uint256_to_char(l_value, NULL), l_wallet_balance_key); + pthread_rwlock_rdlock(&l_ledger_pvt->balance_accounts_rwlock); + HASH_FIND_STR(PVT(a_ledger)->balance_accounts, l_wallet_balance_key, wallet_balance); + pthread_rwlock_unlock(&l_ledger_pvt->balance_accounts_rwlock); + if (wallet_balance) { + SUM_256_256(wallet_balance->balance, l_value, &wallet_balance->balance); + DAP_DELETE (l_wallet_balance_key); + // Update the cache + s_balance_cache_update(a_ledger, wallet_balance); + } else { + wallet_balance = DAP_NEW_Z(dap_ledger_wallet_balance_t); + if (!wallet_balance) { + log_it(L_CRITICAL, "Memory allocation error in s_load_cache_gdb_loaded_txs_callback"); + l_ret = -1; + goto FIN; + } + wallet_balance->key = l_wallet_balance_key; + strcpy(wallet_balance->token_ticker, l_cur_token_ticker); + SUM_256_256(wallet_balance->balance, l_value, &wallet_balance->balance); + if(g_debug_ledger) + log_it(L_DEBUG, "Create new balance item: %s %s", l_addr_str, l_cur_token_ticker); + pthread_rwlock_wrlock(&l_ledger_pvt->balance_accounts_rwlock); + HASH_ADD_KEYPTR(hh, PVT(a_ledger)->balance_accounts, wallet_balance->key, + strlen(l_wallet_balance_key), wallet_balance); + pthread_rwlock_unlock(&l_ledger_pvt->balance_accounts_rwlock); + // Add it to cache + s_balance_cache_update(a_ledger, wallet_balance); } - l_new_tracker->voting_hash = l_vote_tx_item->voting_hash; - l_new_tracker->colored_value = l_value; - l_tx_item->out_metadata[i].trackers = dap_list_append(l_tx_item->out_metadata[i].trackers, l_new_tracker); } + + // Moving colour to new outputs + bool l_voting_found = false; for (dap_list_t *mv = l_trackers_mover; mv; mv = mv->next) { struct tracker_mover *l_mover = mv->data; - if (IS_ZERO_256(l_mover->cur_value) || dap_hash_fast_is_blank(&l_mover->voting_hash)) { - log_it(L_ERROR, "Illegal tracker mover item detected"); + assert(!dap_hash_fast_is_blank(&l_mover->voting_hash)); + bool l_vote_add = false; + if (!l_voting_found && l_vote_tx_item && dap_hash_fast_compare(&l_mover->voting_hash, &l_vote_tx_item->voting_hash)) + l_voting_found = l_vote_add = true; + if (!dap_strcmp(l_cur_token_ticker, l_mover->ticker)) continue; + dap_ledger_tokenizer_t *l_moving_sum = s_tokenizer_find(l_values_from_cur_tx, l_mover->ticker); + assert(l_moving_sum); + dap_ledger_tracker_t *l_tracker = NULL; + struct tracker_mover_item *it; + uint256_t l_moved_value = {}; + DL_FOREACH(l_mover->items, it) { + if (IS_ZERO_256(it->cur_value)) + continue; + if (!l_tracker) { + l_tracker = DAP_NEW_Z(dap_ledger_tracker_t); + if (!l_tracker) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + goto FIN; + } + l_tracker->voting_hash = l_mover->voting_hash; + } + dap_ledger_tracker_item_t *l_tracker_item = DAP_NEW_Z(dap_ledger_tracker_item_t); + if (!l_tracker_item) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + goto FIN; + } + l_tracker_item->pkey_hash = it->pkey_hash; + DL_APPEND(l_tracker->items, l_tracker_item); + uint256_t l_coloured_value; + MULT_256_256(l_value, it->coloured_value, &l_coloured_value); + DIV_256(l_coloured_value, l_moving_sum->sum, &l_coloured_value); + if (compare256(it->cur_value, l_coloured_value) > 0 && + !IS_ZERO_256(l_coloured_value)) { + SUBTRACT_256_256(it->cur_value, l_coloured_value, &it->cur_value); + l_tracker_item->coloured_value = l_coloured_value; + } else { + l_tracker_item->coloured_value = it->cur_value; + it->cur_value = uint256_0; + } + if (l_vote_add) + SUM_256_256(l_moved_value, l_tracker_item->coloured_value, &l_moved_value); } - if (l_action == DAP_CHAIN_TX_TAG_ACTION_VOTE && - dap_hash_fast_compare(&l_vote_tx_item->voting_hash, &l_mover->voting_hash)) - continue; // Don't move trackers for current vote voting + if (l_vote_add) { + dap_ledger_tracker_item_t *l_tracker_item = DAP_NEW_Z(dap_ledger_tracker_item_t); + if (!l_tracker_item) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + goto FIN; + } + l_tracker_item->pkey_hash = l_vote_pkey_hash; + assert(compare256(l_value, l_moved_value) > 0); + SUBTRACT_256_256(l_value, l_moved_value, &l_tracker_item->coloured_value); + l_vote_add = false; + } + l_tx_item->out_metadata[i].trackers = dap_list_append(l_tx_item->out_metadata[i].trackers, l_tracker); - dap_ledger_tracker_t *l_tracker = DAP_NEW_Z(dap_ledger_tracker_t); - if (!l_tracker) { + } + if (!l_voting_found && l_vote_tx_item) { + dap_ledger_tracker_t *l_new_tracker = DAP_NEW_Z(dap_ledger_tracker_t); + dap_ledger_tracker_item_t *l_item_new = DAP_NEW_Z(dap_ledger_tracker_item_t); + if (!l_new_tracker || !l_item_new) { log_it(L_CRITICAL, "%s", c_error_memory_alloc); + l_ret = DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; goto FIN; } - l_tracker->voting_hash = l_mover->voting_hash; - uint256_t l_colored_value; - MULT_256_256(l_value, l_mover->colored_value, &l_colored_value); - DIV_256(l_colored_value, l_native_value, &l_colored_value); - if (compare256(l_mover->cur_value, l_colored_value) == 1) { - SUBTRACT_256_256(l_mover->cur_value, l_colored_value, &l_mover->cur_value); - l_tracker->colored_value = l_colored_value; - } else { - l_tracker->colored_value = l_mover->cur_value; - l_mover->cur_value = uint256_0; - } - l_tx_item->out_metadata[i].trackers = dap_list_append(l_tx_item->out_metadata[i].trackers, l_tracker); + l_new_tracker->voting_hash = l_vote_tx_item->voting_hash; + l_item_new->pkey_hash = l_vote_pkey_hash; + l_item_new->coloured_value = l_value; + DL_APPEND(l_new_tracker->items, l_item_new); + l_tx_item->out_metadata[i].trackers = dap_list_append(l_tx_item->out_metadata[i].trackers, l_new_tracker); } } @@ -1607,15 +1650,52 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha if (*l_item != TX_ITEM_TYPE_TSD) continue; dap_tsd_t *l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t *)l_item)->tsd; - if (l_tsd->type != DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TRACKER) - continue; + dap_ledger_tracker_t *l_tracker_current = NULL; + switch (l_tsd->type) { + case DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_VOTING_HASH: { + if (l_tsd->size != sizeof(dap_hash_fast_t)) { + log_it(L_WARNING, "Illegal harfork datum tx TSD VOTING_HASH size %u", l_tsd->size); + break; + } + l_tracker_current = DAP_NEW_Z(dap_ledger_tracker_t); + if (!l_tracker_current) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + break; + } + l_tracker_current->voting_hash = *(dap_hash_fast_t *)l_tsd->data; + l_tx_item->out_metadata[0].trackers = dap_list_append(l_tx_item->out_metadata[0].trackers, l_tracker_current); + } break; + case DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TRACKER: { + if (l_tsd->size != sizeof(dap_ledger_hardfork_tracker_t)) { + log_it(L_WARNING, "Illegal harfork datum tx TSD VOTING_HASH size %u", l_tsd->size); + break; + } + if (!l_tracker_current) { + log_it(L_WARNING, "No voting hash defined for tracking item"); + break; + } + dap_ledger_hardfork_tracker_t *l_tsd_item = (dap_ledger_hardfork_tracker_t *)l_tsd->data; + dap_ledger_tracker_item_t *l_item = DAP_NEW_Z(dap_ledger_tracker_item_t); + if (!l_tracker_current) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + break; + } + l_item->pkey_hash = l_tsd_item->pkey_hash; + l_item->coloured_value = l_tsd_item->coloured_value; + DL_APPEND(l_tracker_current->items, l_item); + } break; + default: + log_it(L_WARNING, "Illegal harfork datum tx TSD item type 0x%X", l_tsd->type); + break; + } if (l_tsd->size != sizeof(dap_ledger_tracker_t)) { log_it(L_WARNING, "Incorrect size of TSD tracker section %u (need %zu)", l_tsd->size, sizeof(dap_ledger_tracker_t)); break; } - l_tx_item->out_metadata[0].trackers = dap_list_append(l_tx_item->out_metadata[0].trackers, l_tsd->data); } } + + // add transaction to the cache list if (l_multichannel) l_tx_item->cache_data.flags |= LEDGER_PVT_TX_META_FLAG_MULTICHANNEL; l_tx_item->ts_added = dap_nanotime_now(); @@ -1662,11 +1742,16 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha dap_ledger_pvt_threshold_txs_proc(a_ledger); FIN: if (l_trackers_mover) - dap_list_free_full(l_trackers_mover, NULL); + dap_list_free_full(l_trackers_mover, dap_ledger_colour_clear_callback); if (l_list_bound_items) dap_list_free_full(l_list_bound_items, NULL); if (l_list_tx_out) dap_list_free(l_list_tx_out); + if (l_values_from_cur_tx) { + dap_ledger_tokenizer_t *it, *tmp; + LL_FOREACH_SAFE(l_values_from_cur_tx, it, tmp) + DAP_DELETE(it); + } if ( is_ledger_cached(l_ledger_pvt) ) { if (l_cache_used_outs) { for (size_t i = 1; i < l_outs_used; ++i) { @@ -1704,7 +1789,7 @@ int dap_ledger_tx_remove(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap int l_ret_check; if( (l_ret_check = s_tx_cache_check(a_ledger, a_tx, a_tx_hash, false, &l_list_bound_items, &l_list_tx_out, - l_main_token_ticker, &l_tag, &l_action, true))) { + l_main_token_ticker, NULL, &l_tag, &l_action, true))) { debug_if(g_debug_ledger, L_WARNING, "dap_ledger_tx_remove() tx %s not passed the check: %s ", l_tx_hash_str, dap_ledger_check_error_str(l_ret_check)); return l_ret_check; @@ -1944,7 +2029,7 @@ int dap_ledger_tx_remove(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap // Clear & destroy item for (uint32_t i = 0; i < l_tx_item->cache_data.n_outs; i++) - dap_list_free_full(l_tx_item->out_metadata[i].trackers, NULL); + dap_list_free_full(l_tx_item->out_metadata[i].trackers, dap_ledger_colour_clear_callback); DAP_DELETE(l_tx_item); if ( is_ledger_cached(l_ledger_pvt) ) { @@ -2050,8 +2135,8 @@ static dap_ledger_stake_lock_item_t *s_emissions_for_stake_lock_item_find(dap_le * a_public_key_size[in] public key size * a_tx_first_hash [in/out] hash of the initial transaction/ found transaction, if 0 start from the beginning */ -static dap_ledger_tx_item_t *s_tx_item_find_by_addr(dap_ledger_t *a_ledger, const dap_chain_addr_t *a_addr, - const char * a_token, dap_chain_hash_fast_t *a_tx_first_hash) +dap_chain_datum_tx_t* dap_ledger_tx_find_by_addr(dap_ledger_t *a_ledger , const char * a_token , + const dap_chain_addr_t *a_addr, dap_chain_hash_fast_t *a_tx_first_hash) { if(!a_addr || !a_tx_first_hash) return NULL; @@ -2108,20 +2193,7 @@ static dap_ledger_tx_item_t *s_tx_item_find_by_addr(dap_ledger_t *a_ledger, cons break; } pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); - return is_tx_found ? l_iter_current : NULL; -} - -/** - * @brief dap_ledger_tx_find_by_addr - * @param a_addr - * @param a_tx_first_hash - * @return - */ - dap_chain_datum_tx_t* dap_ledger_tx_find_by_addr(dap_ledger_t *a_ledger , const char * a_token , - const dap_chain_addr_t *a_addr, dap_chain_hash_fast_t *a_tx_first_hash) -{ - dap_ledger_tx_item_t* l_tx_item = s_tx_item_find_by_addr(a_ledger, a_addr, a_token, a_tx_first_hash); - return (l_tx_item) ? l_tx_item->tx : NULL; + return is_tx_found ? l_iter_current->tx : NULL; } /** @@ -2257,7 +2329,9 @@ dap_list_t *dap_ledger_tx_get_trackers(dap_ledger_t *a_ledger, dap_chain_hash_fa return l_item_out->out_metadata[a_out_idx].trackers; } -uint256_t dap_ledger_coin_get_uncoloured_value(dap_ledger_t *a_ledger, dap_hash_fast_t *a_voting_hash, dap_hash_fast_t *a_tx_hash, int a_out_idx) +uint256_t dap_ledger_coin_get_uncoloured_value(dap_ledger_t *a_ledger, dap_hash_fast_t *a_voting_hash, + dap_hash_fast_t *a_tx_hash, int a_out_idx, + dap_hash_fast_t *a_pkey_hash) { dap_return_val_if_fail(a_ledger && a_voting_hash && a_tx_hash, uint256_0); dap_ledger_tx_item_t *l_item_out = NULL; @@ -2288,14 +2362,28 @@ uint256_t dap_ledger_coin_get_uncoloured_value(dap_ledger_t *a_ledger, dap_hash_ for (dap_list_t *it = l_item_out->out_metadata[a_out_idx].trackers; it ; it = it->next) { dap_ledger_tracker_t *l_tracker = it->data; if (dap_hash_fast_compare(&l_tracker->voting_hash, a_voting_hash)) { - assert(compare256(l_value, l_tracker->colored_value) >= 0); - SUBTRACT_256_256(l_value, l_tracker->colored_value, &l_value); + uint256_t l_coloured_value = {}; + dap_ledger_tracker_item_t *l_item; + DL_FOREACH(l_tracker->items, l_item) + if (!a_pkey_hash || !dap_hash_fast_compare(a_pkey_hash, &l_item->pkey_hash)) + SUM_256_256(l_coloured_value, l_item->coloured_value, &l_coloured_value); + assert(compare256(l_value, l_coloured_value) >= 0); + SUBTRACT_256_256(l_value, l_coloured_value, &l_value); break; } } return l_value; } +void dap_ledger_colour_clear_callback(void *a_list_data) +{ + dap_ledger_tracker_t *l_free = a_list_data; + dap_ledger_tracker_item_t *it, *tmp; + DL_FOREACH_SAFE(l_free->items, it, tmp) + DAP_DELETE(it); // No need for DL_DELETE cause clear the full list + DAP_DELETE(a_list_data); +} + void dap_ledger_tx_clear_colour(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_hash) { dap_return_if_fail(a_ledger && a_tx_hash); @@ -2308,7 +2396,7 @@ void dap_ledger_tx_clear_colour(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_ha l_item_out->cache_data.flags |= LEDGER_PVT_TX_META_FLAG_IMMUTABLE; for (uint32_t i = 0; i < l_item_out->cache_data.n_outs; i++) { if (!dap_hash_fast_is_blank(&l_item_out->out_metadata[i].tx_spent_hash_fast)) { - dap_list_free_full(l_item_out->out_metadata[i].trackers, NULL); + dap_list_free_full(l_item_out->out_metadata[i].trackers, dap_ledger_colour_clear_callback); l_item_out->out_metadata[i].trackers = NULL; } } @@ -2334,7 +2422,7 @@ dap_chain_token_ticker_str_t dap_ledger_tx_calculate_main_ticker_(dap_ledger_t * { dap_hash_fast_t l_tx_hash = dap_chain_node_datum_tx_calc_hash(a_tx); dap_chain_token_ticker_str_t l_ret = { }; - int l_rc = s_tx_cache_check(a_ledger, a_tx, &l_tx_hash, false, NULL, NULL, (char*)&l_ret, NULL, NULL, false); + int l_rc = s_tx_cache_check(a_ledger, a_tx, &l_tx_hash, false, NULL, NULL, (char*)&l_ret, NULL, NULL, NULL, false); if (l_rc == DAP_LEDGER_CHECK_ALREADY_CACHED) dap_strncpy( (char*)&l_ret, dap_ledger_tx_get_token_ticker_by_hash(a_ledger, &l_tx_hash), DAP_CHAIN_TICKER_SIZE_MAX ); if (a_ledger_rc) @@ -2369,11 +2457,14 @@ int dap_ledger_verificator_add(dap_chain_tx_out_cond_subtype_t a_subtype, return 0; } -int dap_ledger_voting_verificator_add(dap_ledger_voting_callback_t a_callback, dap_ledger_voting_delete_callback_t a_callback_delete, dap_ledger_voting_expire_callback_t a_callback_expire) +int dap_ledger_voting_verificator_add(dap_ledger_voting_callback_t a_voting_callback, dap_ledger_vote_callback_t a_vote_callback, + dap_ledger_voting_delete_callback_t a_callback_delete, dap_ledger_voting_expire_callback_t a_callback_expire) { - dap_return_val_if_fail(a_callback && a_callback_delete && a_callback_expire, -1); - int ret = s_voting_callbacks.voting_callback || s_voting_callbacks.voting_delete_callback || s_voting_callbacks.voting_expire_callback ? 1 : 0; - s_voting_callbacks.voting_callback = a_callback; + dap_return_val_if_fail(a_voting_callback && a_vote_callback && a_callback_delete && a_callback_expire, -1); + int ret = s_voting_callbacks.voting_callback || s_voting_callbacks.vote_callback || + s_voting_callbacks.voting_delete_callback || s_voting_callbacks.voting_expire_callback ? 1 : 0; + s_voting_callbacks.voting_callback = a_voting_callback; + s_voting_callbacks.vote_callback = a_vote_callback; s_voting_callbacks.voting_delete_callback = a_callback_delete; s_voting_callbacks.voting_expire_callback = a_callback_expire; return ret; @@ -2450,6 +2541,10 @@ static int s_compare_balances(dap_ledger_hardfork_balances_t *a_list1, dap_ledge return ret ? ret : memcmp(a_list1->ticker, a_list2->ticker, DAP_CHAIN_TICKER_SIZE_MAX); } +int s_compare_tracker_items_hardfork(dap_ledger_tracker_item_t *a_item1, dap_ledger_tracker_item_t *a_item2) +{ + return memcmp(&a_item1->pkey_hash, &a_item1->pkey_hash, sizeof(dap_hash_fast_t)); +} int s_compare_trackers_hardfork(dap_list_t *a_tracker1, dap_list_t *a_tracker2) { @@ -2461,21 +2556,31 @@ dap_list_t *s_trackers_aggregate_hardfork(dap_ledger_t *a_ledger, dap_list_t *a_ { dap_return_val_if_fail(s_voting_callbacks.voting_expire_callback, a_trackers); for (dap_list_t *it = a_added; it; it = it->next) { - dap_ledger_tracker_t *l_new_tracker = it->data; + dap_ledger_tracker_t *l_new_tracker = it->data, *l_exists_tracker = NULL; + dap_time_t l_exp_time = s_voting_callbacks.voting_expire_callback(a_ledger, &l_new_tracker->voting_hash); + if (a_ts_creation_time > l_exp_time) // Don't track expired votings + continue; dap_list_t *l_exists = dap_list_find(a_trackers, &l_new_tracker->voting_hash, s_compare_trackers_hardfork); - dap_ledger_tracker_t *l_exists_tracker = NULL; - if (l_exists) - l_exists_tracker = l_exists->data; - else { + if (!l_exists) { l_exists_tracker = DAP_NEW_Z_RET_VAL_IF_FAIL(dap_ledger_tracker_t, a_trackers); l_exists_tracker->voting_hash = l_new_tracker->voting_hash; - } - if (SUM_256_256(l_exists_tracker->colored_value, l_new_tracker->colored_value, &l_exists_tracker->colored_value)) { - log_it(L_ERROR, "Tracking value overflow, can't track voting %s anymore", dap_hash_fast_to_str_static(&l_new_tracker->voting_hash)); - return a_trackers; - } - if (!l_exists) a_trackers = dap_list_append(a_trackers, l_exists_tracker); + } else + l_exists_tracker = l_exists->data; + dap_ledger_tracker_item_t *l_item, *l_exists_item; + DL_FOREACH(l_new_tracker->items, l_item) { + dap_ledger_tracker_item_t l_sought = { .pkey_hash = l_item->pkey_hash }; + DL_SEARCH(l_exists_tracker->items, l_exists_item, &l_sought, s_compare_tracker_items_hardfork); + if (!l_exists_item) { + l_exists_item = DAP_NEW_Z_RET_VAL_IF_FAIL(dap_ledger_tracker_item_t, a_trackers); + l_exists_item->pkey_hash = l_item->pkey_hash; + DL_APPEND(l_exists_tracker->items, l_exists_item); + } + if (SUM_256_256(l_exists_item->coloured_value, l_item->coloured_value, &l_exists_item->coloured_value)) { + log_it(L_ERROR, "Tracking value overflow, can't track voting %s for hardfork", dap_hash_fast_to_str_static(&l_new_tracker->voting_hash)); + return a_trackers; + } + } } return a_trackers; } diff --git a/modules/ledger/include/dap_chain_ledger.h b/modules/ledger/include/dap_chain_ledger.h index 2de3e3060d..e131ee8748 100644 --- a/modules/ledger/include/dap_chain_ledger.h +++ b/modules/ledger/include/dap_chain_ledger.h @@ -47,10 +47,21 @@ typedef struct dap_ledger { void *_internal; } dap_ledger_t; +typedef struct dap_ledger_tracker_item { + dap_hash_fast_t pkey_hash; + uint256_t coloured_value; + struct dap_ledger_tracker_item *prev, *next; +} dap_ledger_tracker_item_t; + typedef struct dap_ledger_tracker { dap_hash_fast_t voting_hash; - uint256_t colored_value; -} DAP_ALIGN_PACKED dap_ledger_tracker_t; + dap_ledger_tracker_item_t *items; +} dap_ledger_tracker_t; + +typedef struct dap_ledger_hardfork_tracker { + dap_hash_fast_t pkey_hash; + uint256_t coloured_value; +} DAP_ALIGN_PACKED dap_ledger_hardfork_tracker_t; typedef struct dap_ledger_hardfork_balances { dap_chain_addr_t addr; @@ -290,7 +301,8 @@ typedef void (*dap_ledger_cond_out_delete_callback_t)(dap_ledger_t *a_ledger, da typedef void (* dap_ledger_tx_add_notify_t)(void *a_arg, dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ledger_notify_opcodes_t a_opcode); 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_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 int (*dap_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 int (*dap_ledger_voting_callback_t)(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash, bool a_apply); +typedef int (*dap_ledger_vote_callback_t)(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash, dap_hash_fast_t *a_pkey_hash, bool a_apply); typedef bool (*dap_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, dap_hash_fast_t *a_tx_hash); typedef dap_time_t (*dap_ledger_voting_expire_callback_t)(dap_ledger_t *a_ledger, dap_hash_fast_t *a_voting_hash); 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); @@ -445,38 +457,29 @@ dap_chain_datum_tx_t* dap_ledger_tx_find_by_addr(dap_ledger_t *a_ledger, const c bool dap_ledger_tx_check_recipient(dap_ledger_t* a_ledger, dap_chain_hash_fast_t* a_tx_prev_hash, dap_chain_addr_t *a_addr); -// Get the transaction in the cache by the public key that signed the transaction, starting with a_tx_first_hash -const dap_chain_datum_tx_t* dap_ledger_tx_find_by_pkey(dap_ledger_t *a_ledger, - char *a_public_key, size_t a_public_key_size, dap_chain_hash_fast_t *a_tx_first_hash); - -// Get the transaction in the cache with the out_cond item -dap_chain_datum_tx_t* dap_ledger_tx_cache_find_out_cond(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_cond_type, - dap_chain_hash_fast_t *a_tx_first_hash, dap_chain_tx_out_cond_t **a_out_cond, - int *a_out_cond_idx, char *a_token_ticker); - // Get all transactions from the cache with the specified out_cond items dap_list_t* dap_ledger_tx_cache_find_out_cond_all(dap_ledger_t *a_ledger, dap_chain_srv_uid_t a_srv_uid); -// Get the value from all transactions in the cache with out_cond item -uint256_t dap_ledger_tx_cache_get_out_cond_value(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_cond_type, dap_chain_addr_t *a_addr, - dap_chain_tx_out_cond_t **tx_out_cond); // Get first tx with type a_subtype dap_chain_tx_used_out_item_t *dap_ledger_get_tx_cond_out(dap_ledger_t *a_ledger, const dap_chain_addr_t *a_addr_from, dap_chain_tx_out_cond_subtype_t a_subtype); +dap_chain_tx_out_cond_t *dap_ledger_out_cond_unspent_find_by_addr(dap_ledger_t *a_ledger, const char *a_token, dap_chain_tx_out_cond_subtype_t a_subtype, + const dap_chain_addr_t *a_addr, dap_chain_hash_fast_t *a_tx_first_hash, int *a_out_idx); + dap_list_t *dap_ledger_get_list_tx_outs(dap_ledger_t *a_ledger, const char *a_token_ticker, const dap_chain_addr_t *a_addr_from, uint256_t *a_value_transfer); -// Get the list of 'out_cond' items with summary value >= than a_value_need -dap_list_t *dap_ledger_get_list_tx_cond_outs_with_val(dap_ledger_t *a_ledger, const char *a_token_ticker, const dap_chain_addr_t *a_addr_from, - dap_chain_tx_out_cond_subtype_t a_subtype, uint256_t a_value_need, uint256_t *a_value_transfer); -dap_list_t *dap_ledger_get_list_tx_cond_outs(dap_ledger_t *a_ledger, const dap_chain_addr_t *a_addr_from); +dap_list_t *dap_ledger_get_list_tx_cond_outs(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_subtype, const char *a_token_ticker, const dap_chain_addr_t *a_addr_from); +bool dap_ledger_check_condition_owner(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_hash, dap_chain_tx_out_cond_subtype_t a_cond_subtype, int a_out_idx, dap_sign_t *a_owner_sign); + // Add new verificator callback with associated subtype. Returns 1 if callback replaced, overwise returns 0 int dap_ledger_verificator_add(dap_chain_tx_out_cond_subtype_t a_subtype, dap_ledger_cond_in_verify_callback_t a_callback_in_verify, dap_ledger_cond_out_verify_callback_t a_callback_out_verify, dap_ledger_cond_in_add_callback_t a_callback_in_add, dap_ledger_cond_out_add_callback_t a_callback_out_add, dap_ledger_cond_in_delete_callback_t a_callback_in_delete, dap_ledger_cond_out_delete_callback_t a_callback_out_delete); // Add new verificator callback for voting. Returns 1 if callback replaced, overwise returns 0 -int dap_ledger_voting_verificator_add(dap_ledger_voting_callback_t a_callback, dap_ledger_voting_delete_callback_t a_callback_delete, dap_ledger_voting_expire_callback_t a_callback_expire); +int dap_ledger_voting_verificator_add(dap_ledger_voting_callback_t a_voting_callback, dap_ledger_vote_callback_t a_vote_callback, + dap_ledger_voting_delete_callback_t a_callback_delete, dap_ledger_voting_expire_callback_t a_callback_expire); int dap_ledger_tax_callback_set(dap_ledger_tax_callback_t a_callback); // Getting a list of transactions from the ledger. dap_list_t * dap_ledger_get_txs(dap_ledger_t *a_ledger, size_t a_count, size_t a_page, bool a_reverse, bool a_unspent_only); @@ -521,9 +524,12 @@ int dap_ledger_anchor_purge(dap_ledger_t *a_ledger, dap_chain_id_t a_chain_id); dap_ledger_hardfork_balances_t *dap_ledger_states_aggregate(dap_ledger_t *a_ledger, dap_time_t a_hardfork_decree_creation_time, dap_ledger_hardfork_condouts_t **l_cond_outs_list, json_object* a_changed_addrs); dap_ledger_hardfork_anchors_t *dap_ledger_anchors_aggregate(dap_ledger_t *a_ledger, dap_chain_id_t a_chain_id); -uint256_t dap_ledger_coin_get_uncoloured_value(dap_ledger_t *a_ledger, dap_hash_fast_t *a_voting_hash, dap_hash_fast_t *a_tx_hash, int a_out_idx); +uint256_t dap_ledger_coin_get_uncoloured_value(dap_ledger_t *a_ledger, dap_hash_fast_t *a_voting_hash, + dap_hash_fast_t *a_tx_hash, int a_out_idx, dap_hash_fast_t *a_pkey_hash); dap_list_t *dap_ledger_tx_get_trackers(dap_ledger_t *a_ledger, dap_chain_hash_fast_t *a_tx_hash, uint32_t a_out_idx); void dap_ledger_tx_clear_colour(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_hash); +void dap_ledger_colour_clear_callback(void *a_list_data); + dap_pkey_t *dap_ledger_find_pkey_by_hash(dap_ledger_t *a_ledger, dap_chain_hash_fast_t *a_pkey_hash); dap_list_t *dap_ledger_decrees_get_by_type(dap_ledger_t *a_ledger, int a_type); diff --git a/modules/net/dap_chain_net_tx.c b/modules/net/dap_chain_net_tx.c index 7561efa260..550083ddee 100644 --- a/modules/net/dap_chain_net_tx.c +++ b/modules/net/dap_chain_net_tx.c @@ -1657,7 +1657,7 @@ int dap_chain_net_tx_to_json(dap_chain_datum_tx_t *a_tx, json_object *a_out_json json_object_object_add(json_obj_item,"type", json_object_new_string("voting")); json_object_object_add(json_obj_item,"voting_question", json_object_new_string(l_voting_params->question)); json_object_object_add(json_obj_item,"answer_options", json_object_new_string("")); - + json_object_object_add(json_obj_item, "token", json_object_new_string(l_voting_params->token_ticker)); dap_list_t *l_temp = l_voting_params->options; uint8_t l_index = 0; while (l_temp) { @@ -1672,12 +1672,10 @@ int dap_chain_net_tx_to_json(dap_chain_datum_tx_t *a_tx, json_object *a_out_json if (l_voting_params->votes_max_count) { json_object_object_add(json_obj_item, "votes_max_count", json_object_new_uint64(l_voting_params->votes_max_count)); } - json_object_object_add(json_obj_item,"changing_vote_is", l_voting_params->vote_changing_allowed ? json_object_new_string("available") : - json_object_new_string("not available")); - l_voting_params->delegate_key_required ? - json_object_object_add(json_obj_item,"delegated_key_for_participating_in_voting", json_object_new_string("required")): - json_object_object_add(json_obj_item,"delegated_key_for_participating_in_voting", json_object_new_string("not required")); - + json_object_object_add(json_obj_item, "changing_vote_is", + json_object_new_string(l_voting_params->vote_changing_allowed ? "available" : "not available")); + json_object_object_add(json_obj_item, "delegated_key_for_participating_in_voting", + json_object_new_string(l_voting_params->delegate_key_required ? "required" : "not required")); dap_list_free_full(l_voting_params->options, NULL); DAP_DELETE(l_voting_params->question); DAP_DELETE(l_voting_params); diff --git a/modules/net/dap_chain_node.c b/modules/net/dap_chain_node.c index 079ff718e9..b05535667c 100644 --- a/modules/net/dap_chain_node.c +++ b/modules/net/dap_chain_node.c @@ -610,15 +610,27 @@ dap_chain_datum_t *s_cond_tx_create(dap_chain_tx_out_cond_t *a_cond, dap_chain_t } for (dap_list_t *it = a_trackers; it; it = it->next) { dap_ledger_tracker_t *l_tracker = it->data; - dap_chain_tx_tsd_t *l_tracker_tsd = dap_chain_datum_tx_item_tsd_create(l_tracker, DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TRACKER, sizeof(dap_ledger_tracker_t)); - if (!l_tracker_tsd) { + dap_chain_tx_tsd_t *l_tracker_hash_tsd = dap_chain_datum_tx_item_tsd_create(&l_tracker->voting_hash, DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_VOTING_HASH, sizeof(dap_hash_fast_t)); + if (!l_tracker_hash_tsd) { dap_chain_datum_tx_delete(l_tx); return NULL; } - if (dap_chain_datum_tx_add_item(&l_tx, l_tracker_tsd) != 1) { + if (dap_chain_datum_tx_add_item(&l_tx, l_tracker_hash_tsd) != 1) { dap_chain_datum_tx_delete(l_tx); return NULL; } + for (dap_ledger_tracker_item_t *l_item = l_tracker->items; l_item; l_item = l_item->next) { + dap_ledger_hardfork_tracker_t l_hardfork_tracker = { .pkey_hash = l_item->pkey_hash, .coloured_value = l_item->coloured_value }; + dap_chain_tx_tsd_t *l_tracker_tsd = dap_chain_datum_tx_item_tsd_create(&l_hardfork_tracker, DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TRACKER, sizeof(dap_ledger_hardfork_tracker_t)); + if (!l_tracker_tsd) { + dap_chain_datum_tx_delete(l_tx); + return NULL; + } + if (dap_chain_datum_tx_add_item(&l_tx, l_tracker_tsd) != 1) { + dap_chain_datum_tx_delete(l_tx); + return NULL; + } + } } dap_chain_datum_t *l_datum_tx = dap_chain_datum_create(DAP_CHAIN_DATUM_TX, l_tx, dap_chain_datum_tx_get_size(l_tx)); dap_chain_datum_tx_delete(l_tx); @@ -783,11 +795,19 @@ static int s_compare_trackers(dap_list_t *a_list1, dap_list_t *a_list2) dap_ledger_tracker_t *l_tracker1 = it1->data, *l_tracker2 = it2->data; ret = memcmp(&l_tracker1->voting_hash, &l_tracker1->voting_hash, sizeof(dap_hash_fast_t)); - if (ret) - break; - ret = compare256(l_tracker1->colored_value, l_tracker2->colored_value); - if (ret) + if (ret) // hash mismatch break; + dap_ledger_tracker_item_t *it1 = l_tracker1->items, *it2 = l_tracker2->items; + for (; it1 && it2; it1 = it1->next, it2 = it2->next) { + ret = memcmp(&it1->pkey_hash, &it2->pkey_hash, sizeof(dap_hash_fast_t)); + if (ret) // pkey mismatch + break; + ret = compare256(it1->coloured_value, it2->coloured_value); + if (ret) // value mismatch + break; + } + if (it1 || it2) // count mismatch + return 1; } return ret; } @@ -880,6 +900,14 @@ int s_hardfork_check(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_ } } break; +#define m_ret_clear(rc) { \ + if (l_regular.trackers) \ + dap_list_free_full(l_regular.trackers, dap_ledger_colour_clear_callback); \ + if (l_conitional.trackers) \ + dap_list_free_full(l_conitional.trackers, dap_ledger_colour_clear_callback);\ + return rc; \ + } + case DAP_CHAIN_DATUM_TX: { dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t *)a_datum->data; if (!l_tx->header.ts_created /* TODO add || l_tx->header.ts_created other criteria */) { @@ -890,6 +918,7 @@ int s_hardfork_check(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_ // Parse datum dap_ledger_hardfork_balances_t l_regular = {}; dap_ledger_hardfork_condouts_t l_conitional = {}; + dap_ledger_tracker_t *l_tracker_current = NULL; bool l_out = false; byte_t *l_item; size_t l_size; TX_ITEM_ITER_TX(l_item, l_size, l_tx) { @@ -897,7 +926,7 @@ int s_hardfork_check(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_ case TX_ITEM_TYPE_OUT_EXT: if (l_out || l_conitional.cond) { log_it(L_WARNING, "Additional OUT_EXT item for harfork datum tx is forbidden"); - return -4; + m_ret_clear(-4); } l_out = true; dap_chain_tx_out_ext_t *l_out_ext = (dap_chain_tx_out_ext_t *)l_item; @@ -908,19 +937,23 @@ int s_hardfork_check(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_ case TX_ITEM_TYPE_OUT_COND: if (l_out || l_conitional.cond) { log_it(L_WARNING, "Additional OUT_COND item for harfork datum tx is forbidden"); - return -5; + m_ret_clear(-5); } l_conitional.cond = (dap_chain_tx_out_cond_t *)l_item; break; case TX_ITEM_TYPE_SIG: if (l_conitional.sign) { log_it(L_WARNING, "Additional SIG item for harfork datum tx is forbidden"); - return -6; + m_ret_clear(-6); } l_conitional.sign = (dap_chain_tx_sig_t *)l_item; break; case TX_ITEM_TYPE_TSD: { dap_chain_tx_tsd_t *l_tx_tsd = (dap_chain_tx_tsd_t *)l_item; + if (l_tx_tsd->header.size < sizeof(dap_tsd_t)) { + log_it(L_WARNING, "Illegal harfork datum tx TSD header size %" DAP_UINT64_FORMAT_U, l_tx_tsd->header.size); + m_ret_clear(-8); + } dap_tsd_t *l_tsd = (dap_tsd_t *)l_tx_tsd->tsd; switch (l_tsd->type) { case DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TX_HASH: @@ -929,24 +962,45 @@ int s_hardfork_check(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_ case DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TICKER: if (!l_tsd->size || l_tsd->size > DAP_CHAIN_TICKER_SIZE_MAX) { log_it(L_WARNING, "Illegal harfork datum tx TSD TICKER size %u", l_tsd->size); - return -8; + m_ret_clear(-8); } dap_strncpy(l_conitional.ticker, (char *)l_tsd->data, DAP_CHAIN_TICKER_SIZE_MAX); break; - case DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TRACKER: + case DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_VOTING_HASH: { + if (l_tsd->size != sizeof(dap_hash_fast_t)) { + log_it(L_WARNING, "Illegal harfork datum tx TSD VOTING_HASH size %u", l_tsd->size); + m_ret_clear(-8); + } + l_tracker_current = DAP_NEW_Z_RET_VAL_IF_FAIL(dap_ledger_tracker_t, -2); + l_tracker_current->voting_hash = *(dap_hash_fast_t *)l_tsd->data; if (l_out) - l_regular.trackers = dap_list_append(l_regular.trackers, l_tsd->data); + l_regular.trackers = dap_list_append(l_regular.trackers, l_tracker_current); else - l_conitional.trackers = dap_list_append(l_conitional.trackers, l_tsd->data); - break; + l_conitional.trackers = dap_list_append(l_conitional.trackers, l_tracker_current); + } break; + case DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TRACKER: { + if (l_tsd->size != sizeof(dap_ledger_hardfork_tracker_t)) { + log_it(L_WARNING, "Illegal harfork datum tx TSD VOTING_HASH size %u", l_tsd->size); + m_ret_clear(-8); + } + if (!l_tracker_current) { + log_it(L_WARNING, "No voting hash defined for tracking item"); + m_ret_clear(-19); + } + dap_ledger_hardfork_tracker_t *l_tsd_item = (dap_ledger_hardfork_tracker_t *)l_tsd->data; + dap_ledger_tracker_item_t *l_item = DAP_NEW_Z_RET_VAL_IF_FAIL(dap_ledger_tracker_item_t, -2); + l_item->pkey_hash = l_tsd_item->pkey_hash; + l_item->coloured_value = l_tsd_item->coloured_value; + DL_APPEND(l_tracker_current->items, l_item); + } break; default: log_it(L_WARNING, "Illegal harfork datum tx TSD item type 0x%X", l_tsd->type); - return -7; + m_ret_clear(-7); } } break; default: log_it(L_WARNING, "Illegal harfork datum tx item type %d", *l_item); - return -4; + m_ret_clear(-4); } } // Call comparators @@ -956,7 +1010,7 @@ int s_hardfork_check(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_ if (l_found) { if (a_remove) { DL_DELETE(a_chain->hardfork_data->balances, l_found); - dap_list_free_full(l_found->trackers, NULL); + dap_list_free_full(l_found->trackers, dap_ledger_colour_clear_callback); DAP_DELETE(l_found); if (!a_chain->hardfork_data->balances) a_chain->hardfork_data->state_current = STATE_CONDOUTS; @@ -970,7 +1024,7 @@ int s_hardfork_check(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_ if (l_found) { if (a_remove) { DL_DELETE(a_chain->hardfork_data->condouts, l_found); - dap_list_free_full(l_found->trackers, NULL); + dap_list_free_full(l_found->trackers, dap_ledger_colour_clear_callback); DAP_DELETE(l_found); if (!a_chain->hardfork_data->condouts) a_chain->hardfork_data->state_current = STATE_FEES; @@ -994,14 +1048,15 @@ int s_hardfork_check(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_ } } else { log_it(L_WARNING, "Illegal harfork datum tx item with no OUT"); - return -8; + m_ret_clear(-18); } // Clean memory if (l_regular.trackers) - dap_list_free(l_regular.trackers); + dap_list_free_full(l_regular.trackers, dap_ledger_colour_clear_callback); if (l_conitional.trackers) - dap_list_free(l_conitional.trackers); + dap_list_free_full(l_conitional.trackers, dap_ledger_colour_clear_callback); } break; +#undef m_ret_clear case DAP_CHAIN_DATUM_SERVICE_STATE: { dap_chain_srv_hardfork_state_t *l_found = NULL, @@ -1078,6 +1133,7 @@ int s_hardfork_check(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_ } } break; +#undef m_ret default: log_it(L_WARNING, "Incorrect harfork datum type %u", a_datum->header.type_id); diff --git a/modules/node-cli/dap_chain_node_cli_cmd_tx.c b/modules/node-cli/dap_chain_node_cli_cmd_tx.c index 14a6733b94..ef3831c6fa 100644 --- a/modules/node-cli/dap_chain_node_cli_cmd_tx.c +++ b/modules/node-cli/dap_chain_node_cli_cmd_tx.c @@ -110,7 +110,7 @@ bool s_dap_chain_datum_tx_out_data(json_object* a_json_arr_reply, TX_ITEM_ITER_TX_TYPE(l_item, TX_ITEM_TYPE_OUT_ALL, l_size, i, a_datum) { ++l_out_idx; dap_hash_fast_t l_spender = { }; - json_object *l_json_obj_out = NULL, *json_arr_colours = NULL; + json_object *l_json_obj_out = NULL, *l_json_arr_colours = NULL; if ( dap_ledger_tx_hash_is_used_out_item(a_ledger, a_tx_hash, l_out_idx, &l_spender) ) { char l_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE] = { '\0' }; dap_hash_fast_to_str(&l_spender, l_hash_str, sizeof(l_hash_str)); @@ -125,18 +125,26 @@ bool s_dap_chain_datum_tx_out_data(json_object* a_json_arr_reply, l_json_obj_out = json_object_new_object(); json_object_object_add(l_json_obj_out, "out_number", json_object_new_int(l_out_idx)); } - json_arr_colours = json_object_new_array(); - json_object_object_add(l_json_obj_out, "trackers", json_arr_colours); + l_json_arr_colours = json_object_new_array(); + json_object_object_add(l_json_obj_out, "trackers", l_json_arr_colours); } for (dap_list_t *it = l_trackers; it; it = it->next) { dap_ledger_tracker_t *l_tracker = it->data; json_object *l_json_obj_tracker = json_object_new_object(); + json_object_array_add(l_json_arr_colours, l_json_obj_tracker); const char *l_voling_hash_str = dap_hash_fast_to_str_static(&l_tracker->voting_hash); json_object_object_add(l_json_obj_tracker, "voting_hash", json_object_new_string(l_voling_hash_str)); - const char *l_coloured_coins, *l_coloured_value = dap_uint256_to_char(l_tracker->colored_value, &l_coloured_coins); - json_object_object_add(l_json_obj_tracker, "coloured_coins", json_object_new_string(l_coloured_coins)); - json_object_object_add(l_json_obj_tracker, "coloured_value", json_object_new_string(l_coloured_coins)); - json_object_array_add(json_arr_colours, l_json_obj_tracker); + json_object *l_json_arr_tracker_items = json_object_new_array(); + json_object_object_add(l_json_obj_tracker, "items", l_json_arr_tracker_items); + for (dap_ledger_tracker_item_t *l_item = l_tracker->items; l_item; l_item = l_item->next) { + json_object *l_json_obj_tracker_item = json_object_new_object(); + json_object_array_add(l_json_arr_tracker_items, l_json_obj_tracker_item); + const char *l_pkey_hash_str = dap_hash_fast_to_str_static(&l_item->pkey_hash); + json_object_object_add(l_json_obj_tracker_item, "pkey_hash", json_object_new_string(l_pkey_hash_str)); + const char *l_coloured_coins, *l_coloured_value = dap_uint256_to_char(l_item->coloured_value, &l_coloured_coins); + json_object_object_add(l_json_obj_tracker_item, "coloured_coins", json_object_new_string(l_coloured_coins)); + json_object_object_add(l_json_obj_tracker_item, "coloured_value", json_object_new_string(l_coloured_value)); + } } if (l_json_obj_out) json_object_array_add(json_arr_items, l_json_obj_out); diff --git a/modules/service/stake/dap_chain_net_srv_stake_pos_delegate.c b/modules/service/stake/dap_chain_net_srv_stake_pos_delegate.c index 835fbbe90f..1df12d34ac 100644 --- a/modules/service/stake/dap_chain_net_srv_stake_pos_delegate.c +++ b/modules/service/stake/dap_chain_net_srv_stake_pos_delegate.c @@ -3652,7 +3652,14 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, void **a_str_reply) l_node_address_text_block = dap_strdup_printf("node_address:\t" NODE_ADDR_FP_STR,NODE_ADDR_FP_ARGS_S(l_tx_out_cond->subtype.srv_stake_pos_delegate.signer_node_addr)); json_object_object_add(l_json_obj_tx, "node_address", json_object_new_string(l_node_address_text_block)); json_object_object_add(l_json_obj_tx, "value_coins", json_object_new_string(l_coins)); - json_object_object_add(l_json_obj_tx, "value_datoshi", json_object_new_string(l_balance)); + json_object_object_add(l_json_obj_tx, "value_datoshi", json_object_new_string(l_balance)); + dap_hash_fast_t l_owner_hash = dap_ledger_get_first_chain_tx_hash(l_net->pub.ledger, DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE, &l_datum_hash); + dap_chain_datum_tx_t *l_owner_tx = dap_hash_fast_is_blank(&l_owner_hash) ? l_datum_tx + : dap_ledger_tx_find_by_hash(l_net->pub.ledger, &l_owner_hash); + assert(l_owner_tx); + dap_sign_t *l_owner_sign = dap_chain_datum_tx_get_sign(l_owner_tx, 0); + dap_chain_addr_t l_owner_addr; dap_chain_addr_fill_from_sign(&l_owner_addr, l_owner_sign, l_net->pub.id); + json_object_object_add(l_json_obj_tx, "owner_addr", json_object_new_string(dap_chain_addr_to_str_static(&l_owner_addr))); json_object_array_add(l_json_arr_tx, l_json_obj_tx); DAP_DELETE(l_node_address_text_block); } diff --git a/modules/service/voting/dap_chain_net_srv_voting.c b/modules/service/voting/dap_chain_net_srv_voting.c index 3847e37142..bbd028763d 100644 --- a/modules/service/voting/dap_chain_net_srv_voting.c +++ b/modules/service/voting/dap_chain_net_srv_voting.c @@ -25,24 +25,15 @@ #include "dap_common.h" #include "dap_chain_net_srv_voting.h" #include "dap_chain_datum_tx_voting.h" -#include "dap_chain_datum_service_state.h" #include "dap_chain_net_srv_stake_pos_delegate.h" #include "dap_chain_net_tx.h" #include "dap_chain_mempool.h" #include "uthash.h" #include "dap_chain_srv.h" #include "dap_cli_server.h" -#include "dap_chain_wallet_cache.h" #define LOG_TAG "dap_chain_net_srv_voting" - -struct voting_cond_outs { - dap_chain_hash_fast_t tx_hash; - int out_idx; - UT_hash_handle hh; -}; - struct vote { dap_chain_hash_fast_t vote_hash; dap_chain_hash_fast_t pkey_hash; @@ -67,8 +58,8 @@ static void s_callback_delete(void *a_service_internal); static int s_callback_purge(dap_chain_net_id_t a_net_id, void *a_service_internal); static byte_t *s_votings_backup(dap_chain_net_id_t a_net_id, uint64_t *a_state_size, uint32_t *a_state_count, void *a_service_internal); static int s_votings_restore(dap_chain_net_id_t a_net_id, byte_t *a_state, uint64_t a_state_size, uint32_t a_states_count); - -static int s_voting_ledger_verificator_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 int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, bool a_apply); +static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, dap_hash_fast_t *a_pkey_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, dap_hash_fast_t *a_tx_hash); static int s_cli_voting(int argc, char **argv, void **a_str_reply); @@ -91,10 +82,11 @@ static bool s_tag_check_voting(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_t int dap_chain_net_srv_voting_init() { - dap_ledger_voting_verificator_add(s_voting_ledger_verificator_callback, s_datum_tx_voting_verification_delete_callback, dap_chain_net_srv_voting_get_expiration_time); + dap_ledger_voting_verificator_add(s_voting_verificator, s_vote_verificator, + s_datum_tx_voting_verification_delete_callback, dap_chain_net_srv_voting_get_expiration_time); dap_cli_server_cmd_add("voting", s_cli_voting, "Voting commands.", "voting create -net <net_name> -question <\"Question_string\"> -options <\"Option0\", \"Option1\" ... \"OptionN\"> [-expire <voting_expire_time_in_RCF822>] [-max_votes_count <Votes_count>] [-delegated_key_required] [-vote_changing_allowed] -fee <value> -w <fee_wallet_name>\n" - "voting vote -net <net_name> -hash <voting_hash> -option_idx <option_index> [-cert <delegate_cert_name>] -fee <value> -w <fee_wallet_name>\n" + "voting vote -net <net_name> -hash <voting_hash> -option_idx <option_index> [-cert <delegate_cert_name>] -fee <value> -w <fee_wallet_name> [-token <ticker>]\n" "voting list -net <net_name>\n" "voting dump -net <net_name> -hash <voting_hash>\n" "Hint:\n" @@ -242,7 +234,14 @@ static int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_ TX_ITEM_ITER_TX(l_item, l_tx_item_size, a_tx_in) { if (*l_item != TX_ITEM_TYPE_TSD) continue; - dap_tsd_t *l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t *)l_item)->tsd; + dap_chain_tx_tsd_t *l_tx_tsd = (dap_chain_tx_tsd_t *)l_item; + dap_tsd_t *l_tsd = (dap_tsd_t *)l_tx_tsd->tsd; + if (l_tx_tsd->header.size < sizeof(dap_tsd_t) || + l_tx_tsd->header.size != dap_tsd_size(l_tsd)) { + log_it(L_WARNING, "Incorrect size %" DAP_UINT64_FORMAT_U " of TX_TSD item for voting %s", + l_tx_tsd->header.size, dap_hash_fast_to_str_static(a_tx_hash)); + return -DAP_LEDGER_CHECK_INVALID_SIZE; + } switch(l_tsd->type) { case VOTING_TSD_TYPE_QUESTION: if (!l_tsd->size || *l_tsd->data == '\0') { @@ -282,6 +281,11 @@ static int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_ return -DAP_LEDGER_CHECK_INVALID_SIZE; } break; + case VOTING_TSD_TYPE_TOKEN: + if (!l_tsd->size || l_tsd->size >= DAP_CHAIN_TICKER_SIZE_MAX) { + log_it(L_WARNING, "Incorrect size %u of TSD section TOKEN for voting %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); + return -DAP_LEDGER_CHECK_INVALID_SIZE; + } default: break; } @@ -298,15 +302,19 @@ static int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_ struct voting *l_item = DAP_NEW_Z_RET_VAL_IF_FAIL(struct voting, -DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); l_item->hash = *a_tx_hash; l_item->start_time = a_tx_in->header.ts_created; - l_item->params = dap_chain_datum_tx_voting_parse_tsd(a_tx_in); + l_item->params = dap_chain_datum_tx_voting_parse_tsd(a_tx_in); if (!l_item->params) return DAP_DELETE(l_item), -DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; + if (!*l_item->params->token_ticker) + strcpy(l_item->params->token_ticker, a_ledger->net->pub.native_ticker); s_voting_add(a_ledger->net->pub.id, l_item); + log_it(L_NOTICE, "Voting with hash %s succefully added to ledger", dap_hash_fast_to_str_static(a_tx_hash)); + return DAP_LEDGER_CHECK_OK; } -static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, bool a_apply) +static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, dap_hash_fast_t *a_pkey_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); assert(l_vote_tx_item); @@ -318,43 +326,43 @@ static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx return -5; } - dap_hash_fast_t pkey_hash = {}; - int l_item_cnt = 0; - dap_list_t *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_WARNING, "Can't get signs from tx %s", dap_chain_hash_fast_to_str_static(a_tx_hash)); - return -9; + // Get last sign item from transaction + dap_hash_fast_t l_pkey_hash = {}; + dap_sign_t *l_pkey_sign = NULL; + uint8_t *l_tx_item = NULL; size_t l_size; int i, l_sign_num = 0; + TX_ITEM_ITER_TX_TYPE(l_tx_item, TX_ITEM_TYPE_SIG, l_size, i, a_tx_in) { + l_pkey_sign = dap_chain_datum_tx_item_sig_get_sign((dap_chain_tx_sig_t *)l_tx_item); + l_sign_num++; + } + dap_sign_get_pkey_hash(l_pkey_sign, &l_pkey_hash); + if (--l_sign_num && dap_chain_datum_tx_verify_sign(a_tx_in, l_sign_num)) { + log_it(L_WARNING, "Last vote tx %s sign verification failed", dap_chain_hash_fast_to_str_static(a_tx_hash)); + return -22; } - dap_chain_tx_sig_t *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_list_free(l_signs_list); - if (!a_apply) { - if (l_vote_tx_item->answer_idx > dap_list_length(l_voting->params->options)) { - log_it(L_WARNING, "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->params->votes_max_count && dap_list_length(l_voting->votes) >= l_voting->params->votes_max_count){ - log_it(L_WARNING, "The required number of votes has been collected for voting %s", dap_chain_hash_fast_to_str_static(&l_voting->hash)); - return -7; - } - if (l_voting->params->voting_expire && l_voting->params->voting_expire <= a_tx_in->header.ts_created) { - log_it(L_WARNING, "The voting %s has been expired", dap_chain_hash_fast_to_str_static(&l_voting->hash)); - return -8; - } + if (l_vote_tx_item->answer_idx > dap_list_length(l_voting->params->options)) { + log_it(L_WARNING, "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->params->votes_max_count && dap_list_length(l_voting->votes) >= l_voting->params->votes_max_count){ + log_it(L_WARNING, "The required number of votes has been collected for voting %s", dap_chain_hash_fast_to_str_static(&l_voting->hash)); + return -7; + } + if (l_voting->params->voting_expire && l_voting->params->voting_expire <= a_tx_in->header.ts_created) { + log_it(L_WARNING, "The voting %s has been expired", dap_chain_hash_fast_to_str_static(&l_voting->hash)); + return -8; + } - if (l_voting->params->delegate_key_required && - !dap_chain_net_srv_stake_check_pkey_hash(a_ledger->net->pub.id, &pkey_hash)){ - log_it(L_WARNING, "Voting %s required a delegated key", dap_chain_hash_fast_to_str_static(&l_voting->hash)); - return -10; - } + if (l_voting->params->delegate_key_required && + !dap_chain_net_srv_stake_check_pkey_hash(a_ledger->net->pub.id, &l_pkey_hash)){ + log_it(L_WARNING, "Voting %s required a delegated key", dap_chain_hash_fast_to_str_static(&l_voting->hash)); + return -10; } dap_list_t *l_vote_overwrited = NULL; for (dap_list_t *it = l_voting->votes; it; it = it->next) { - if (dap_hash_fast_compare(&((struct vote *)it->data)->pkey_hash, &pkey_hash)) { + if (dap_hash_fast_compare(&((struct vote *)it->data)->pkey_hash, &l_pkey_hash)) { dap_hash_fast_t *l_vote_hash = &((struct vote *)it->data)->vote_hash; if (!l_voting->params->vote_changing_allowed) { char l_vote_hash_str[DAP_HASH_FAST_STR_SIZE]; @@ -380,15 +388,23 @@ static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx case TX_ITEM_TYPE_TSD: { // check out conds dap_tsd_t *l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t *)l_item)->tsd; if (l_tsd->type != VOTING_TSD_TYPE_VOTE_TX_COND) - continue; + return -14; l_tx_hash = ((dap_chain_tx_voting_tx_cond_t *)l_tsd->data)->tx_hash; l_out_idx = ((dap_chain_tx_voting_tx_cond_t *)l_tsd->data)->out_idx; + dap_chain_datum_tx_t *l_tx_prev_temp = dap_ledger_tx_find_by_hash(a_ledger, &l_tx_hash); + dap_chain_tx_out_cond_t *l_prev_out = (dap_chain_tx_out_cond_t *)dap_chain_datum_tx_out_get_by_out_idx(l_tx_prev_temp, l_out_idx); + if (!l_prev_out || l_prev_out->header.item_type != TX_ITEM_TYPE_OUT_COND || + l_prev_out->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE) + return -16; + if (!dap_ledger_check_condition_owner(a_ledger, &l_tx_hash, l_prev_out->header.subtype, l_out_idx, l_pkey_sign)) + return -17; break; } default: continue; } - uint256_t l_uncoloured_value = dap_ledger_coin_get_uncoloured_value(a_ledger, &l_vote_tx_item->voting_hash, &l_tx_hash, l_out_idx); + uint256_t l_uncoloured_value = dap_ledger_coin_get_uncoloured_value(a_ledger, &l_vote_tx_item->voting_hash, &l_tx_hash, l_out_idx, + l_vote_overwrited ? &l_pkey_hash : NULL); if (IS_ZERO_256(l_uncoloured_value)) { log_it(L_ERROR, "Coin with OUT number %d of tx %s is voted before in voting %s", l_out_idx, dap_chain_hash_fast_to_str_static(&l_tx_hash), dap_chain_hash_fast_to_str_static(&l_vote_tx_item->voting_hash)); @@ -409,35 +425,29 @@ static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx if (a_apply) { struct vote *l_vote_item = DAP_NEW_Z_RET_VAL_IF_FAIL(struct vote, -DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); l_vote_item->vote_hash = *a_tx_hash; - l_vote_item->pkey_hash = pkey_hash; + l_vote_item->pkey_hash = l_pkey_hash; l_vote_item->answer_idx = l_vote_tx_item->answer_idx; l_vote_item->weight = l_weight; if (l_vote_overwrited) { - SUM_256_256(l_vote_item->weight, ((struct vote *)l_vote_overwrited->data)->weight, &l_vote_item->weight); // change vote & move it to the end of list - l_voting->votes = dap_list_remove_link(l_voting->votes, l_vote_overwrited); + const char *l_vote_hash_str = dap_hash_fast_to_str_static(&((struct vote *)l_vote_overwrited->data)->vote_hash); DAP_DELETE(l_vote_overwrited->data); + l_voting->votes = dap_list_delete_link(l_voting->votes, l_vote_overwrited); + log_it(L_NOTICE, "Vote %s of voting %s has been changed", l_vote_hash_str, dap_hash_fast_to_str_static(&l_voting->hash)); + } else { + const char *l_vote_hash_str = dap_hash_fast_to_str_static(a_tx_hash); + log_it(L_NOTICE, "Vote %s of voting %s has been accepted", l_vote_hash_str, dap_hash_fast_to_str_static(&l_voting->hash)); } + l_voting->votes = dap_list_append(l_voting->votes, l_vote_item); - char l_vote_hash_str[DAP_HASH_FAST_STR_SIZE]; - dap_hash_fast_to_str(a_tx_hash, l_vote_hash_str, DAP_HASH_FAST_STR_SIZE); - log_it(L_INFO, "Vote %s of voting %s has been %s", l_vote_hash_str, dap_hash_fast_to_str_static(&l_voting->hash), - l_vote_overwrited ? "changed" : "accepted"); + } + if (a_pkey_hash) + *a_pkey_hash = l_voting->params->vote_changing_allowed ? l_pkey_hash : (dap_hash_fast_t) { }; return DAP_LEDGER_CHECK_OK; } -int s_voting_ledger_verificator_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) - return s_voting_verificator(a_ledger, a_tx_in, a_tx_hash, a_apply); - if (a_type == TX_ITEM_TYPE_VOTE) - return s_vote_verificator(a_ledger, a_tx_in, a_tx_hash, a_apply); - log_it(L_ERROR, "Item %d is not supported in votings", a_type); - return -3; -} - static inline bool s_vote_delete(dap_chain_net_id_t a_net_id, dap_chain_datum_tx_t *a_vote_tx, dap_hash_fast_t *a_vote_tx_hash) { dap_chain_tx_vote_t *l_vote_tx_item = (dap_chain_tx_vote_t *)dap_chain_datum_tx_item_get(a_vote_tx, NULL, NULL, TX_ITEM_TYPE_VOTE, NULL); @@ -559,6 +569,7 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) const char* l_max_votes_count_str = NULL; const char* l_fee_str = NULL; const char* l_wallet_str = NULL; + const char *l_token_str = NULL; dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-question", &l_question_str); if (!l_question_str){ @@ -591,8 +602,6 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) return -DAP_CHAIN_NET_VOTE_CREATE_CONTAIN_MAX_OPTIONS; } - dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-expire", &l_voting_expire_str); - dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-max_votes_count", &l_max_votes_count_str); dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-fee", &l_fee_str); if (!l_fee_str){ dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_FEE_PARAM_NOT_VALID, "Voting requires paramete -fee to be valid."); @@ -606,6 +615,7 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) return -DAP_CHAIN_NET_VOTE_CREATE_WALLET_PARAM_NOT_VALID; } + dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-expire", &l_voting_expire_str); dap_time_t l_time_expire = 0; if (l_voting_expire_str) l_time_expire = dap_time_from_str_rfc822(l_voting_expire_str); @@ -614,6 +624,8 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) "Wrong time format. -expire parameter must be in format \"Day Month Year HH:MM:SS Timezone\" e.g. \"19 August 2024 22:00:00 +00\""); return -DAP_CHAIN_NET_VOTE_CREATE_WRONG_TIME_FORMAT; } + + dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-max_votes_count", &l_max_votes_count_str); uint64_t l_max_count = 0; if (l_max_votes_count_str) l_max_count = strtoul(l_max_votes_count_str, NULL, 10); @@ -621,14 +633,25 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) bool l_is_delegated_key = dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-delegated_key_required", NULL) ? true : false; bool l_is_vote_changing_allowed = dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-vote_changing_allowed", NULL) ? true : false; const char *c_wallets_path = dap_chain_wallet_get_path(g_config); - dap_chain_wallet_t *l_wallet_fee = dap_chain_wallet_open(l_wallet_str, c_wallets_path,NULL); + dap_chain_wallet_t *l_wallet_fee = dap_chain_wallet_open(l_wallet_str, c_wallets_path, NULL); if (!l_wallet_fee) { dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_WALLET_DOES_NOT_EXIST, "Wallet %s does not exist", l_wallet_str); return -DAP_CHAIN_NET_VOTE_CREATE_WALLET_DOES_NOT_EXIST; } char *l_hash_ret = NULL; - int res = dap_chain_net_srv_voting_create(l_question_str, l_options_list, l_time_expire, l_max_count, l_value_fee, l_is_delegated_key, l_is_vote_changing_allowed, l_wallet_fee, l_net, l_hash_out_type, &l_hash_ret); + int res = dap_chain_net_srv_voting_create(l_question_str, + l_options_list, + l_time_expire, + l_max_count, + l_value_fee, + l_is_delegated_key, + l_is_vote_changing_allowed, + l_wallet_fee, + l_net, + l_token_str, + l_hash_out_type, + &l_hash_ret); dap_list_free(l_options_list); dap_chain_wallet_close(l_wallet_fee); @@ -719,8 +742,10 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) } dap_hash_fast_t l_voting_hash = {}; - dap_chain_hash_fast_from_str(l_hash_str, &l_voting_hash); - + if (dap_chain_hash_fast_from_str(l_hash_str, &l_voting_hash)) { + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_HASH_INVALID, "Hash string is not recognozed as hex of base58 hash"); + return -DAP_CHAIN_NET_VOTE_VOTING_HASH_INVALID; + } dap_chain_hash_fast_t l_pkey_hash; dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-cert", &l_cert_name); @@ -756,8 +781,8 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) } const char *c_wallets_path = dap_chain_wallet_get_path(g_config); - dap_chain_wallet_t *l_wallet_fee = dap_chain_wallet_open(l_wallet_str, c_wallets_path,NULL); - if (!l_wallet_fee) { + dap_chain_wallet_t *l_wallet = dap_chain_wallet_open(l_wallet_str, c_wallets_path, NULL); + if (!l_wallet) { dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_WALLET_DOES_NOT_EXIST, "Wallet %s does not exist", l_wallet_str); return -DAP_CHAIN_NET_VOTE_VOTING_WALLET_DOES_NOT_EXIST; } @@ -766,9 +791,9 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) char *l_hash_tx; - int res = dap_chain_net_srv_vote_create(l_cert, l_value_fee, l_wallet_fee, &l_voting_hash, l_option_idx_count, + int res = dap_chain_net_srv_vote_create(l_cert, l_value_fee, l_wallet, &l_voting_hash, l_option_idx_count, l_net, l_hash_out_type, &l_hash_tx); - dap_chain_wallet_close(l_wallet_fee); + dap_chain_wallet_close(l_wallet); switch (res) { case DAP_CHAIN_NET_VOTE_VOTING_OK: { @@ -788,16 +813,12 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) case DAP_CHAIN_NET_VOTE_VOTING_ALREADY_EXPIRED: { dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_ALREADY_EXPIRED, "This voting already expired."); } break; - case DAP_CHAIN_NET_VOTE_VOTING_NO_KEY_FOUND_IN_CERT: { - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_NO_KEY_FOUND_IN_CERT, - "No key found in \"%s\" certificate", l_cert_name); - } break; - case DAP_CHAIN_NET_VOTE_VOTING_CERT_REQUIRED: { + case DAP_CHAIN_NET_VOTE_VOTING_CERT_REQUIRED: { dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_CERT_REQUIRED, "This voting required a delegated key. Parameter -cert must contain a valid certificate name"); } break; - case DAP_CHAIN_NET_VOTE_VOTING_NO_PUBLIC_KEY_IN_CERT: { - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_NO_PUBLIC_KEY_IN_CERT, + case DAP_CHAIN_NET_VOTE_VOTING_NO_KEY_FOUND_IN_CERT: { + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_NO_KEY_FOUND_IN_CERT, "Can't serialize public key of certificate \"%s\"", l_cert_name); } break; @@ -859,6 +880,7 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) json_object_new_string_len(dap_chain_hash_fast_to_str_static(&it->hash), sizeof(dap_hash_str_t)) ); json_object_object_add( json_obj_vote, "question", json_object_new_string(it->params->question) ); + json_object_object_add(json_obj_vote, "token", json_object_new_string(it->params->token_ticker)); json_object_array_add(json_arr_voting_out, json_obj_vote); } json_object_array_add(*json_arr_reply, json_arr_voting_out); @@ -908,6 +930,7 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) json_object* json_vote_out = json_object_new_object(); json_object_object_add(json_vote_out, "voting_tx", json_object_new_string_len(l_hash_str, sizeof(dap_hash_str_t))); json_object_object_add(json_vote_out, "question", json_object_new_string(l_voting->params->question)); + json_object_object_add(json_vote_out, "token", json_object_new_string(l_voting->params->token_ticker)); if (l_voting->params->voting_expire) { char l_tmp_buf[DAP_TIME_STR_SIZE]; dap_time_to_str_rfc822(l_tmp_buf, DAP_TIME_STR_SIZE, l_voting->params->voting_expire); @@ -952,7 +975,6 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) const char *l_tw_coins, *l_tw_datoshi = dap_uint256_to_char(l_total_weight, &l_tw_coins); json_object_object_add(json_vote_out, "total_sum", json_object_new_string(l_tw_coins)); json_object_object_add(json_vote_out, "total_sum_datoshi", json_object_new_string(l_tw_datoshi)); - json_object_object_add(json_vote_out, "ticker", json_object_new_string(l_net->pub.native_ticker)); json_object_array_add(*json_arr_reply, json_vote_out); } break; default: @@ -962,33 +984,25 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) } int dap_chain_net_srv_voting_create(const char *a_question, dap_list_t *a_options, dap_time_t a_expire_vote, - uint64_t a_max_vote, uint256_t a_fee, bool a_delegated_key_required, - bool a_vote_changing_allowed, dap_chain_wallet_t *a_wallet, - dap_chain_net_t *a_net, const char *a_hash_out_type, char **a_hash_output) + uint64_t a_max_vote, uint256_t a_fee, bool a_delegated_key_required, + bool a_vote_changing_allowed, dap_chain_wallet_t *a_wallet, + dap_chain_net_t *a_net, const char *a_token_ticker, + const char *a_hash_out_type, char **a_hash_output) { - if (strlen(a_question) > DAP_CHAIN_DATUM_TX_VOTING_QUESTION_MAX_LENGTH){ + if (strlen(a_question) > DAP_CHAIN_DATUM_TX_VOTING_QUESTION_MAX_LENGTH) return DAP_CHAIN_NET_VOTE_CREATE_LENGTH_QUESTION_OVERSIZE_MAX; - } // Parse options list - - if(dap_list_length(a_options) > DAP_CHAIN_DATUM_TX_VOTING_OPTION_MAX_COUNT){ + if (dap_list_length(a_options) > DAP_CHAIN_DATUM_TX_VOTING_OPTION_MAX_COUNT) return DAP_CHAIN_NET_VOTE_CREATE_COUNT_OPTION_OVERSIZE_MAX; - } - if (IS_ZERO_256(a_fee)) { + if (IS_ZERO_256(a_fee)) return DAP_CHAIN_NET_VOTE_CREATE_FEE_IS_ZERO; - } - - dap_enc_key_t *l_priv_key = NULL; - l_priv_key = dap_chain_wallet_get_key(a_wallet, 0); const dap_chain_addr_t *l_addr_from = (const dap_chain_addr_t *) dap_chain_wallet_get_addr(a_wallet, a_net->pub.id); - - if(!l_addr_from) { + if(!l_addr_from) return DAP_CHAIN_NET_VOTE_CREATE_SOURCE_ADDRESS_IS_INVALID; - } const char *l_native_ticker = a_net->pub.native_ticker; uint256_t l_net_fee = {}, l_total_fee = {}, l_value_transfer; @@ -1081,6 +1095,16 @@ int dap_chain_net_srv_voting_create(const char *a_question, dap_list_t *a_option DAP_DEL_Z(l_vote_changing_item); } + if (a_token_ticker) { + dap_chain_tx_tsd_t *l_voting_token_item = dap_chain_datum_voting_token_tsd_create(a_token_ticker); + if (!l_voting_token_item) { + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_CREATE_TSD_TOKEN; + } + dap_chain_datum_tx_add_item(&l_tx, l_voting_token_item); + DAP_DEL_Z(l_voting_token_item); + } + // add 'in' items uint256_t l_value_to_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out); assert(EQUAL_256(l_value_to_items, l_value_transfer)); @@ -1088,7 +1112,7 @@ int dap_chain_net_srv_voting_create(const char *a_question, dap_list_t *a_option uint256_t l_value_pack = {}; // Network fee if (l_net_fee_used) { - if (dap_chain_datum_tx_add_out_item(&l_tx, &l_addr_fee, l_net_fee) == 1) + if (dap_chain_datum_tx_add_out_ext_item(&l_tx, &l_addr_fee, l_net_fee, l_native_ticker) == 1) SUM_256_256(l_value_pack, l_net_fee, &l_value_pack); else { dap_chain_datum_tx_delete(l_tx); @@ -1108,17 +1132,20 @@ int dap_chain_net_srv_voting_create(const char *a_question, dap_list_t *a_option uint256_t l_value_back; SUBTRACT_256_256(l_value_transfer, l_value_pack, &l_value_back); if(!IS_ZERO_256(l_value_back)) { - if(dap_chain_datum_tx_add_out_item(&l_tx, l_addr_from, l_value_back) != 1) { + if(dap_chain_datum_tx_add_out_ext_item(&l_tx, l_addr_from, l_value_back, l_native_ticker) != 1) { dap_chain_datum_tx_delete(l_tx); return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_ADD_OUT_WITH_VALUE_BACK; } } // add 'sign' items - if(dap_chain_datum_tx_add_sign_item(&l_tx, l_priv_key) != 1) { + dap_enc_key_t *l_priv_key = dap_chain_wallet_get_key(a_wallet, 0); + if (dap_chain_datum_tx_add_sign_item(&l_tx, l_priv_key) != 1) { dap_chain_datum_tx_delete(l_tx); + dap_enc_key_delete(l_priv_key); return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_SIGNED_TX; } + dap_enc_key_delete(l_priv_key); size_t l_tx_size = dap_chain_datum_tx_get_size(l_tx); dap_hash_fast_t l_tx_hash; @@ -1141,8 +1168,6 @@ int dap_chain_net_srv_vote_create(dap_cert_t *a_cert, uint256_t a_fee, dap_chain uint64_t a_option_idx, dap_chain_net_t *a_net, const char *a_hash_out_type, char **a_hash_tx_out) { - - struct voting *l_voting = s_voting_find(a_net->pub.id, a_voting_hash); if (!l_voting) return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_FIND_VOTE; @@ -1153,47 +1178,44 @@ int dap_chain_net_srv_vote_create(dap_cert_t *a_cert, uint256_t a_fee, dap_chain if (l_voting->params->voting_expire && dap_time_now() > l_voting->params->voting_expire) return DAP_CHAIN_NET_VOTE_VOTING_ALREADY_EXPIRED; + + dap_chain_addr_t *l_addr_from = dap_chain_wallet_get_addr(a_wallet, a_net->pub.id); + if (!l_addr_from) + return DAP_CHAIN_NET_VOTE_VOTING_SOURCE_ADDRESS_INVALID; + dap_hash_fast_t l_pkey_hash = {0}; if (l_voting->params->delegate_key_required) { if (!a_cert) return DAP_CHAIN_NET_VOTE_VOTING_CERT_REQUIRED; - if (!a_cert->enc_key) + if (dap_cert_get_pkey_hash(a_cert, &l_pkey_hash)) return DAP_CHAIN_NET_VOTE_VOTING_NO_KEY_FOUND_IN_CERT; - // Get publivc key hash - size_t l_pub_key_size = 0; - uint8_t *l_pub_key = dap_enc_key_serialize_pub_key(a_cert->enc_key, &l_pub_key_size);; - if (l_pub_key == NULL) - return DAP_CHAIN_NET_VOTE_VOTING_NO_PUBLIC_KEY_IN_CERT; - - dap_hash_fast(l_pub_key, l_pub_key_size, &l_pkey_hash); - DAP_DELETE(l_pub_key); if (!dap_chain_net_srv_stake_check_pkey_hash(a_net->pub.id, &l_pkey_hash)) return DAP_CHAIN_NET_VOTE_VOTING_KEY_IS_NOT_DELEGATED; - for (dap_list_t *it = l_voting->votes; it; it = it->next) - if (dap_hash_fast_compare(&((struct vote *)it->data)->pkey_hash, &l_pkey_hash) && - !l_voting->params->vote_changing_allowed) - return DAP_CHAIN_NET_VOTE_VOTING_DOES_NOT_ALLOW_CHANGE_YOUR_VOTE; - } - - dap_enc_key_t *l_priv_key = NULL; - - l_priv_key = dap_chain_wallet_get_key(a_wallet, 0); - - const dap_chain_addr_t *l_addr_from = (const dap_chain_addr_t *) dap_chain_wallet_get_addr(a_wallet, a_net->pub.id); + } else + l_pkey_hash = l_addr_from->data.hash_fast; - if (!l_addr_from) - return DAP_CHAIN_NET_VOTE_VOTING_SOURCE_ADDRESS_INVALID; + bool l_vote_changed = false; + for (dap_list_t *it = l_voting->votes; it; it = it->next) + if (dap_hash_fast_compare(&((struct vote *)it->data)->pkey_hash, &l_pkey_hash)) { + if (!l_voting->params->vote_changing_allowed) + return DAP_CHAIN_NET_VOTE_VOTING_DOES_NOT_ALLOW_CHANGE_YOUR_VOTE; + l_vote_changed = true; + break; + } - const char *l_native_ticker = a_net->pub.native_ticker; - uint256_t l_net_fee = {}, l_total_fee = {}, l_value_transfer; + const char *l_token_ticker = l_voting->params->token_ticker; + uint256_t l_net_fee = {}, l_total_fee = a_fee, l_value_transfer, l_fee_transfer; dap_chain_addr_t l_addr_fee = {}; bool l_net_fee_used = dap_chain_net_tx_get_fee(a_net->pub.id, &l_net_fee, &l_addr_fee); - SUM_256_256(l_net_fee, a_fee, &l_total_fee); - - dap_ledger_t* l_ledger = dap_ledger_by_net_name(a_net->pub.name); - dap_list_t *l_list_used_out = dap_ledger_get_list_tx_outs(l_ledger, l_native_ticker, l_addr_from, &l_value_transfer); - if (!l_list_used_out || compare256(l_value_transfer, l_total_fee) <= 0) { + if (l_net_fee_used) + SUM_256_256(l_net_fee, a_fee, &l_total_fee); + + bool l_native_tx = !dap_strcmp(l_token_ticker, a_net->pub.native_ticker); + dap_ledger_t *l_ledger = a_net->pub.ledger; + dap_list_t *l_list_used_out = dap_ledger_get_list_tx_outs(l_ledger, l_token_ticker, l_addr_from, &l_value_transfer); + if (!l_list_used_out || (l_native_tx && compare256(l_value_transfer, l_total_fee) < 0)) { + dap_list_free_full(l_list_used_out, NULL); return DAP_CHAIN_NET_VOTE_VOTING_NOT_ENOUGH_FUNDS_TO_TRANSFER; } @@ -1203,16 +1225,17 @@ int dap_chain_net_srv_vote_create(dap_cert_t *a_cert, uint256_t a_fee, dap_chain DL_FOREACH_SAFE(l_list_used_out, it, tmp) { dap_chain_tx_used_out_item_t *l_out = (dap_chain_tx_used_out_item_t *)it->data; uint256_t l_uncoloured_value = dap_ledger_coin_get_uncoloured_value(a_net->pub.ledger, a_voting_hash, - &l_out->tx_hash_fast, l_out->num_idx_out); + &l_out->tx_hash_fast, l_out->num_idx_out, + l_vote_changed ? &l_pkey_hash : NULL); if (IS_ZERO_256(l_uncoloured_value)) { - dap_list_delete_link(l_list_used_out, it); + l_list_used_out = dap_list_delete_link(l_list_used_out, it); continue; } if (SUM_256_256(l_value_transfer_new, l_out->value, &l_value_transfer_new)) return DAP_CHAIN_NET_VOTE_VOTING_INTEGER_OVERFLOW; } - if ((IS_ZERO_256(l_value_transfer_new) || compare256(l_value_transfer_new, l_total_fee) <= 0)) + if (IS_ZERO_256(l_value_transfer_new) || (l_native_tx && compare256(l_value_transfer_new, l_total_fee) <= 0)) return DAP_CHAIN_NET_VOTE_VOTING_UNSPENT_UTX0_FOR_PARTICIPATION_THIS_VOTING; l_value_transfer = l_value_transfer_new; @@ -1220,6 +1243,27 @@ int dap_chain_net_srv_vote_create(dap_cert_t *a_cert, uint256_t a_fee, dap_chain // create empty transaction dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create(); + // add 'in' items for fee + uint256_t l_value_back = l_value_transfer, l_fee_back = {}; + if (!l_native_tx) { + dap_list_t *l_list_fee_outs = dap_chain_wallet_get_list_tx_outs_with_val(l_ledger, a_net->pub.native_ticker, + l_addr_from, l_total_fee, &l_fee_transfer); + if (!l_list_fee_outs) { + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_VOTING_NOT_ENOUGH_FUNDS_TO_TRANSFER; + } + uint256_t l_value_fee_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_fee_outs); + assert(EQUAL_256(l_value_fee_items, l_fee_transfer)); + dap_list_free_full(l_list_fee_outs, NULL); + SUBTRACT_256_256(l_fee_transfer, l_total_fee, &l_fee_back); + } else + SUBTRACT_256_256(l_value_transfer, l_total_fee, &l_value_back); + + // add 'in' items + uint256_t l_value_to_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out); + assert(EQUAL_256(l_value_to_items, l_value_transfer)); + dap_list_free_full(l_list_used_out, NULL); + // Add vote item if (a_option_idx > dap_list_length(l_voting->params->options)){ dap_chain_datum_tx_delete(l_tx); @@ -1233,24 +1277,26 @@ int dap_chain_net_srv_vote_create(dap_cert_t *a_cert, uint256_t a_fee, dap_chain dap_chain_datum_tx_add_item(&l_tx, l_vote_item); DAP_DEL_Z(l_vote_item); - // add stake out conds items + // add out conds items int err = 0; - dap_list_t *l_outs = dap_ledger_get_list_tx_cond_outs(l_ledger, l_addr_from); + dap_list_t *l_outs = dap_ledger_get_list_tx_cond_outs(l_ledger, DAP_CHAIN_TX_OUT_COND_SUBTYPE_ALL, l_token_ticker, l_addr_from); for (dap_list_t *it = l_outs; it; it = it->next) { dap_chain_tx_used_out_item_t *l_out_item = (dap_chain_tx_used_out_item_t *)it->data; - uint256_t l_value = dap_ledger_coin_get_uncoloured_value(l_ledger, a_voting_hash, &l_out_item->tx_hash_fast, l_out_item->num_idx_out); - if (IS_ZERO_256(l_value)) + uint256_t l_uncoloured_value = dap_ledger_coin_get_uncoloured_value(l_ledger, a_voting_hash, + &l_out_item->tx_hash_fast, l_out_item->num_idx_out, + l_vote_changed ? &l_pkey_hash : NULL); + if (IS_ZERO_256(l_uncoloured_value)) continue; dap_chain_tx_tsd_t *l_item = dap_chain_datum_voting_vote_tx_cond_tsd_create(l_out_item->tx_hash_fast, l_out_item->num_idx_out); if (!l_item) { err = DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_CREATE_TSD_TX_COND_ITEM; break; } - if (dap_chain_datum_tx_add_item(&l_tx, l_item) != 1) { + if (dap_chain_datum_tx_add_item(&l_tx, l_item) != 1) err = DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_CREATE_TSD_TX_COND_ITEM; - break; - } DAP_DEL_Z(l_item); + if (err) + break; } dap_list_free_full(l_outs, NULL); if (err) { @@ -1258,53 +1304,44 @@ int dap_chain_net_srv_vote_create(dap_cert_t *a_cert, uint256_t a_fee, dap_chain return err; } - // add 'in' items - uint256_t l_value_to_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out); - assert(EQUAL_256(l_value_to_items, l_value_transfer)); - dap_list_free_full(l_list_used_out, NULL); - uint256_t l_value_pack = {}; // Network fee - if (l_net_fee_used) { - if (dap_chain_datum_tx_add_out_item(&l_tx, &l_addr_fee, l_net_fee) == 1) - SUM_256_256(l_value_pack, l_net_fee, &l_value_pack); - else { - dap_chain_datum_tx_delete(l_tx); - return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_NET_FEE_OUT; - } + if (l_net_fee_used && dap_chain_datum_tx_add_out_ext_item(&l_tx, &l_addr_fee, l_net_fee, a_net->pub.native_ticker) != 1) { + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_NET_FEE_OUT; } + // Validator's fee - if (!IS_ZERO_256(a_fee)) { - if (dap_chain_datum_tx_add_fee_item(&l_tx, a_fee) == 1) - SUM_256_256(l_value_pack, a_fee, &l_value_pack); - else { - dap_chain_datum_tx_delete(l_tx); - return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_NET_FEE_OUT; - } + if (!IS_ZERO_256(a_fee) && dap_chain_datum_tx_add_fee_item(&l_tx, a_fee) != 1) { + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_NET_FEE_OUT; } + // coin back - uint256_t l_value_back; - SUBTRACT_256_256(l_value_transfer, l_value_pack, &l_value_back); - if(!IS_ZERO_256(l_value_back)) { - if(dap_chain_datum_tx_add_out_item(&l_tx, l_addr_from, l_value_back) != 1) { - dap_chain_datum_tx_delete(l_tx); - return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_OUT_WITH_VALUE_BACK; - } + if (!IS_ZERO_256(l_value_back) && dap_chain_datum_tx_add_out_ext_item(&l_tx, l_addr_from, l_value_back, l_token_ticker) != 1) { + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_OUT_WITH_VALUE_BACK; + } + if (!IS_ZERO_256(l_fee_back) && dap_chain_datum_tx_add_out_ext_item(&l_tx, l_addr_from, l_fee_back, a_net->pub.native_ticker) != 1) { + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_OUT_WITH_VALUE_BACK; } + dap_enc_key_t *l_priv_key = dap_chain_wallet_get_key(a_wallet, 0); // add 'sign' items with wallet sign - if(dap_chain_datum_tx_add_sign_item(&l_tx, l_priv_key) != 1) { + if (dap_chain_datum_tx_add_sign_item(&l_tx, l_priv_key) != 1) { dap_chain_datum_tx_delete(l_tx); + dap_enc_key_delete(l_priv_key); return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_SIGN_TX; } + dap_enc_key_delete(l_priv_key); // add 'sign' items with delegated key if needed - if(a_cert){ - if(dap_chain_datum_tx_add_sign_item(&l_tx, a_cert->enc_key) != 1) { - dap_chain_datum_tx_delete(l_tx); - return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_SIGN_TX; - } + if (a_cert && dap_chain_datum_tx_add_sign_item(&l_tx, a_cert->enc_key) != 1) { + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_SIGN_TX; } + size_t l_tx_size = dap_chain_datum_tx_get_size(l_tx); dap_hash_fast_t l_tx_hash; dap_hash_fast(l_tx, l_tx_size, &l_tx_hash); diff --git a/modules/service/voting/include/dap_chain_net_srv_voting.h b/modules/service/voting/include/dap_chain_net_srv_voting.h index 89c80746ac..1fc6505066 100644 --- a/modules/service/voting/include/dap_chain_net_srv_voting.h +++ b/modules/service/voting/include/dap_chain_net_srv_voting.h @@ -83,13 +83,14 @@ enum DAP_CHAIN_NET_SRV_VOTING_CLI_ERRORS { DAP_CHAIN_NET_VOTE_CREATE_WALLET_PARAM_NOT_VALID, DAP_CHAIN_NET_VOTE_CREATE_WALLET_DOES_NOT_EXIST, DAP_CHAIN_NET_VOTE_CREATE_WRONG_TIME_FORMAT, + DAP_CHAIN_NET_VOTE_CREATE_WRONG_TOKEN, + DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_CREATE_TSD_TOKEN, DAP_CHAIN_NET_VOTE_VOTING_OK, DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_FIND_VOTE, DAP_CHAIN_NET_VOTE_VOTING_THIS_VOTING_HAVE_MAX_VALUE_VOTES, DAP_CHAIN_NET_VOTE_VOTING_ALREADY_EXPIRED, DAP_CHAIN_NET_VOTE_VOTING_NO_KEY_FOUND_IN_CERT, - DAP_CHAIN_NET_VOTE_VOTING_NO_PUBLIC_KEY_IN_CERT, DAP_CHAIN_NET_VOTE_VOTING_CERT_REQUIRED, DAP_CHAIN_NET_VOTE_VOTING_KEY_IS_NOT_DELEGATED, DAP_CHAIN_NET_VOTE_VOTING_DOES_NOT_ALLOW_CHANGE_YOUR_VOTE, @@ -106,6 +107,7 @@ enum DAP_CHAIN_NET_SRV_VOTING_CLI_ERRORS { DAP_CHAIN_NET_VOTE_VOTING_NET_PARAM_MISSING, DAP_CHAIN_NET_VOTE_VOTING_NET_PARAM_NOT_VALID, DAP_CHAIN_NET_VOTE_VOTING_HASH_NOT_FOUND, + DAP_CHAIN_NET_VOTE_VOTING_HASH_INVALID, DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_FIND_CERT, DAP_CHAIN_NET_VOTE_VOTING_FEE_PARAM_NOT_VALID, DAP_CHAIN_NET_VOTE_VOTING_FEE_PARAM_BAD_TYPE, @@ -130,9 +132,10 @@ uint64_t *dap_chain_net_srv_voting_get_result(dap_ledger_t* a_ledger, dap_chain_ dap_time_t dap_chain_net_srv_voting_get_expiration_time(dap_ledger_t *a_ledger, dap_chain_hash_fast_t *a_voting_hash); int dap_chain_net_srv_voting_create(const char *a_question, dap_list_t *a_options, dap_time_t a_expire_vote, - uint64_t a_max_vote, uint256_t a_fee, bool a_delegated_key_required, - bool a_vote_changing_allowed, dap_chain_wallet_t *a_wallet, - dap_chain_net_t *a_net, const char *a_hash_out_type, char **a_hash_output); + uint64_t a_max_vote, uint256_t a_fee, bool a_delegated_key_required, + bool a_vote_changing_allowed, dap_chain_wallet_t *a_wallet, + dap_chain_net_t *a_net, const char *a_token_ticker, + const char *a_hash_out_type, char **a_hash_output); int dap_chain_net_srv_vote_create(dap_cert_t *a_cert, uint256_t a_fee, dap_chain_wallet_t *a_wallet, dap_hash_fast_t *a_voting_hash, uint64_t a_option_idx, dap_chain_net_t *a_net, const char *a_hash_out_type, char **a_hash_tx_out); -- GitLab