diff --git a/dap-sdk b/dap-sdk index a7b6da64ee24a3aba64dcbbd5f3f356069d853ad..f01ce0c6fc7ad0ad0cc4975e46f281b2c9a893d0 160000 --- a/dap-sdk +++ b/dap-sdk @@ -1 +1 @@ -Subproject commit a7b6da64ee24a3aba64dcbbd5f3f356069d853ad +Subproject commit f01ce0c6fc7ad0ad0cc4975e46f281b2c9a893d0 diff --git a/modules/common/dap_chain_datum_tx_voting.c b/modules/common/dap_chain_datum_tx_voting.c index 2a161dd3a93ce110042cf4c8c30030a536e9ec28..eb2ab0320c2da6b324b7dcf002790f0b6644c9f7 100644 --- a/modules/common/dap_chain_datum_tx_voting.c +++ b/modules/common/dap_chain_datum_tx_voting.c @@ -124,6 +124,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 = { @@ -176,6 +188,9 @@ json_object *dap_chain_datum_tx_item_voting_tsd_to_json(dap_chain_datum_tx_t* a_ case VOTING_TSD_TYPE_ANSWER: 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/common/include/dap_chain_datum_tx_voting.h b/modules/common/include/dap_chain_datum_tx_voting.h index a87c67824ce16362b1e40c0822a3b47737e94fe9..add632997a948cee77c4aeb2d464f4ff9979ed03 100644 --- a/modules/common/include/dap_chain_datum_tx_voting.h +++ b/modules/common/include/dap_chain_datum_tx_voting.h @@ -43,7 +43,8 @@ typedef enum dap_chain_datum_voting_tsd_type { VOTING_TSD_TYPE_MAX_VOTES_COUNT, VOTING_TSD_TYPE_DELEGATED_KEY_REQUIRED, VOTING_TSD_TYPE_VOTE_CHANGING_ALLOWED, - VOTING_TSD_TYPE_VOTE_TX_COND + VOTING_TSD_TYPE_VOTE_TX_COND, + VOTING_TSD_TYPE_TOKEN } dap_chain_datum_voting_tsd_type_t; typedef struct dap_chain_tx_voting { @@ -63,13 +64,14 @@ typedef struct dap_chain_tx_vote { typedef struct dap_chain_datum_tx_voting_params { - char *voting_question; + char *voting_question; dap_list_t *answers_list; uint8_t answers_count; 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 @@ -86,6 +88,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); @@ -96,4 +99,4 @@ json_object *dap_chain_datum_tx_item_vote_to_json(dap_chain_tx_vote_t *a_vote, d #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/modules/consensus/esbocs/dap_chain_cs_esbocs.c b/modules/consensus/esbocs/dap_chain_cs_esbocs.c index 88546b2272cf201be28153caa0099fd8a14a22dc..dea80a38fde2eb5ea503349ceba3f17ed8450ec7 100644 --- a/modules/consensus/esbocs/dap_chain_cs_esbocs.c +++ b/modules/consensus/esbocs/dap_chain_cs_esbocs.c @@ -2774,6 +2774,9 @@ static size_t s_callback_block_sign(dap_chain_cs_blocks_t *a_blocks, dap_chain_b static uint64_t s_get_precached_key_hash(dap_list_t **a_precached_keys_list, dap_sign_t *a_source_sign, dap_hash_fast_t *a_result) { + if (DAP_SIGN_GET_PKEY_HASHING_FLAG(a_source_sign->header.hash_type)) + return !dap_sign_get_pkey_hash(a_source_sign, a_result); + bool l_found = false; struct precached_key *l_key = NULL; dap_list_t *l_cur; diff --git a/modules/net/dap_chain_ledger.c b/modules/net/dap_chain_ledger.c index 1a883dd8dd234777d93cd68e95586b140b31d11d..0d4530aca7b7bfc399dca1ab21b7c65742334871 100644 --- a/modules/net/dap_chain_ledger.c +++ b/modules/net/dap_chain_ledger.c @@ -288,8 +288,6 @@ typedef struct dap_ledger_private { #define PVT(a) ( (dap_ledger_private_t *) a->_internal ) -static dap_ledger_tx_item_t* 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 void s_threshold_emissions_proc( dap_ledger_t * a_ledger); static void s_threshold_txs_proc( dap_ledger_t * a_ledger); static void s_threshold_txs_free(dap_ledger_t *a_ledger); @@ -4190,13 +4188,14 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger, debug_if(s_debug_more, L_WARNING, "Verificator check error for voting item"); l_err_num = DAP_LEDGER_TX_CHECK_NO_VERIFICATOR_SET; } - // if (a_tag) + // if (a_tag) // a_tag->uint64 = DAP_CHAIN_TX_TAG_ACTION_VOTING; - if (a_action) + if (a_action) *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 (!s_check_hal(a_ledger, a_tx_hash) && + (l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTE, a_tx, a_tx_hash, false))) { debug_if(s_debug_more, L_WARNING, "Verificator check error %d for vote", l_err_num); l_err_num = DAP_LEDGER_TX_CHECK_VERIFICATOR_CHECK_FAILURE; } @@ -4204,8 +4203,6 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger, debug_if(s_debug_more, L_WARNING, "Verificator check error for vote item"); l_err_num = DAP_LEDGER_TX_CHECK_NO_VERIFICATOR_SET; } - // if (a_tag) - // a_tag->uint64 = DAP_CHAIN_TX_TAG_ACTION_VOTE; if (a_action) *a_action = DAP_CHAIN_TX_TAG_ACTION_VOTE; } @@ -4616,7 +4613,8 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha 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, a_tx_hash, true); } - assert(!l_err_num); + if (!s_check_hal(a_ledger, a_tx_hash)) + assert(!l_err_num); // add transaction to the cache list 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_chain_hash_fast_t)); @@ -5273,8 +5271,8 @@ uint256_t dap_ledger_calc_balance_full(dap_ledger_t *a_ledger, const dap_chain_a * 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 *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; @@ -5331,20 +5329,7 @@ static dap_ledger_tx_item_t *tx_item_find_by_addr(dap_ledger_t *a_ledger, const 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 = 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; } 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) @@ -5374,52 +5359,6 @@ static dap_ledger_tx_item_t *tx_item_find_by_addr(dap_ledger_t *a_ledger, const 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_sign_get_sig(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 Get all transactions from the cache with the out_cond item * @param a_ledger @@ -5442,93 +5381,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; -} - /** * @brief dap_ledger_get_list_tx_outs_with_val * @param a_ledger @@ -5784,7 +5636,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; @@ -5793,72 +5645,120 @@ dap_chain_datum_tx_t *dap_ledger_datum_iter_get_last(dap_ledger_datum_iter_t *a_ return a_iter->cur; } - /** - * @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; + // 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->cache_data.tx_hash_spent_fast[l_out_idx])) + continue; + dap_hash_fast_t l_owner_tx_hash = dap_ledger_get_first_chain_tx_hash(a_ledger, it->tx, a_subtype); + 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_sign_get_sig(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 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_transfer) +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 = { }; - uint256_t l_value_transfer = { }; - dap_chain_datum_tx_t *l_tx; - while(( 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); - } + 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; +} - return 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 ); +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, l_check_tx, a_cond_subtype); + 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_sign_get_sig((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); } void dap_ledger_tx_add_notify(dap_ledger_t *a_ledger, dap_ledger_tx_add_notify_t a_callback, void *a_arg) { diff --git a/modules/net/dap_chain_net_tx.c b/modules/net/dap_chain_net_tx.c index addcb27c56eff00a0018ff62fc0785ebbc1eba8a..937c0464f50f9b54297dfce0366f9d6e62749d47 100644 --- a/modules/net/dap_chain_net_tx.c +++ b/modules/net/dap_chain_net_tx.c @@ -1633,7 +1633,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->voting_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->answers_list; uint8_t l_index = 0; while (l_temp) { @@ -1643,7 +1643,7 @@ int dap_chain_net_tx_to_json(dap_chain_datum_tx_t *a_tx, json_object *a_out_json } if (l_voting_params->voting_expire) { dap_time_to_str_rfc822(l_tmp_buf, DAP_TIME_STR_SIZE, l_voting_params->voting_expire); - json_object_object_add(json_obj_item,"Voting expire", json_object_new_string(l_tmp_buf)); + json_object_object_add(json_obj_item, "Voting expire", json_object_new_string(l_tmp_buf)); } 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)); diff --git a/modules/net/dap_chain_node_cli.c b/modules/net/dap_chain_node_cli.c index 5faac2932007afb9fc02e5d573a9f7dbbd9ec26f..85b96544b5c02fdaa9bf489c111834adb9db1db6 100644 --- a/modules/net/dap_chain_node_cli.c +++ b/modules/net/dap_chain_node_cli.c @@ -159,7 +159,7 @@ int dap_chain_node_cli_init(dap_config_t * g_config) "wallet info {-addr <addr> | -w <wallet_name>} -net <net_name>\n" "wallet activate -w <wallet_name> -password <password> [-ttl <password_ttl_in_minutes>]\n" "wallet deactivate -w <wallet_name>>\n" - "wallet outputs {-addr <addr> | -w <wallet_name>} -net <net_name> -token <token_tiker> [-value <uint256_value>]\n" + "wallet outputs {-addr <addr> | -w <wallet_name>} -net <net_name> -token <token_tiker> [{-cond | -value <uint256_value>}]\n" "wallet convert -w <wallet_name> {-password <password> | -remove_password }\n"); diff --git a/modules/net/dap_chain_node_cli_cmd.c b/modules/net/dap_chain_node_cli_cmd.c index a5db76bcfe770a5633fe288d5dbbf233d0d0428f..fa3c41d20b62e0da62a48baaf8e1d7b597457019 100644 --- a/modules/net/dap_chain_node_cli_cmd.c +++ b/modules/net/dap_chain_node_cli_cmd.c @@ -1799,45 +1799,55 @@ int l_arg_index = 1, l_rc, cmd_num = CMD_NONE; return DAP_CHAIN_NODE_CLI_COM_TX_WALLET_PARAM_ERR; } json_object * json_obj_wall = json_object_new_object(); - const char* l_value_str = NULL; - dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-value", &l_value_str); + const char *l_value_str = NULL; + uint256_t l_value_datoshi = uint256_0, l_value_sum = uint256_0; + bool l_cond_outs = dap_cli_server_cmd_check_option(a_argv, l_arg_index, a_argc, "-cond") != -1; + if (!l_cond_outs) { + dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-value", &l_value_str); + if (l_value_str) { + l_value_datoshi = dap_chain_balance_scan(l_value_str); + if (IS_ZERO_256(l_value_datoshi)) { + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_TX_WALLET_PARAM_ERR, + "Can't convert -value param to 256bit integer"); + json_object_put(json_arr_out); + return DAP_CHAIN_NODE_CLI_COM_TX_WALLET_PARAM_ERR; + } + } + } dap_list_t *l_outs_list = NULL; - uint256_t l_value_sum = uint256_0; - - - - if (l_value_str){ - uint256_t l_value_datoshi = dap_chain_balance_scan(l_value_str); + if (l_cond_outs) + l_outs_list = dap_ledger_get_list_tx_cond_outs(l_net->pub.ledger, DAP_CHAIN_TX_OUT_COND_SUBTYPE_ALL, l_token_tiker, l_addr); + else if (l_value_str) { if (dap_chain_wallet_cache_tx_find_outs_with_val(l_net, l_token_tiker, l_addr, &l_outs_list, l_value_datoshi, &l_value_sum)) - l_outs_list = dap_ledger_get_list_tx_outs_with_val(l_net->pub.ledger, l_token_tiker, l_addr, l_value_datoshi, &l_value_sum); + l_outs_list = dap_ledger_get_list_tx_outs_with_val(l_net->pub.ledger, l_token_tiker, l_addr, l_value_datoshi, &l_value_sum); } else { if (dap_chain_wallet_cache_tx_find_outs(l_net, l_token_tiker, l_addr, &l_outs_list, &l_value_sum)) l_outs_list = dap_ledger_get_list_tx_outs(l_net->pub.ledger, l_token_tiker, l_addr, &l_value_sum); } json_object_object_add(json_obj_wall, "wallet_addr", json_object_new_string(dap_chain_addr_to_str_static(l_addr))); - const char *l_out_total_value_str = dap_chain_balance_print(l_value_sum); - const char *l_out_total_value_coins_str = dap_chain_balance_to_coins(l_value_sum); - json_object_object_add(json_obj_wall, "total_value_coins", json_object_new_string(l_out_total_value_coins_str)); - json_object_object_add(json_obj_wall, "total_value_datoshi", json_object_new_string(l_out_total_value_str)); - DAP_DEL_Z(l_out_total_value_str); - DAP_DEL_Z(l_out_total_value_coins_str); struct json_object *l_json_outs_arr = json_object_new_array(); - for (dap_list_t *l_temp = l_outs_list; l_temp; l_temp = l_temp->next){ + if (!l_json_outs_arr) + return json_object_put(json_arr_out), DAP_CHAIN_NODE_CLI_COM_TX_WALLET_MEMORY_ERR; + for (dap_list_t *l_temp = l_outs_list; l_temp; l_temp = l_temp->next) { + json_object *json_obj_item = json_object_new_object(); + if (!json_obj_item) + return json_object_put(json_arr_out), DAP_CHAIN_NODE_CLI_COM_TX_WALLET_MEMORY_ERR; dap_chain_tx_used_out_item_t *l_item = l_temp->data; - json_object* json_obj_item = json_object_new_object(); - const char *l_out_value_str = dap_chain_balance_print(l_item->value); - const char *l_out_value_coins_str = dap_chain_balance_to_coins(l_item->value); - json_object_object_add(json_obj_item,"item_type", json_object_new_string("unspent_out")); + const char *l_out_value_coins_str, *l_out_value_str = dap_uint256_to_char(l_item->value, &l_out_value_coins_str); + json_object_object_add(json_obj_item,"item_type", json_object_new_string(l_cond_outs ? "unspent_cond_out" : "unspent_out")); json_object_object_add(json_obj_item,"value_coins", json_object_new_string(l_out_value_coins_str)); json_object_object_add(json_obj_item,"value_datosi", json_object_new_string(l_out_value_str)); - json_object_object_add(json_obj_item,"prev_hash", json_object_new_string(dap_hash_fast_to_str_static(&l_item->tx_hash_fast))); - json_object_object_add(json_obj_item,"out_prev_idx", json_object_new_int64(l_item->num_idx_out)); + json_object_object_add(json_obj_item,"prev_hash", json_object_new_string(dap_hash_fast_to_str_static(&l_item->tx_hash_fast))); + json_object_object_add(json_obj_item,"out_prev_idx", json_object_new_int64(l_item->num_idx_out)); json_object_array_add(l_json_outs_arr, json_obj_item); - DAP_DEL_Z(l_out_value_str); - DAP_DEL_Z(l_out_value_coins_str); + if (l_cond_outs) + SUM_256_256(l_value_sum, l_item->value, &l_value_sum); } dap_list_free_full(l_outs_list, NULL); + const char * l_out_total_value_coins_str, *l_out_total_value_str = dap_uint256_to_char(l_value_sum, &l_out_total_value_coins_str); + json_object_object_add(json_obj_wall, "total_value_coins", json_object_new_string(l_out_total_value_coins_str)); + json_object_object_add(json_obj_wall, "total_value_datoshi", json_object_new_string(l_out_total_value_str)); json_object_object_add(json_obj_wall, "outs", l_json_outs_arr); json_object_array_add(json_arr_out, json_obj_wall); } break; diff --git a/modules/net/dap_chain_node_cli_cmd_tx.c b/modules/net/dap_chain_node_cli_cmd_tx.c index 0748ba83ce64ac7bdffe97f91b6f8549df941509..edf553a80b41b88f3d0a1cd105181f9ace48045e 100644 --- a/modules/net/dap_chain_node_cli_cmd_tx.c +++ b/modules/net/dap_chain_node_cli_cmd_tx.c @@ -429,7 +429,7 @@ json_object* dap_db_history_addr(json_object* a_json_arr_reply, dap_chain_addr_t int l_src_subtype = DAP_CHAIN_TX_OUT_COND_SUBTYPE_UNDEFINED; uint8_t *l_tx_item = NULL; size_t l_size; int i, q = 0; - // Проход по входам + // Inputs iteration TX_ITEM_ITER_TX_TYPE(l_tx_item, TX_ITEM_TYPE_IN_ALL, l_size, i, l_tx) { dap_chain_hash_fast_t *l_tx_prev_hash = NULL; int l_tx_prev_out_idx; @@ -562,7 +562,7 @@ json_object* dap_db_history_addr(json_object* a_json_arr_reply, dap_chain_addr_t if (l_src_addr && l_dst_addr && dap_chain_addr_compare(l_dst_addr, l_src_addr) && - dap_strcmp(l_noaddr_token, l_dst_token)) + l_noaddr_token && dap_strcmp(l_noaddr_token, l_dst_token)) continue; // sent to self (coinback) if (l_dst_addr && l_net_fee_used && dap_chain_addr_compare(&l_net_fee_addr, l_dst_addr)) @@ -698,9 +698,7 @@ json_object* dap_db_history_addr(json_object* a_json_arr_reply, dap_chain_addr_t json_object_object_add(l_corr_object, "recv_datoshi", json_object_new_string(l_value_str)); } if (l_send_to_same_cond) { - json_object *l_cond_recv_value_obj = json_object_object_get(l_cond_recv_object, "recv_datoshi"); - const char *l_cond_recv_value_str = json_object_get_string(l_cond_recv_value_obj); - uint256_t l_cond_recv_value = dap_uint256_scan_uninteger(l_cond_recv_value_str); + uint256_t l_cond_recv_value = l_cond_value; json_object *l_cond_send_value_obj = json_object_object_get(l_cond_send_object, "send_datoshi"); const char *l_cond_send_value_str = json_object_get_string(l_cond_send_value_obj); uint256_t l_cond_send_value = dap_uint256_scan_uninteger(l_cond_send_value_str); diff --git a/modules/net/include/dap_chain_ledger.h b/modules/net/include/dap_chain_ledger.h index f2e43d962d7f7617a4a8457eada2479fa56218a2..059b3142eefc41c190b515702cda3aa0e7f73538 100644 --- a/modules/net/include/dap_chain_ledger.h +++ b/modules/net/include/dap_chain_ledger.h @@ -418,21 +418,11 @@ 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_net_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); +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); // Get the list of 'out' items from previous transactions with summary value >= than a_value_need // Put this summary value to a_value_transfer @@ -440,12 +430,8 @@ dap_list_t *dap_ledger_get_list_tx_outs_with_val(dap_ledger_t *a_ledger, const c uint256_t a_value_need, uint256_t *a_value_transfer); 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 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_transfer); +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_verificator_callback_t a_callback, dap_ledger_updater_callback_t a_callback_added, dap_ledger_delete_callback_t a_callback_deleted); 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 59320cec6de15245daecfc9701db163d7dd24cf3..6339b4093580089da11d3ecb2794c8f74021e7d2 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 @@ -3523,6 +3523,13 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, void **a_str_reply) 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)); + dap_hash_fast_t l_owner_hash = dap_ledger_get_first_chain_tx_hash(l_net->pub.ledger, l_datum_tx, DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE); + dap_chain_datum_tx_t *l_owner_tx = dap_hash_fast_is_blank(&l_owner_hash) ? l_datum_tx + : dap_ledger_tx_find_datum_by_hash(l_net->pub.ledger, &l_owner_hash, NULL, false); + 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 c74cf6fd33c9b20d697e37c1b38737aa0dad1006..f01d67fb0a40dad0085dc271800f179f976ef38d 100644 --- a/modules/service/voting/dap_chain_net_srv_voting.c +++ b/modules/service/voting/dap_chain_net_srv_voting.c @@ -50,6 +50,7 @@ typedef struct dap_chain_net_voting_params_offsets{ uint64_t votes_max_count; bool delegate_key_required; bool vote_changing_allowed; + char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX]; } dap_chain_net_voting_params_offsets_t; typedef struct dap_chain_net_vote_option { @@ -60,7 +61,7 @@ typedef struct dap_chain_net_vote_option { typedef struct dap_chain_net_voting_cond_outs { dap_chain_hash_fast_t tx_hash; int out_idx; - + dap_hash_fast_t pkey_hash; UT_hash_handle hh; } dap_chain_net_voting_cond_outs_t; @@ -86,9 +87,10 @@ typedef struct dap_chain_net_votings { static dap_chain_net_votings_t *s_votings; static pthread_rwlock_t s_votings_rwlock; -static int s_datum_tx_voting_coin_check_cond_out(dap_chain_net_t *a_net, dap_hash_fast_t a_voting_hash, dap_hash_fast_t a_tx_cond_hash, int a_cond_out_idx); +static int s_datum_tx_voting_coin_check_cond_out(dap_chain_net_t *a_net, dap_hash_fast_t a_voting_hash, dap_hash_fast_t a_tx_cond_hash, int a_cond_out_idx, dap_hash_fast_t *a_vote_hash); /// -1 error, 0 - unspent, 1 - spent -static int s_datum_tx_voting_coin_check_spent(dap_chain_net_t *a_net, dap_hash_fast_t a_voting_hash, dap_hash_fast_t a_tx_prev_hash, int a_out_idx, dap_hash_fast_t *a_pkey_hash); +static int s_datum_tx_voting_coin_check_spent(dap_chain_net_t *a_net, dap_hash_fast_t a_voting_hash, + dap_hash_fast_t a_tx_prev_hash, int a_out_idx, dap_hash_fast_t *a_pkey_hash); static int s_datum_tx_voting_verification_callback(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, bool a_apply); static bool s_datum_tx_voting_verification_delete_callback(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in); static int s_cli_voting(int argc, char **argv, void **a_str_reply); @@ -114,15 +116,18 @@ int dap_chain_net_srv_voting_init() { pthread_rwlock_init(&s_votings_rwlock, NULL); dap_chain_ledger_voting_verificator_add(s_datum_tx_voting_verification_callback, s_datum_tx_voting_verification_delete_callback); - 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_datoshi> -w <fee_wallet_name>\n" - "voting vote -net <net_name> -hash <voting_hash> -option_idx <option_index> [-cert <delegate_cert_name>] -fee <value_datoshi> -w <fee_wallet_name>\n" - "voting list -net <net_name>\n" - "voting dump -net <net_name> -hash <voting_hash>\n"); - + dap_cli_server_cmd_add("poll", s_cli_voting, "Voting/poll commands", + "poll create -net <net_name> -question <\"Question_string\"> -options <\"Option0\", \"Option1\" ... \"OptionN\"> [-expire <poll_expire_time_in_RCF822>]" + " [-max_votes_count <Votes_count>] [-delegated_key_required] [-vote_changing_allowed] -fee <value_datoshi> -w <fee_wallet_name> [-token <ticker>]\n" + "poll vote -net <net_name> -hash <poll_hash> -option_idx <option_index> [-cert <delegate_cert_name>] -fee <value_datoshi> -w <fee_wallet_name>\n" + "poll list -net <net_name>\n" + "poll dump -net <net_name> -hash <poll_hash>\n" + "Hint:\n" + "\texample value_coins (only natural) 1.0 123.4567\n" + "\texample value_datoshi (only integer) 1 20 0.4321e+4\n"); dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_VOTING_ID }; - dap_ledger_service_add(l_uid, "voting", s_tag_check_voting); + dap_ledger_service_add(l_uid, "poll", s_tag_check_voting); return 0; } @@ -146,7 +151,7 @@ uint64_t* dap_chain_net_voting_get_result(dap_ledger_t* a_ledger, dap_chain_hash pthread_rwlock_unlock(&s_votings_rwlock); if(!l_voting || l_voting->net_id.uint64 != a_ledger->net->pub.id.uint64){ char* l_hash_str = dap_hash_fast_to_str_new(a_voting_hash); - log_it(L_ERROR, "Can't find voting with hash %s in net %s", l_hash_str, a_ledger->net->pub.name); + log_it(L_ERROR, "Can't find poll with hash %s in net %s", l_hash_str, a_ledger->net->pub.name); DAP_DEL_Z(l_hash_str); return NULL; } @@ -172,7 +177,6 @@ uint64_t* dap_chain_net_voting_get_result(dap_ledger_t* a_ledger, dap_chain_hash return l_voting_results; } - static int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, bool a_apply) { if (!a_apply) { @@ -181,7 +185,7 @@ static int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t HASH_FIND(hh, s_votings, a_tx_hash, sizeof(dap_hash_fast_t), l_voting); pthread_rwlock_unlock(&s_votings_rwlock); if (l_voting && l_voting->net_id.uint64 == a_ledger->net->pub.id.uint64) { - log_it(L_DEBUG, "Voting with hash %s is already presents in net %s", dap_hash_fast_to_str_static(a_tx_hash), a_ledger->net->pub.name); + log_it(L_DEBUG, "Poll with hash %s is already presents in net %s", dap_hash_fast_to_str_static(a_tx_hash), a_ledger->net->pub.name); return -1; } @@ -203,7 +207,7 @@ static int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t dap_list_free(l_tsd_list); if (!l_question_len || !l_options_count) { - log_it(L_WARNING, "Voting with hash %s contain no question or answer options", dap_hash_fast_to_str_static(a_tx_hash)); + log_it(L_WARNING, "Poll with hash %s contain no question or answer options", dap_hash_fast_to_str_static(a_tx_hash)); return -2; } @@ -218,14 +222,29 @@ static int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t dap_list_t* l_tsd_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_TSD, NULL); for (dap_list_t *it = l_tsd_list; it; it = it->next) { - dap_tsd_t* l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t*)it->data)->tsd; + dap_chain_tx_tsd_t *l_tx_tsd = it->data; + 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 poll %s", + l_tx_tsd->header.size, dap_hash_fast_to_str_static(a_tx_hash)); + return -DAP_LEDGER_CHECK_INVALID_SIZE; + } dap_chain_net_vote_option_t *l_vote_option = NULL; switch(l_tsd->type){ case VOTING_TSD_TYPE_QUESTION: + if (!l_tsd->size) { + log_it(L_WARNING, "Incorrect size %u of TSD section QUESTION for poll %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); + return -DAP_LEDGER_CHECK_INVALID_SIZE; + } l_item->voting_params.voting_question_offset = (size_t)(l_tsd->data - (byte_t*)l_item->voting_params.voting_tx); l_item->voting_params.voting_question_length = l_tsd->size; break; case VOTING_TSD_TYPE_ANSWER: + if (!l_tsd->size) { + log_it(L_WARNING, "Incorrect size %u of TSD section ANSWER for poll %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); + return -DAP_LEDGER_CHECK_INVALID_SIZE; + } l_vote_option = DAP_NEW_Z(dap_chain_net_vote_option_t); l_vote_option->vote_option_offset = (size_t)(l_tsd->data - (byte_t*)l_item->voting_params.voting_tx); l_vote_option->vote_option_length = l_tsd->size; @@ -233,41 +252,50 @@ static int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t break; case VOTING_TSD_TYPE_EXPIRE: if (l_tsd->size != sizeof(dap_time_t)) { - log_it(L_WARNING, "Incorrect size %u of TSD section EXPIRE vot voting %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); + log_it(L_WARNING, "Incorrect size %u of TSD section EXPIRE for poll %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); return -DAP_LEDGER_CHECK_INVALID_SIZE; } l_item->voting_params.voting_expire = *(dap_time_t *)l_tsd->data; break; case VOTING_TSD_TYPE_MAX_VOTES_COUNT: if (l_tsd->size != sizeof(uint64_t)) { - log_it(L_WARNING, "Incorrect size %u of TSD section MAX_VOTES_COUNT vot voting %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); + log_it(L_WARNING, "Incorrect size %u of TSD section MAX_VOTES_COUNT for poll %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); return -DAP_LEDGER_CHECK_INVALID_SIZE; } l_item->voting_params.votes_max_count = *(uint64_t *)l_tsd->data; break; case VOTING_TSD_TYPE_DELEGATED_KEY_REQUIRED: if (l_tsd->size != sizeof(byte_t)) { - log_it(L_WARNING, "Incorrect size %u of TSD section DELEGATED_KEY_REQUIRED vot voting %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); + log_it(L_WARNING, "Incorrect size %u of TSD section DELEGATED_KEY_REQUIRED for poll %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); return -DAP_LEDGER_CHECK_INVALID_SIZE; } l_item->voting_params.delegate_key_required = *(byte_t *)l_tsd->data; break; case VOTING_TSD_TYPE_VOTE_CHANGING_ALLOWED: if (l_tsd->size != sizeof(byte_t)) { - log_it(L_WARNING, "Incorrect size %u of TSD section VOTE_CHANGING_ALLOWED vot voting %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); + log_it(L_WARNING, "Incorrect size %u of TSD section VOTE_CHANGING_ALLOWED for poll %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); return -DAP_LEDGER_CHECK_INVALID_SIZE; } l_item->voting_params.vote_changing_allowed = *(byte_t *)l_tsd->data; 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 poll %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); + return -DAP_LEDGER_CHECK_INVALID_SIZE; + } + strcpy(l_item->voting_params.token_ticker, (char *)l_tsd->data); default: break; } } dap_list_free(l_tsd_list); + if (!*l_item->voting_params.token_ticker) + strcpy(l_item->voting_params.token_ticker, a_ledger->net->pub.native_ticker); pthread_rwlock_wrlock(&s_votings_rwlock); HASH_ADD(hh, s_votings, voting_hash, sizeof(dap_hash_fast_t), l_item); pthread_rwlock_unlock(&s_votings_rwlock); + log_it(L_NOTICE, "Poll with hash %s succefully added to ledger", dap_hash_fast_to_str_static(a_tx_hash)); return DAP_LEDGER_CHECK_OK; } @@ -284,88 +312,62 @@ static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a HASH_FIND(hh, s_votings, &l_vote_tx_item->voting_hash, sizeof(dap_hash_fast_t), l_voting); pthread_rwlock_unlock(&s_votings_rwlock); if (!l_voting || l_voting->net_id.uint64 != a_ledger->net->pub.id.uint64) { - log_it(L_ERROR, "Can't find voting with hash %s in net %s", + log_it(L_ERROR, "Can't find poll with hash %s in net %s", dap_chain_hash_fast_to_str_static(&l_vote_tx_item->voting_hash), a_ledger->net->pub.name); return -5; } - 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_sign_get_sig((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->voting_params.option_offsets_list)) { - 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->voting_params.votes_max_count && dap_list_length(l_voting->votes) >= l_voting->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->voting_hash)); - return -7; - } - if (l_voting->voting_params.voting_expire && l_voting->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->voting_hash)); - return -8; - } - - if (l_voting->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->voting_hash)); - return -10; - } - - for (dap_list_t *it = l_voting->votes; it; it = it->next) { - if (dap_hash_fast_compare(&((dap_chain_net_vote_t *)it->data)->pkey_hash, &pkey_hash)) { - dap_hash_fast_t *l_vote_hash = &((dap_chain_net_vote_t *)it->data)->vote_hash; - if (!l_voting->voting_params.vote_changing_allowed) { - char l_vote_hash_str[DAP_HASH_FAST_STR_SIZE]; - dap_hash_fast_to_str(l_vote_hash, l_vote_hash_str, DAP_HASH_FAST_STR_SIZE); - log_it(L_WARNING, "The voting %s don't allow change your vote %s", - dap_hash_fast_to_str_static(&l_voting->voting_hash), l_vote_hash_str); - return -11; - } - break; - } - } + if (l_vote_tx_item->answer_idx > dap_list_length(l_voting->voting_params.option_offsets_list)) { + 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->voting_params.votes_max_count && dap_list_length(l_voting->votes) >= l_voting->voting_params.votes_max_count){ + log_it(L_WARNING, "The required number of votes has been collected for poll %s", dap_chain_hash_fast_to_str_static(&l_voting->voting_hash)); + return -7; + } + if (l_voting->voting_params.voting_expire && l_voting->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->voting_hash)); + return -8; } - uint256_t l_weight = {}; + if (l_voting->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, "Poll %s required a delegated key", dap_chain_hash_fast_to_str_static(&l_voting->voting_hash)); + return -10; + } - // check out conds - dap_list_t *l_tsd_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_TSD, NULL); - for (dap_list_t *it = l_tsd_list; it; it = it->next) { - dap_tsd_t *l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t*)it->data)->tsd; - dap_hash_fast_t l_hash = ((dap_chain_tx_voting_tx_cond_t*)l_tsd->data)->tx_hash; - int l_out_idx = ((dap_chain_tx_voting_tx_cond_t*)l_tsd->data)->out_idx; - if (l_tsd->type == VOTING_TSD_TYPE_VOTE_TX_COND) { - if (s_datum_tx_voting_coin_check_cond_out(a_ledger->net, l_vote_tx_item->voting_hash, l_hash, l_out_idx)) - continue; - dap_chain_datum_tx_t *l_tx_prev_temp = dap_ledger_tx_find_by_hash(a_ledger, &l_hash); - dap_chain_tx_out_cond_t *l_prev_out = (dap_chain_tx_out_cond_t*)dap_chain_datum_tx_item_get(l_tx_prev_temp, &l_out_idx, NULL, TX_ITEM_TYPE_OUT_COND, NULL); - if (!l_prev_out || l_prev_out->header.subtype != DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK) - continue; - if (SUM_256_256(l_weight, l_prev_out->header.value, &l_weight)) { - log_it(L_WARNING, "Integer overflow while parsing vote tx %s", dap_chain_hash_fast_to_str_static(a_tx_hash)); - return -DAP_LEDGER_CHECK_INTEGER_OVERFLOW; + dap_list_t *l_old_vote = NULL; + for (dap_list_t *it = l_voting->votes; it; it = it->next) { + if (dap_hash_fast_compare(&((dap_chain_net_vote_t *)it->data)->pkey_hash, &l_pkey_hash)) { + dap_hash_fast_t *l_vote_hash = &((dap_chain_net_vote_t *)it->data)->vote_hash; + if (!l_voting->voting_params.vote_changing_allowed) { + char l_vote_hash_str[DAP_HASH_FAST_STR_SIZE]; + dap_hash_fast_to_str(l_vote_hash, l_vote_hash_str, DAP_HASH_FAST_STR_SIZE); + log_it(L_WARNING, "The poll %s don't allow change your vote %s", + dap_hash_fast_to_str_static(&l_voting->voting_hash), l_vote_hash_str); + return -11; } - - dap_chain_net_voting_cond_outs_t *l_item = DAP_NEW_Z_RET_VAL_IF_FAIL(dap_chain_net_voting_cond_outs_t, -DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - l_item->tx_hash = l_hash; - l_item->out_idx = l_out_idx; - pthread_rwlock_wrlock(&l_voting->s_tx_outs_rwlock); - HASH_ADD(hh, l_voting->voting_spent_cond_outs, tx_hash, sizeof(dap_hash_fast_t), l_item); - pthread_rwlock_unlock(&l_voting->s_tx_outs_rwlock); + l_old_vote = it; + break; } } - dap_list_free(l_tsd_list); + + uint256_t l_weight = {}; // check inputs dap_list_t *l_ins_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_IN, NULL); if (!l_ins_list) { @@ -374,17 +376,35 @@ static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a } for (dap_list_t *it = l_ins_list; it; it = it->next) { dap_chain_tx_in_t *l_tx_in = (dap_chain_tx_in_t *)it->data; - if (!s_datum_tx_voting_coin_check_spent(a_ledger->net, l_vote_tx_item->voting_hash, - l_tx_in->header.tx_prev_hash, l_tx_in->header.tx_out_prev_idx, &pkey_hash)) { - dap_chain_datum_tx_t *l_tx_prev_temp = dap_ledger_tx_find_by_hash(a_ledger, &l_tx_in->header.tx_prev_hash); - dap_chain_tx_out_t *l_prev_out_union = (dap_chain_tx_out_t *)dap_chain_datum_tx_out_get_by_out_idx(l_tx_prev_temp, l_tx_in->header.tx_out_prev_idx); - if (!l_prev_out_union) - continue; - if ((l_prev_out_union->header.type == TX_ITEM_TYPE_OUT || l_prev_out_union->header.type == TX_ITEM_TYPE_OUT_EXT) && - SUM_256_256(l_weight, l_prev_out_union->header.value, &l_weight)) { - log_it(L_WARNING, "Integer overflow while parsing vote tx %s", dap_chain_hash_fast_to_str_static(a_tx_hash)); - return -DAP_LEDGER_CHECK_INTEGER_OVERFLOW; - } + dap_chain_datum_tx_t *l_tx_prev_temp = dap_ledger_tx_find_by_hash(a_ledger, &l_tx_in->header.tx_prev_hash); + dap_chain_tx_out_ext_t *l_prev_out_union = (dap_chain_tx_out_ext_t *)dap_chain_datum_tx_out_get_by_out_idx( + l_tx_prev_temp, l_tx_in->header.tx_out_prev_idx); + if (!l_prev_out_union) + return -18; + const char *l_ticker_in = NULL; + switch (l_prev_out_union->header.type) { + case TX_ITEM_TYPE_OUT: + l_ticker_in = dap_ledger_tx_get_token_ticker_by_hash(a_ledger, &l_tx_in->header.tx_prev_hash); + break; + case TX_ITEM_TYPE_OUT_EXT: + l_ticker_in = l_prev_out_union->token; + break; + default: + log_it(L_WARNING, "Unexpected tx item %d in vote tx %s", l_prev_out_union->header.type, dap_hash_fast_to_str_static(a_tx_hash)); + return -19; + } + if (dap_strcmp(l_ticker_in, l_voting->voting_params.token_ticker)) + continue; + if (s_datum_tx_voting_coin_check_spent(a_ledger->net, l_vote_tx_item->voting_hash, + l_tx_in->header.tx_prev_hash, l_tx_in->header.tx_out_prev_idx, + l_old_vote ? &l_pkey_hash : NULL)) { + log_it(L_WARNING, "Coin with out number %u for tx %s is spent for poll %s", l_tx_in->header.tx_out_prev_idx, + dap_hash_fast_to_str_static(a_tx_hash), dap_hash_fast_to_str_static(&l_vote_tx_item->voting_hash)); + return -20; + } + if (SUM_256_256(l_weight, l_prev_out_union->header.value, &l_weight)) { + log_it(L_WARNING, "Integer overflow while parsing vote tx %s", dap_chain_hash_fast_to_str_static(a_tx_hash)); + return -DAP_LEDGER_CHECK_INTEGER_OVERFLOW; } } dap_list_free(l_ins_list); @@ -394,62 +414,76 @@ static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a return -13; } + // check out conds + dap_list_t *l_tsd_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_TSD, NULL); + for (dap_list_t *it = l_tsd_list; it; it = it->next) { + dap_tsd_t *l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t*)it->data)->tsd; + dap_hash_fast_t l_hash = ((dap_chain_tx_voting_tx_cond_t*)l_tsd->data)->tx_hash; + int l_out_idx = ((dap_chain_tx_voting_tx_cond_t*)l_tsd->data)->out_idx; + if (l_tsd->type != VOTING_TSD_TYPE_VOTE_TX_COND) + return -14; + dap_chain_datum_tx_t *l_tx_prev_temp = dap_ledger_tx_find_by_hash(a_ledger, &l_hash); + dap_chain_tx_out_cond_t *l_prev_out = (dap_chain_tx_out_cond_t *)dap_chain_datum_tx_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_hash, l_prev_out->header.subtype, l_out_idx, l_pkey_sign)) + return -17; + if (s_datum_tx_voting_coin_check_cond_out(a_ledger->net, l_vote_tx_item->voting_hash, l_hash, l_out_idx, + l_old_vote ? &l_pkey_hash : NULL)) + return -15; + if (SUM_256_256(l_weight, l_prev_out->header.value, &l_weight)) { + log_it(L_WARNING, "Integer overflow while parsing vote tx %s", dap_chain_hash_fast_to_str_static(a_tx_hash)); + return -DAP_LEDGER_CHECK_INTEGER_OVERFLOW; + } + } + if (a_apply) { + // Mark conditional outs + pthread_rwlock_wrlock(&l_voting->s_tx_outs_rwlock); + if (l_old_vote) { + dap_hash_fast_t *l_vote_hash = &((dap_chain_net_vote_t *)l_old_vote->data)->vote_hash; + dap_chain_net_voting_cond_outs_t *it = NULL, *tmp; + HASH_ITER(hh, l_voting->voting_spent_cond_outs, it, tmp) { + if (!dap_hash_fast_compare(l_vote_hash, &it->pkey_hash)) + continue; + HASH_DEL(l_voting->voting_spent_cond_outs, it); + DAP_DELETE(it); + } + } + for (dap_list_t *it = l_tsd_list; it; it = it->next) { + dap_tsd_t *l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t *)it->data)->tsd; + if (l_tsd->type != VOTING_TSD_TYPE_VOTE_TX_COND) + continue; + dap_chain_net_voting_cond_outs_t *l_tx_out = DAP_NEW_Z_RET_VAL_IF_FAIL(dap_chain_net_voting_cond_outs_t, -DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + l_tx_out->tx_hash = ((dap_chain_tx_voting_tx_cond_t *)l_tsd->data)->tx_hash; + l_tx_out->out_idx = ((dap_chain_tx_voting_tx_cond_t *)l_tsd->data)->out_idx; + l_tx_out->pkey_hash = l_pkey_hash; + HASH_ADD(hh, l_voting->voting_spent_cond_outs, tx_hash, sizeof(dap_hash_fast_t), l_tx_out); + } + pthread_rwlock_unlock(&l_voting->s_tx_outs_rwlock); + dap_chain_net_vote_t *l_vote_item = DAP_NEW_Z_RET_VAL_IF_FAIL(dap_chain_net_vote_t, -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; - // cycle is safe cause return after link deletion - for (dap_list_t *it = l_voting->votes; it; it = it->next) { - if (dap_hash_fast_compare(&((dap_chain_net_vote_t *)it->data)->pkey_hash, &pkey_hash)){ - if (!l_voting->voting_params.vote_changing_allowed) { - 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_WARNING, "The voting %s don't allow change your vote %s", - dap_hash_fast_to_str_static(&l_voting->voting_hash), l_vote_hash_str); - DAP_DELETE(l_vote_item); - return -11; - } - dap_hash_fast_t *l_vote_hash = &((dap_chain_net_vote_t *)it->data)->vote_hash; - //delete conditional outputs - dap_chain_datum_tx_t *l_old_tx = dap_ledger_tx_find_by_hash(a_ledger, l_vote_hash); - if (!l_old_tx) { - char l_vote_hash_str[DAP_HASH_FAST_STR_SIZE]; - dap_hash_fast_to_str(l_vote_hash, l_vote_hash_str, DAP_HASH_FAST_STR_SIZE); - log_it(L_ERROR, "Can't find old vote %s of voting %s in ledger", - l_vote_hash_str, dap_hash_fast_to_str_static(&l_voting->voting_hash)); - } - dap_list_t* l_tsd_list = dap_chain_datum_tx_items_get(l_old_tx, TX_ITEM_TYPE_TSD, NULL); - for (dap_list_t *it_tsd = l_tsd_list; it_tsd; it_tsd = it_tsd->next) { - dap_tsd_t* l_tsd = (dap_tsd_t*)((dap_chain_tx_tsd_t*)it_tsd->data)->tsd; - dap_hash_fast_t *l_hash = &((dap_chain_tx_voting_tx_cond_t*)l_tsd->data)->tx_hash; - if (l_tsd->type == VOTING_TSD_TYPE_VOTE_TX_COND) { - dap_chain_net_voting_cond_outs_t *l_tx_outs = NULL; - pthread_rwlock_wrlock(&l_voting->s_tx_outs_rwlock); - HASH_FIND(hh, l_voting->voting_spent_cond_outs, l_hash, sizeof(dap_hash_fast_t), l_tx_outs); - if(l_tx_outs) - HASH_DELETE(hh, l_voting->voting_spent_cond_outs, l_tx_outs); - pthread_rwlock_unlock(&l_voting->s_tx_outs_rwlock); - } - } - dap_list_free(l_tsd_list); - // change vote & move it to the end of list - l_voting->votes = dap_list_remove_link(l_voting->votes, it); - 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(&((dap_chain_net_vote_t *)it->data)->vote_hash, l_vote_hash_str, DAP_HASH_FAST_STR_SIZE); - DAP_DELETE(it->data); - log_it(L_INFO, "Vote %s of voting %s has been changed", l_vote_hash_str, dap_hash_fast_to_str_static(&l_voting->voting_hash)); - return DAP_LEDGER_CHECK_OK; - } + if (l_old_vote) { + // change vote & move it to the end of list + const char *l_vote_hash_str = dap_hash_fast_to_str_static(&((dap_chain_net_vote_t *)l_old_vote->data)->vote_hash); + DAP_DELETE(l_old_vote->data); + l_voting->votes = dap_list_delete_link(l_voting->votes, l_old_vote); + log_it(L_NOTICE, "Vote %s of poll %s has been changed", l_vote_hash_str, dap_hash_fast_to_str_static(&l_voting->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 poll %s has been accepted", l_vote_hash_str, dap_hash_fast_to_str_static(&l_voting->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 accepted", l_vote_hash_str, dap_hash_fast_to_str_static(&l_voting->voting_hash)); } + dap_list_free(l_tsd_list); + return DAP_LEDGER_CHECK_OK; } @@ -459,7 +493,7 @@ int s_datum_tx_voting_verification_callback(dap_ledger_t *a_ledger, dap_chain_tx return s_voting_verificator(a_ledger, a_type, a_tx_in, a_tx_hash, a_apply); if (a_type == TX_ITEM_TYPE_VOTE) return s_vote_verificator(a_ledger, a_type, a_tx_in, a_tx_hash, a_apply); - log_it(L_ERROR, "Item %d is not supported in votings", a_type); + log_it(L_ERROR, "Item %d is not supported in polls", a_type); return -3; } @@ -474,7 +508,7 @@ static bool s_datum_tx_voting_verification_delete_callback(dap_ledger_t *a_ledge HASH_FIND(hh, s_votings, &l_hash, sizeof(dap_hash_fast_t), l_voting); if(!l_voting){ char* l_hash_str = dap_hash_fast_to_str_new(&l_hash); - log_it(L_ERROR, "Can't find voting with hash %s in net %s", l_hash_str, a_ledger->net->pub.name); + log_it(L_ERROR, "Can't find poll with hash %s in net %s", l_hash_str, a_ledger->net->pub.name); DAP_DEL_Z(l_hash_str); pthread_rwlock_unlock(&s_votings_rwlock); return false; @@ -508,13 +542,13 @@ static bool s_datum_tx_voting_verification_delete_callback(dap_ledger_t *a_ledge return false; } - dap_chain_net_votings_t * l_voting = NULL; + dap_chain_net_votings_t *l_voting = NULL; pthread_rwlock_wrlock(&s_votings_rwlock); HASH_FIND(hh, s_votings, &l_vote_tx_item->voting_hash, sizeof(dap_hash_fast_t), l_voting); pthread_rwlock_unlock(&s_votings_rwlock); if(!l_voting || l_voting->net_id.uint64 != a_ledger->net->pub.id.uint64) { char *l_hash_str = dap_chain_hash_fast_to_str_new(&l_hash); - log_it(L_ERROR, "Can't find voting with hash %s in net %s", l_hash_str, a_ledger->net->pub.name); + log_it(L_ERROR, "Can't find poll with hash %s in net %s", l_hash_str, a_ledger->net->pub.name); DAP_DELETE(l_hash_str); return false; } @@ -620,10 +654,11 @@ 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){ - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_QUESTION_PARAM_MISSING, "Voting requires a question parameter to be valid."); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_QUESTION_PARAM_MISSING, "Poll requires a question parameter to be valid."); return -DAP_CHAIN_NET_VOTE_CREATE_QUESTION_PARAM_MISSING; } @@ -636,7 +671,7 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) dap_list_t *l_options_list = NULL; dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-options", &l_options_list_str); if (!l_options_list_str){ - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_OPTION_PARAM_MISSING, "Voting requires a question parameter to be valid."); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_OPTION_PARAM_MISSING, "Poll requires a question parameter to be valid."); return -DAP_CHAIN_NET_VOTE_CREATE_OPTION_PARAM_MISSING; } // Parse options list @@ -652,21 +687,20 @@ 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."); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_FEE_PARAM_NOT_VALID, "Poll requires parameter -fee to be valid."); return -DAP_CHAIN_NET_VOTE_CREATE_FEE_PARAM_NOT_VALID; } uint256_t l_value_fee = dap_chain_balance_scan(l_fee_str); dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-w", &l_wallet_str); if (!l_wallet_str){ - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_WALLET_PARAM_NOT_VALID, "Voting requires parameter -w to be valid."); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_WALLET_PARAM_NOT_VALID, "Poll requires parameter -w to be valid."); 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); @@ -675,6 +709,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); @@ -688,8 +724,26 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) return -DAP_CHAIN_NET_VOTE_CREATE_WALLET_DOES_NOT_EXIST; } + + dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-token", &l_token_str); + if (l_token_str && !dap_ledger_token_ticker_check(l_net->pub.ledger, l_token_str)) { + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_WRONG_TOKEN, "Token %s does not exist", l_token_str); + return -DAP_CHAIN_NET_VOTE_CREATE_WRONG_TOKEN; + } + char *l_hash_ret = NULL; - int res = dap_chain_net_vote_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_vote_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); @@ -707,7 +761,7 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) return DAP_CHAIN_NET_VOTE_CREATE_LENGTH_QUESTION_OVERSIZE_MAX; } break; case DAP_CHAIN_NET_VOTE_CREATE_COUNT_OPTION_OVERSIZE_MAX: { - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_COUNT_OPTION_OVERSIZE_MAX, "The voting can contain no more than %d options", + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_COUNT_OPTION_OVERSIZE_MAX, "The poll can contain no more than %d options", DAP_CHAIN_DATUM_TX_VOTING_OPTION_MAX_COUNT); return DAP_CHAIN_NET_VOTE_CREATE_COUNT_OPTION_OVERSIZE_MAX; } break; @@ -716,7 +770,7 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) return DAP_CHAIN_NET_VOTE_CREATE_FEE_IS_ZERO; } break; case DAP_CHAIN_NET_VOTE_CREATE_SOURCE_ADDRESS_IS_INVALID: { - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_SOURCE_ADDRESS_IS_INVALID, "source address is invalid"); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_SOURCE_ADDRESS_IS_INVALID, "Source address is invalid"); return DAP_CHAIN_NET_VOTE_CREATE_SOURCE_ADDRESS_IS_INVALID; } break; case DAP_CHAIN_NET_VOTE_CREATE_NOT_ENOUGH_FUNDS_TO_TRANSFER: { @@ -729,11 +783,11 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) return DAP_CHAIN_NET_VOTE_CREATE_MAX_COUNT_OPTION_EXCEEDED; } break; case DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_OPTION_TSD_ITEM: { - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_OPTION_TSD_ITEM, "Can't create voting with expired time"); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_OPTION_TSD_ITEM, "Can't create poll with expired time"); return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_OPTION_TSD_ITEM; } break; case DAP_CHAIN_NET_VOTE_CREATE_INPUT_TIME_MORE_CURRENT_TIME: { - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_INPUT_TIME_MORE_CURRENT_TIME, "Can't create voting with expired time"); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_INPUT_TIME_MORE_CURRENT_TIME, "Can't create poll with expired time"); return DAP_CHAIN_NET_VOTE_CREATE_INPUT_TIME_MORE_CURRENT_TIME; } break; case DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_CREATE_TSD_EXPIRE_TIME: { @@ -780,10 +834,11 @@ 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); dap_cert_t * l_cert = dap_cert_find_by_name(l_cert_name); if (l_cert_name){ @@ -817,8 +872,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; } @@ -827,9 +882,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_vote_voting(l_cert, l_value_fee, l_wallet_fee, l_voting_hash, l_option_idx_count, + int res = dap_chain_net_vote_voting(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: { @@ -840,14 +895,14 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) return DAP_CHAIN_NET_VOTE_CREATE_OK; } break; case DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_FIND_VOTE: { - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_FIND_VOTE, "Can't find voting with hash %s", l_hash_str); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_FIND_VOTE, "Can't find poll with hash %s", l_hash_str); } break; case DAP_CHAIN_NET_VOTE_VOTING_THIS_VOTING_HAVE_MAX_VALUE_VOTES: { dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_THIS_VOTING_HAVE_MAX_VALUE_VOTES, - "This voting already received the required number of votes."); + "This poll already received the required number of votes."); } break; 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."); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_ALREADY_EXPIRED, "This poll is 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, @@ -855,18 +910,13 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) } break; 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, - "Can't serialize public key of certificate \"%s\"", - l_cert_name); + "This poll required a delegated key. Parameter -cert must contain a valid certificate name"); } break; case DAP_CHAIN_NET_VOTE_VOTING_KEY_IS_NOT_DELEGATED: { dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_KEY_IS_NOT_DELEGATED, "Your key is not delegated."); } break; case DAP_CHAIN_NET_VOTE_VOTING_DOES_NOT_ALLOW_CHANGE_YOUR_VOTE: { - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_DOES_NOT_ALLOW_CHANGE_YOUR_VOTE, "The voting doesn't allow change your vote."); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_DOES_NOT_ALLOW_CHANGE_YOUR_VOTE, "The poll doesn't allow change your vote."); } break; case DAP_CHAIN_NET_VOTE_VOTING_SOURCE_ADDRESS_INVALID: { dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_SOURCE_ADDRESS_INVALID, "source address is invalid"); @@ -910,7 +960,7 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) }break; case CMD_LIST:{ json_object* json_vote_out = json_object_new_object(); - json_object_object_add(json_vote_out, "List of votings in net", json_object_new_string(l_net->pub.name)); + json_object_object_add(json_vote_out, "list_of_polls", json_object_new_string(l_net->pub.name)); json_object* json_arr_voting_out = json_object_new_array(); dap_chain_net_votings_t *l_voting = NULL, *l_tmp; pthread_rwlock_rdlock(&s_votings_rwlock); @@ -918,11 +968,12 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) if (l_voting->net_id.uint64 != l_net->pub.id.uint64) continue; json_object* json_obj_vote = json_object_new_object(); - json_object_object_add( json_obj_vote, "voting_tx", + json_object_object_add( json_obj_vote, "poll_tx", json_object_new_string(dap_chain_hash_fast_to_str_static(&l_voting->voting_hash))); char* l_voting_question = (char*)l_voting->voting_params.voting_tx + l_voting->voting_params.voting_question_offset; json_object_object_add( json_obj_vote, "question", json_object_new_string_len(l_voting_question, l_voting->voting_params.voting_question_length) ); + json_object_object_add(json_obj_vote, "token", json_object_new_string(l_voting->voting_params.token_ticker)); json_object_array_add(json_arr_voting_out, json_obj_vote); } pthread_rwlock_unlock(&s_votings_rwlock); @@ -937,13 +988,17 @@ 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_DUMP_HASH_PARAM_INVALID, + "Can't recognize hash string as a valid HEX or BASE58 format hash"); + return -DAP_CHAIN_NET_VOTE_DUMP_HASH_PARAM_INVALID; + } dap_chain_net_votings_t *l_voting = NULL; pthread_rwlock_rdlock(&s_votings_rwlock); HASH_FIND(hh, s_votings, &l_voting_hash, sizeof(l_voting_hash), l_voting); pthread_rwlock_unlock(&s_votings_rwlock); if (!l_voting) { - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_DUMP_CAN_NOT_FIND_VOTE, "Can't find voting with hash %s", l_hash_str); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_DUMP_CAN_NOT_FIND_VOTE, "Can't find poll with hash %s", l_hash_str); return -DAP_CHAIN_NET_VOTE_DUMP_CAN_NOT_FIND_VOTE; } @@ -969,10 +1024,11 @@ 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(l_hash_str)); + json_object_object_add(json_vote_out, "poll_tx", json_object_new_string(l_hash_str)); json_object_object_add(json_vote_out, "question", json_object_new_string_len((char*)l_voting->voting_params.voting_tx + l_voting->voting_params.voting_question_offset, l_voting->voting_params.voting_question_length)); + json_object_object_add(json_vote_out, "token", json_object_new_string(l_voting->voting_params.token_ticker)); if (l_voting->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->voting_params.voting_expire); @@ -998,21 +1054,20 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) json_object* json_vote_obj = json_object_new_object(); json_object_object_add(json_vote_obj, "option_id", json_object_new_int(i)); dap_chain_net_vote_option_t* l_vote_option = (dap_chain_net_vote_option_t*)l_option->data; - json_object_object_add( json_vote_obj, "vote_tx", + json_object_object_add( json_vote_obj, "option_text", json_object_new_string_len((char*)l_voting->voting_params.voting_tx + l_vote_option->vote_option_offset, l_vote_option->vote_option_length) ); - json_object_object_add(json_vote_obj, "voting_power", json_object_new_uint64( l_results[i].num_of_votes) ); - int l_percentage = l_votes_count ? (int)((float)l_results[i].num_of_votes/l_votes_count * 100 + 0.5) : 0; - json_object_object_add(json_vote_obj, "vote_share", json_object_new_int(l_percentage) ); + json_object_object_add(json_vote_obj, "votes_count", json_object_new_uint64( l_results[i].num_of_votes) ); + int l_percentage = l_votes_count ? ((double)(l_results[i].num_of_votes * 100))/l_votes_count + 0.5 : 0; + json_object_object_add(json_vote_obj, "votes_percent", json_object_new_int(l_percentage) ); uint256_t l_weight_percentage = { }; - DIV_256_COIN(l_results[i].weights, l_total_weight, &l_weight_percentage); MULT_256_COIN(l_weight_percentage, dap_chain_coins_to_balance("100.0"), &l_weight_percentage); const char *l_weight_percentage_str = dap_uint256_decimal_to_round_char(l_weight_percentage, 2, true), *l_w_coins, *l_w_datoshi = dap_uint256_to_char(l_results[i].weights, &l_w_coins); - json_object_object_add(json_vote_obj, "vote_sum", json_object_new_string(l_w_coins)); - json_object_object_add(json_vote_obj, "vote_sum_datoshi", json_object_new_string(l_w_datoshi)); - json_object_object_add(json_vote_obj, "vote_sum_weight", json_object_new_string(l_weight_percentage_str)); + json_object_object_add(json_vote_obj, "votes_sum", json_object_new_string(l_w_coins)); + json_object_object_add(json_vote_obj, "votes_sum_datoshi", json_object_new_string(l_w_datoshi)); + json_object_object_add(json_vote_obj, "votes_sum_weight", json_object_new_string(l_weight_percentage_str)); json_object_array_add(json_arr_vote_out, json_vote_obj); } json_object_object_add(json_vote_out, "results", json_arr_vote_out); @@ -1020,7 +1075,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: @@ -1029,164 +1083,116 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) return 0; } -static int s_datum_tx_voting_coin_check_spent(dap_chain_net_t *a_net, dap_hash_fast_t a_voting_hash, dap_hash_fast_t a_tx_prev_hash, int a_out_idx, dap_hash_fast_t *a_pkey_hash) +static int s_tx_is_spent(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_hash, dap_hash_fast_t *a_voting_hash, + dap_hash_fast_t *a_pkey_hash, dap_chain_net_votings_t *a_voting, dap_time_t a_voting_ts) { - int l_coin_is_spent = 0; - - - dap_ledger_t *l_ledger = a_net->pub.ledger; - if(!l_ledger){ - log_it(L_ERROR, "Can't find ledger"); - return -1; + dap_chain_datum_tx_t *l_tx = dap_ledger_tx_find_by_hash(a_ledger, a_tx_hash); + if (!l_tx) { + log_it(L_ERROR, "Can't find tx %s", dap_hash_fast_to_str_static(a_tx_hash)); + return -3; } - dap_chain_datum_tx_t *l_voting_tx = dap_ledger_tx_find_by_hash(l_ledger, &a_voting_hash); - const char *l_native_ticker = a_net->pub.native_ticker; - - dap_list_t *l_tx_list = NULL; // "stack" for saving txs on up level - dap_chain_datum_tx_t *l_tx = dap_ledger_tx_find_by_hash(l_ledger, &a_tx_prev_hash); - if (!l_tx){ - log_it(L_ERROR, "Can't find tx"); - return -1; - } - - if (l_tx->header.ts_created < l_voting_tx->header.ts_created){ + if (l_tx->header.ts_created < a_voting_ts) return 0; - } - - if (s_datum_tx_voting_coin_check_cond_out(a_net, a_voting_hash, a_tx_prev_hash, a_out_idx) != 0){ - return 1; - } dap_chain_tx_vote_t *l_vote = (dap_chain_tx_vote_t *)dap_chain_datum_tx_item_get(l_tx, NULL, NULL, TX_ITEM_TYPE_VOTE, NULL); - if (l_vote && dap_hash_fast_compare(&l_vote->voting_hash, &a_voting_hash)) { - dap_chain_net_votings_t *l_voting = NULL; - pthread_rwlock_wrlock(&s_votings_rwlock); - HASH_FIND(hh, s_votings, &a_voting_hash, sizeof(dap_hash_fast_t), l_voting); - pthread_rwlock_unlock(&s_votings_rwlock); - if (l_voting) { - for (dap_list_t *it = l_voting->votes; it; it = it->next) { - dap_chain_net_vote_t *l_vote = (dap_chain_net_vote_t *)it->data; - if (dap_hash_fast_compare(&l_vote->vote_hash, &a_tx_prev_hash)) { - if (l_voting->voting_params.vote_changing_allowed && - !dap_hash_fast_is_blank(a_pkey_hash) && - dap_hash_fast_compare(&l_vote->pkey_hash, a_pkey_hash)) - break; // it's vote changing, allow it - return 1; - } + if (l_vote && dap_hash_fast_compare(&l_vote->voting_hash, &a_voting->voting_hash)) { + for (dap_list_t *it = a_voting->votes; it; it = it->next) { + dap_chain_net_vote_t *l_vote = (dap_chain_net_vote_t *)it->data; + if (dap_hash_fast_compare(&l_vote->vote_hash, a_tx_hash)) { + if (a_voting->voting_params.vote_changing_allowed && + !dap_hash_fast_is_blank(a_pkey_hash) && + dap_hash_fast_compare(&l_vote->pkey_hash, a_pkey_hash)) + break; // it's vote changing, allow it + return 1; } } } + dap_list_t *l_ins_list = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN, NULL); + l_ins_list = dap_list_concat(l_ins_list, dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN_COND, NULL)); + if (!l_ins_list) // it's emisssion or reward TX, not marked yet + return 0; - dap_list_t *l_ins_list_temp = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN, NULL); - dap_list_t *l_cond_ins_list = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN_COND, NULL); - if (!l_ins_list_temp && !l_cond_ins_list){ - log_it(L_ERROR, "Can't get inputs from tx"); - return -1; - } - - dap_list_t *l_ins_list = NULL; - l_ins_list = dap_list_concat(l_ins_list, l_ins_list_temp); - l_ins_list = dap_list_concat(l_ins_list, l_cond_ins_list); - - l_tx_list = dap_list_append(l_tx_list, l_ins_list); - dap_list_t* l_tx_temp = dap_list_last(l_tx_list); - - while(l_tx_temp && !l_coin_is_spent){ - if (l_tx_temp->data == NULL){ - l_tx_list = dap_list_delete_link(l_tx_list, l_tx_temp); - l_tx_temp = l_tx_list ? dap_list_last(l_tx_list) : NULL; - continue; - } - dap_list_t *l_ins_list = (dap_list_t*)l_tx_temp->data; - dap_chain_tx_in_t* l_temp_in = (dap_chain_tx_in_t*)l_ins_list->data; - dap_chain_datum_tx_t *l_tx_prev_temp = dap_ledger_tx_find_by_hash(l_ledger, &l_temp_in->header.tx_prev_hash); + dap_hash_fast_t l_prev_hash = {}; + for (dap_list_t *it = l_ins_list; it; it = it->next) { + uint32_t l_prev_idx = -1; + if (*(byte_t *)it->data == TX_ITEM_TYPE_IN_COND) { + dap_chain_tx_in_cond_t *in = it->data; + l_prev_hash = in->header.tx_prev_hash; + l_prev_idx = in->header.tx_out_prev_idx; + } else { + dap_chain_tx_in_t *in = it->data; + l_prev_hash = in->header.tx_prev_hash; + l_prev_idx = in->header.tx_out_prev_idx; + } + dap_chain_datum_tx_t *l_tx_prev = dap_ledger_tx_find_by_hash(a_ledger, &l_prev_hash); const char* l_tx_token = NULL; - dap_chain_tx_out_t *l_prev_out_union = (dap_chain_tx_out_t*)dap_chain_datum_tx_out_get_by_out_idx(l_tx_prev_temp, l_temp_in->header.tx_out_prev_idx); - if (!l_prev_out_union){ - l_tx_temp->data = dap_list_remove(l_tx_temp->data, l_temp_in); - if (l_tx_temp->data == NULL){ - l_tx_list = dap_list_delete_link(l_tx_list, l_tx_temp); - l_tx_temp = l_tx_list ? dap_list_last(l_tx_list) : NULL; - } - continue; - } - - switch (l_prev_out_union->header.type) { + byte_t *l_prev_out_union = dap_chain_datum_tx_out_get_by_out_idx(l_tx_prev, l_prev_idx); + switch (*l_prev_out_union) { case TX_ITEM_TYPE_OUT:{ - l_tx_token = dap_ledger_tx_get_token_ticker_by_hash(l_ledger, &l_temp_in->header.tx_prev_hash); + dap_chain_tx_out_t *l_out = (dap_chain_tx_out_t *)l_prev_out_union; + l_tx_token = dap_ledger_tx_get_token_ticker_by_hash(a_ledger, &l_prev_hash); }break; case TX_ITEM_TYPE_OUT_EXT:{ - dap_chain_tx_out_ext_t *l_temp_out = (dap_chain_tx_out_ext_t *)l_prev_out_union; - l_tx_token = l_temp_out->token; + dap_chain_tx_out_ext_t *l_out = (dap_chain_tx_out_ext_t *)l_prev_out_union; + l_tx_token = l_out->token; }break; case TX_ITEM_TYPE_OUT_COND:{ - dap_chain_tx_out_cond_t *l_temp_out = (dap_chain_tx_out_cond_t*)l_prev_out_union; - if (l_temp_out->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK || - s_datum_tx_voting_coin_check_cond_out(a_net, a_voting_hash, l_temp_in->header.tx_prev_hash, l_temp_in->header.tx_out_prev_idx) == 0) + dap_chain_tx_out_cond_t *l_out = (dap_chain_tx_out_cond_t *)l_prev_out_union; + if (l_out->header.subtype != DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE) { + l_tx_token = a_ledger->net->pub.native_ticker; break; + } + if (s_datum_tx_voting_coin_check_cond_out(a_ledger->net, *a_voting_hash, l_prev_hash, l_prev_idx, a_pkey_hash) != 0) { + dap_list_free(l_ins_list); + return 1; + } + l_tx_token = dap_ledger_tx_get_token_ticker_by_hash(a_ledger, &l_prev_hash); } default: - l_tx_temp->data = dap_list_remove((dap_list_t*)l_tx_temp->data, l_temp_in); - if (l_tx_temp->data == NULL){ - l_tx_list = dap_list_delete_link(l_tx_list, l_tx_temp); - l_tx_temp = l_tx_list ? dap_list_last(l_tx_list) : NULL; - } - continue; + break; } - - if (l_tx_prev_temp->header.ts_created < l_voting_tx->header.ts_created || - dap_strcmp(l_tx_token, l_native_ticker)){ - l_tx_temp->data = dap_list_remove((dap_list_t*)l_tx_temp->data, l_temp_in); - if (l_tx_temp->data == NULL){ - l_tx_list = dap_list_delete_link(l_tx_list, l_tx_temp); - l_tx_temp = l_tx_list ? dap_list_last(l_tx_list) : NULL; - } + if (dap_strcmp(l_tx_token, a_voting->voting_params.token_ticker)) continue; - } - + } - dap_chain_tx_vote_t *l_vote =(dap_chain_tx_vote_t *) dap_chain_datum_tx_item_get(l_tx_prev_temp, NULL, NULL, TX_ITEM_TYPE_VOTE, NULL); - if(l_vote && dap_hash_fast_compare(&l_vote->voting_hash, &a_voting_hash)){ - dap_chain_net_votings_t *l_voting = NULL; - pthread_rwlock_wrlock(&s_votings_rwlock); - HASH_FIND(hh, s_votings, &a_voting_hash, sizeof(dap_hash_fast_t), l_voting); - pthread_rwlock_unlock(&s_votings_rwlock); - dap_list_t *l_temp = NULL; - while (l_temp){ - dap_chain_net_vote_t *l_vote = (dap_chain_net_vote_t *)l_temp->data; - if (dap_hash_fast_compare(&l_vote->vote_hash, &l_temp_in->header.tx_prev_hash)){ - l_coin_is_spent = 1; - break; - } - l_temp = l_temp->next; - } - } + return s_tx_is_spent(a_ledger, &l_prev_hash, a_voting_hash, a_pkey_hash, a_voting, a_voting_ts); +} - l_ins_list = dap_chain_datum_tx_items_get(l_tx_prev_temp, TX_ITEM_TYPE_IN, NULL); - l_tx_list = dap_list_append(l_tx_list, l_ins_list); - l_tx_temp->data = dap_list_remove((dap_list_t*)l_tx_temp->data, l_temp_in); - l_tx_temp = l_tx_list ? dap_list_last(l_tx_list) : NULL; +static int s_datum_tx_voting_coin_check_spent(dap_chain_net_t *a_net, dap_hash_fast_t a_voting_hash, + dap_hash_fast_t a_tx_prev_hash, int a_out_idx, dap_hash_fast_t *a_pkey_hash) +{ + int l_coin_is_spent = 0; + dap_chain_net_votings_t *l_voting = NULL; + pthread_rwlock_wrlock(&s_votings_rwlock); + HASH_FIND(hh, s_votings, &a_voting_hash, sizeof(dap_hash_fast_t), l_voting); + pthread_rwlock_unlock(&s_votings_rwlock); + if (!l_voting) { + log_it(L_ERROR, "Can't find poll %s", dap_hash_fast_to_str_static(&a_voting_hash)); + return -1; } - if(l_tx_list){ - l_tx_temp = l_tx_list; - while(l_tx_temp){ - if (l_tx_temp->data) - dap_list_free((dap_list_t*)l_tx_temp->data); - l_tx_list = dap_list_delete_link(l_tx_list, l_tx_temp); - l_tx_temp = dap_list_first(l_tx_list); - } + dap_ledger_t *l_ledger = a_net->pub.ledger; + dap_chain_datum_tx_t *l_voting_tx = dap_ledger_tx_find_by_hash(l_ledger, &a_voting_hash); + if (!l_voting_tx) { + log_it(L_ERROR, "Can't find poll tx %s", dap_hash_fast_to_str_static(&a_voting_hash)); + return -2; } - return l_coin_is_spent; + if (s_datum_tx_voting_coin_check_cond_out(a_net, a_voting_hash, a_tx_prev_hash, a_out_idx, a_pkey_hash) != 0) + return 1; + + return s_tx_is_spent(l_ledger, &a_tx_prev_hash, &a_voting_hash, a_pkey_hash, l_voting, l_voting_tx->header.ts_created); + } -static int s_datum_tx_voting_coin_check_cond_out(dap_chain_net_t *a_net, dap_hash_fast_t a_voting_hash, dap_hash_fast_t a_tx_cond_hash, int a_cond_out_idx) +static int s_datum_tx_voting_coin_check_cond_out(dap_chain_net_t *a_net, dap_hash_fast_t a_voting_hash, + dap_hash_fast_t a_tx_cond_hash, int a_cond_out_idx, + dap_hash_fast_t *a_pkey_hash) { dap_chain_net_votings_t * l_voting = NULL; @@ -1194,27 +1200,27 @@ static int s_datum_tx_voting_coin_check_cond_out(dap_chain_net_t *a_net, dap_has HASH_FIND(hh, s_votings, &a_voting_hash, sizeof(dap_hash_fast_t), l_voting); pthread_rwlock_unlock(&s_votings_rwlock); if(!l_voting || l_voting->net_id.uint64 != a_net->pub.id.uint64) { - log_it(L_ERROR, "Can't find voting with hash %s in net %s", + log_it(L_ERROR, "Can't find poll with hash %s in net %s", dap_chain_hash_fast_to_str_static(&a_voting_hash), a_net->pub.name); return -1; } - dap_chain_net_voting_cond_outs_t *l_tx_outs = NULL; + dap_chain_net_voting_cond_outs_t *l_tx_out = NULL; pthread_rwlock_wrlock(&l_voting->s_tx_outs_rwlock); - HASH_FIND(hh, l_voting->voting_spent_cond_outs, &a_tx_cond_hash, sizeof(dap_hash_fast_t), l_tx_outs); + HASH_FIND(hh, l_voting->voting_spent_cond_outs, &a_tx_cond_hash, sizeof(dap_hash_fast_t), l_tx_out); pthread_rwlock_unlock(&l_voting->s_tx_outs_rwlock); - if (!l_tx_outs || l_tx_outs->out_idx != a_cond_out_idx){ - return 0; - } + if (l_tx_out && l_tx_out->out_idx == a_cond_out_idx) + return a_pkey_hash ? !dap_hash_fast_compare(a_pkey_hash, &l_tx_out->pkey_hash) : 1; - return 1; + return 0; } int dap_chain_net_vote_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) { + 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){ return DAP_CHAIN_NET_VOTE_CREATE_LENGTH_QUESTION_OVERSIZE_MAX; @@ -1248,8 +1254,7 @@ int dap_chain_net_vote_create(const char *a_question, dap_list_t *a_options, dap dap_ledger_t* l_ledger = a_net->pub.ledger; dap_list_t *l_list_used_out = NULL; if (dap_chain_wallet_cache_tx_find_outs_with_val(a_net, l_native_ticker, l_addr_from, &l_list_used_out, l_total_fee, &l_value_transfer) == -101) - l_list_used_out = dap_ledger_get_list_tx_outs_with_val(l_ledger, l_native_ticker, - l_addr_from, l_total_fee, &l_value_transfer); + l_list_used_out = dap_ledger_get_list_tx_outs_with_val(l_ledger, l_native_ticker, l_addr_from, l_total_fee, &l_value_transfer); if (!l_list_used_out) { return DAP_CHAIN_NET_VOTE_CREATE_NOT_ENOUGH_FUNDS_TO_TRANSFER; } @@ -1332,6 +1337,16 @@ int dap_chain_net_vote_create(const char *a_question, dap_list_t *a_options, dap 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)); @@ -1339,7 +1354,7 @@ int dap_chain_net_vote_create(const char *a_question, dap_list_t *a_options, dap 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); @@ -1359,7 +1374,7 @@ int dap_chain_net_vote_create(const char *a_question, dap_list_t *a_options, dap 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; } @@ -1406,47 +1421,42 @@ int dap_chain_net_vote_voting(dap_cert_t *a_cert, uint256_t a_fee, dap_chain_wal if (l_voting->voting_params.voting_expire && dap_time_now() > l_voting->voting_params.voting_expire) return DAP_CHAIN_NET_VOTE_VOTING_ALREADY_EXPIRED; - dap_hash_fast_t l_pkey_hash = {0}; + 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->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(&((dap_chain_net_vote_t *)it->data)->pkey_hash, &l_pkey_hash) && - !l_voting->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(&((dap_chain_net_vote_t *)it->data)->pkey_hash, &l_pkey_hash)) { + if (!l_voting->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->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); + if (l_net_fee_used) + 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) { + 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; } @@ -1455,24 +1465,42 @@ int dap_chain_net_vote_voting(dap_cert_t *a_cert, uint256_t a_fee, dap_chain_wal dap_list_t *it, *tmp; 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; - if (s_datum_tx_voting_coin_check_spent(a_net, a_hash, l_out->tx_hash_fast, l_out->num_idx_out, &l_pkey_hash) && - !l_voting->voting_params.vote_changing_allowed) { - dap_list_delete_link(l_list_used_out, it); + if (s_datum_tx_voting_coin_check_spent(a_net, a_hash, l_out->tx_hash_fast, l_out->num_idx_out, + l_vote_changed ? &l_pkey_hash : NULL)) { + 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; // create empty transaction dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create(); + uint256_t l_value_back = l_value_transfer, l_fee_back = {}; + if (!l_native_tx) { + dap_list_t *l_list_fee_outs = dap_ledger_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->voting_params.option_offsets_list)){ dap_chain_datum_tx_delete(l_tx); @@ -1486,17 +1514,13 @@ int dap_chain_net_vote_voting(dap_cert_t *a_cert, uint256_t a_fee, dap_chain_wal dap_chain_datum_tx_add_item(&l_tx, l_vote_item); DAP_DEL_Z(l_vote_item); - // add stake out conds items - dap_list_t *l_outs = dap_ledger_get_list_tx_cond_outs(l_ledger, a_net->pub.native_ticker, l_addr_from, - DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK, NULL); - dap_list_t *l_temp = l_outs; - while(l_temp){ - dap_chain_tx_used_out_item_t *l_out_item = (dap_chain_tx_used_out_item_t *)l_temp->data; - if (dap_ledger_tx_hash_is_used_out_item(a_net->pub.ledger, &l_out_item->tx_hash_fast, l_out_item->num_idx_out, NULL) || - s_datum_tx_voting_coin_check_cond_out(a_net, a_hash, l_out_item->tx_hash_fast, l_out_item->num_idx_out ) != 0){ - l_temp = l_temp->next; + // add out conds items + 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; + if (s_datum_tx_voting_coin_check_cond_out(a_net, a_hash, l_out_item->tx_hash_fast, l_out_item->num_idx_out, + l_vote_changed ? &l_pkey_hash : NULL) != 0) 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){ dap_chain_datum_tx_delete(l_tx); @@ -1506,55 +1530,44 @@ int dap_chain_net_vote_voting(dap_cert_t *a_cert, uint256_t a_fee, dap_chain_wal } dap_chain_datum_tx_add_item(&l_tx, l_item); DAP_DEL_Z(l_item); - l_temp = l_temp->next; } dap_list_free_full(l_outs, NULL); - // 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); 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 4bd1f71694180eb6b8f390da41c401cca1241aec..70509562bd780f59492b249905f881a3866799a3 100644 --- a/modules/service/voting/include/dap_chain_net_srv_voting.h +++ b/modules/service/voting/include/dap_chain_net_srv_voting.h @@ -94,13 +94,14 @@ enum DAP_CHAIN_NET_VOTE_CREATE_ERROR { 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_CREATE_UNKNOWN_ERR }; int dap_chain_net_vote_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); + dap_chain_net_t *a_net, const char *a_token_ticker, const char *a_hash_out_type, char **a_hash_output); enum DAP_CHAIN_NET_VOTE_VOTING_ERROR{ DAP_CHAIN_NET_VOTE_VOTING_OK, @@ -108,7 +109,6 @@ enum DAP_CHAIN_NET_VOTE_VOTING_ERROR{ 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, @@ -125,13 +125,13 @@ enum DAP_CHAIN_NET_VOTE_VOTING_ERROR{ 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, DAP_CHAIN_NET_VOTE_VOTING_WALLET_PARAM_NOT_VALID, DAP_CHAIN_NET_VOTE_VOTING_OPTION_IDX_PARAM_NOT_VALID, DAP_CHAIN_NET_VOTE_VOTING_WALLET_DOES_NOT_EXIST, - DAP_CHAIN_NET_VOTE_VOTING_UNKNOWN_ERR, DAP_CHAIN_NET_VOTE_VOTING_INTEGER_OVERFLOW @@ -140,6 +140,7 @@ enum DAP_CHAIN_NET_VOTE_VOTING_ERROR{ enum DAP_CHAIN_NET_VOTE_DUMP_ERROR{ DAP_CHAIN_NET_VOTE_DUMP_HASH_PARAM_NOT_FOUND, + DAP_CHAIN_NET_VOTE_DUMP_HASH_PARAM_INVALID, DAP_CHAIN_NET_VOTE_DUMP_CAN_NOT_FIND_VOTE, DAP_CHAIN_NET_VOTE_DUMP_NO_OPTIONS, DAP_CHAIN_NET_VOTE_DUMP_MEMORY_ERR