diff --git a/modules/net/dap_chain_net_voting.c b/modules/net/dap_chain_net_voting.c index 00d39e9717ad367b3c67c03a3e4f349a8644bb16..aa1c047126a0491e35a21c423e0c403f1aa2c62c 100644 --- a/modules/net/dap_chain_net_voting.c +++ b/modules/net/dap_chain_net_voting.c @@ -50,6 +50,12 @@ typedef struct dap_chain_net_voting_params_offsets{ size_t vote_changing_allowed_offset; } dap_chain_net_voting_params_offsets_t; + +typedef struct dap_chain_net_voting{ + dap_hash_fast_t hash_vote; + dap_chain_net_id_t net_id; +}dap_chain_net_voting_t; + typedef struct dap_chain_net_vote_option { size_t vote_option_offset; size_t vote_option_length; @@ -578,11 +584,6 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) return -102; } uint256_t l_value_fee = dap_chain_balance_scan(l_fee_str); - if (IS_ZERO_256(l_value_fee)) { - dap_cli_server_cmd_set_reply_text(a_str_reply, - "command requires parameter '-fee' to be valid uint256"); - return -103; - } dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-w", &l_wallet_str); if (!l_wallet_str){ @@ -590,187 +591,99 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) return -103; } - dap_enc_key_t *l_priv_key = NULL; + dap_time_t *l_time_expire = NULL; + if(l_voting_expire_str){ + dap_time_t l_expired_time = dap_time_from_str_rfc822(l_voting_expire_str); + l_time_expire = &l_expired_time; + } + uint64_t *l_max_count = NULL; + if (l_max_votes_count_str) { + uint64_t ll_max_count = atoll(l_max_votes_count_str); + l_max_count = &ll_max_count; + } + + bool l_is_delegated_key = dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-delegated_key_required", NULL) ? true : false; + bool l_is_vote_changing_allowed = dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-vote_changing_allowed", NULL) ? true : false; const char *c_wallets_path = dap_chain_wallet_get_path(g_config); dap_chain_wallet_t *l_wallet_fee = dap_chain_wallet_open(l_wallet_str, c_wallets_path); if (!l_wallet_fee) { dap_cli_server_cmd_set_reply_text(a_str_reply, "Wallet %s does not exist", l_wallet_str); return -112; } - l_priv_key = dap_chain_wallet_get_key(l_wallet_fee, 0); - - const dap_chain_addr_t *l_addr_from = (const dap_chain_addr_t *) dap_chain_wallet_get_addr(l_wallet_fee, l_net->pub.id); - - if(!l_addr_from) { - dap_cli_server_cmd_set_reply_text(a_str_reply, "source address is invalid"); - return -113; - } - - const char *l_native_ticker = l_net->pub.native_ticker; - uint256_t l_net_fee = {}, l_total_fee = {}, l_value_transfer; - dap_chain_addr_t l_addr_fee = {}; - bool l_net_fee_used = dap_chain_net_tx_get_fee(l_net->pub.id, &l_net_fee, &l_addr_fee); - SUM_256_256(l_net_fee, l_value_fee, &l_total_fee); - - dap_ledger_t* l_ledger = dap_ledger_by_net_name(l_net->pub.name); - dap_list_t *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) { - dap_cli_server_cmd_set_reply_text(a_str_reply, "Not enough funds to transfer"); - return -113; - } - // create empty transaction - dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create(); - - // Add Voting item - dap_chain_tx_voting_t* l_voting_item = dap_chain_datum_tx_item_voting_create(); - dap_chain_datum_tx_add_item(&l_tx, l_voting_item); - DAP_DELETE(l_voting_item); - - // Add question to tsd data - dap_chain_tx_tsd_t* l_question_tsd = dap_chain_datum_voting_question_tsd_create(l_question_str, strlen(l_question_str)); - dap_chain_datum_tx_add_item(&l_tx, l_question_tsd); - DAP_DELETE(l_question_tsd); - - // Add options to tsd - dap_list_t *l_temp = l_options_list; - while(l_temp){ - if(strlen((char*)l_temp->data) > DAP_CHAIN_DATUM_TX_VOTING_OPTION_MAX_LENGTH){ - dap_chain_datum_tx_delete(l_tx); - dap_list_free_full(l_options_list, NULL); - dap_cli_server_cmd_set_reply_text(a_str_reply, "The option must contain no more than %d characters", DAP_CHAIN_DATUM_TX_VOTING_OPTION_MAX_LENGTH); - return -114; - } - dap_chain_tx_tsd_t* l_option = dap_chain_datum_voting_answer_tsd_create((char*)l_temp->data, strlen((char*)l_temp->data)); - if(!l_option){ - dap_chain_datum_tx_delete(l_tx); - dap_list_free_full(l_options_list, NULL); - dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't create option tsd item."); - return -114; - } - dap_chain_datum_tx_add_item(&l_tx, l_option); - DAP_DEL_Z(l_option); - - l_temp = l_temp->next; - } - dap_list_free_full(l_options_list, NULL); - - // add voting expire time if needed - if(l_voting_expire_str){ - dap_time_t l_expired_time = dap_time_from_str_rfc822(l_voting_expire_str); - if(!l_expired_time){ - dap_chain_datum_tx_delete(l_tx); - dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't parse expire time"); - return -114; - } - if (l_expired_time < dap_time_now()){ - dap_chain_datum_tx_delete(l_tx); + 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); + + switch (res) { + case DAP_CHAIN_NET_VOTE_CREATE_OK: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Datum %s successfully added to mempool", l_hash_ret); + DAP_DELETE(l_hash_ret); + return DAP_CHAIN_NET_VOTE_CREATE_OK; + } break; + case DAP_CHAIN_NET_VOTE_CREATE_LENGTH_QUESTION_OVERSIZE_MAX: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "The question must contain no more than %d characters", + DAP_CHAIN_DATUM_TX_VOTING_QUESTION_MAX_LENGTH); + return DAP_CHAIN_NET_VOTE_CREATE_LENGTH_QUESTION_OVERSIZE_MAX; + } break; + case DAP_CHAIN_NET_VOTE_CREATE_COUNT_OPTION_OVERSIZE_MAX: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "The voting can to 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; + case DAP_CHAIN_NET_VOTE_CREATE_FEE_IS_ZERO: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "The commission amount must be greater than zero"); + return DAP_CHAIN_NET_VOTE_CREATE_FEE_IS_ZERO; + } break; + case DAP_CHAIN_NET_VOTE_CREATE_SOURCE_ADDRESS_IS_INVALID: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "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: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Not enough funds to transfer"); + return DAP_CHAIN_NET_VOTE_CREATE_NOT_ENOUGH_FUNDS_TO_TRANSFER; + } break; + case DAP_CHAIN_NET_VOTE_CREATE_MAX_COUNT_OPTION_EXCEEDED: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "The option must contain no more than %d characters", + DAP_CHAIN_DATUM_TX_VOTING_OPTION_MAX_LENGTH); + return DAP_CHAIN_NET_VOTE_CREATE_MAX_COUNT_OPTION_EXCEEDED; + } break; + case DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_OPTION_TSD_ITEM: { dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't create voting with expired time"); - return -114; - } - - dap_chain_tx_tsd_t* l_expired_item = dap_chain_datum_voting_expire_tsd_create(l_expired_time); - if(!l_expired_item){ - dap_chain_datum_tx_delete(l_tx); - dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't create expired tsd item."); - return -114; - } - dap_chain_datum_tx_add_item(&l_tx, l_expired_item); - DAP_DEL_Z(l_expired_item); - } - - // Add vote max count if needed - if(l_max_votes_count_str){ - uint64_t l_max_votes_count = atoll(l_max_votes_count_str); - dap_chain_tx_tsd_t* l_max_votes_item = dap_chain_datum_voting_max_votes_count_tsd_create(l_max_votes_count); - if(!l_max_votes_item){ - dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_OPTION_TSD_ITEM; + } break; + case DAP_CHAIN_NET_VOTE_CREATE_INPUT_TIME_MORE_CURRENT_TIME: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't create voting 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: { dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't create expired tsd item."); - return -114; - } - dap_chain_datum_tx_add_item(&l_tx, l_max_votes_item); - DAP_DEL_Z(l_max_votes_item); - } - - if(dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-delegated_key_required", NULL)){ - dap_chain_tx_tsd_t* l_delegated_key_req_item = dap_chain_datum_voting_delegated_key_required_tsd_create(true); - if(!l_delegated_key_req_item){ - dap_chain_datum_tx_delete(l_tx); - dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't create delegated key req tsd item."); - return -114; - } - dap_chain_datum_tx_add_item(&l_tx, l_delegated_key_req_item); - DAP_DEL_Z(l_delegated_key_req_item); - } - - if(dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-vote_changing_allowed", NULL)){ - dap_chain_tx_tsd_t* l_vote_changing_item = dap_chain_datum_voting_vote_changing_allowed_tsd_create(true); - if(!l_vote_changing_item){ - dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_CREATE_TSD_EXPIRE_TIME; + } break; + case DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_CREATE_TSD_DELEGATE_KEY: { dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't create delegated key req tsd item."); - return -114; - } - dap_chain_datum_tx_add_item(&l_tx, l_vote_changing_item); - DAP_DEL_Z(l_vote_changing_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)); - 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); - dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't add net fee out."); - return -114; - } - } - // Validator's fee - if (!IS_ZERO_256(l_value_fee)) { - if (dap_chain_datum_tx_add_fee_item(&l_tx, l_value_fee) == 1) - SUM_256_256(l_value_pack, l_value_fee, &l_value_pack); - else { - dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_CREATE_TSD_DELEGATE_KEY; + } break; + case DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_ADD_NET_FEE_OUT: { dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't add net fee out."); - return -115; - } - } - // 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_CREATE_CAN_NOT_ADD_NET_FEE_OUT; + } break; + case DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_ADD_OUT_WITH_VALUE_BACK: { dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't add out with value back"); - return -116; + return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_ADD_OUT_WITH_VALUE_BACK; + } break; + case DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_SIGNED_TX: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Can not sign transaction"); + return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_SIGNED_TX; + } break; + case DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_POOL_DATUM_IN_MEMPOOL: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Can not pool transaction in mempool"); + return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_POOL_DATUM_IN_MEMPOOL; + } break; + default: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Unknown error. Code: %d", res); + return -1; } } - - // add 'sign' items - if(dap_chain_datum_tx_add_sign_item(&l_tx, l_priv_key) != 1) { - dap_chain_datum_tx_delete(l_tx); - dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't sign tx"); - return -117; - } - - size_t l_tx_size = dap_chain_datum_tx_get_size(l_tx); - dap_hash_fast_t l_tx_hash; - dap_hash_fast(l_tx, l_tx_size, &l_tx_hash); - dap_chain_datum_t *l_datum = dap_chain_datum_create(DAP_CHAIN_DATUM_TX, l_tx, l_tx_size); - DAP_DELETE(l_tx); - dap_chain_t* l_chain = dap_chain_net_get_default_chain_by_chain_type(l_net, CHAIN_TYPE_TX); - - char *l_ret = dap_chain_mempool_datum_add(l_datum, l_chain, l_hash_out_type); - if (l_ret) - dap_cli_server_cmd_set_reply_text(a_str_reply, "Datum %s successfully added to mempool", l_ret); - else - dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't add datum to mempool"); - DAP_DELETE(l_datum); }break; case CMD_VOTE:{ const char* l_cert_name = NULL; @@ -797,19 +710,6 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't find \"%s\" certificate", l_cert_name); return -7; } - if (l_cert->enc_key == NULL) { - dap_cli_server_cmd_set_reply_text(a_str_reply, "No key found in \"%s\" certificate", l_cert_name ); - return -8; - } - // Get publivc key hash - size_t l_pub_key_size = 0; - uint8_t *l_pub_key = dap_enc_key_serialize_pub_key(l_cert->enc_key, &l_pub_key_size);; - if (l_pub_key == NULL) { - dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't serialize public key of certificate \"%s\"", l_cert_name); - return -9; - } - - dap_hash_fast(l_pub_key, l_pub_key_size, &l_pkey_hash); } dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-fee", &l_fee_str); @@ -836,221 +736,87 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) return -103; } - 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 || l_voting->net_id.uint64 != l_net->pub.id.uint64){ - dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't find voting with hash %s", l_hash_str); - return -111; - } - - if(l_voting->voting_params.votes_max_count_offset){ - uint64_t l_max_count = *(uint64_t*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.votes_max_count_offset); - if (l_max_count && dap_list_length(l_voting->votes) >= l_max_count){ - dap_cli_server_cmd_set_reply_text(a_str_reply, "This voting already received the required number of votes."); - return -111; - } - } - - if(l_voting->voting_params.voting_expire_offset){ - dap_time_t l_expire = *(dap_time_t*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.voting_expire_offset); - dap_time_t l_time_now = dap_time_now(); - if (l_expire && l_time_now > l_expire){ - dap_cli_server_cmd_set_reply_text(a_str_reply, "This voting already expired."); - return -111; - } - } - - if(l_voting->voting_params.delegate_key_required_offset && - *(bool*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.delegate_key_required_offset) ){ - if (!l_cert){ - dap_cli_server_cmd_set_reply_text(a_str_reply, "This voting required a delegated key."); - return -111; - } else if(!dap_chain_net_srv_stake_check_pkey_hash(&l_pkey_hash)){ - dap_cli_server_cmd_set_reply_text(a_str_reply, "Your key is not delegated."); - return -111; - } - } - - if(l_cert){ - dap_list_t *l_temp = l_voting->votes; - while(l_temp){ - if (dap_hash_fast_compare(&((dap_chain_net_vote_t *)l_temp->data)->pkey_hash, &l_pkey_hash)){ - if(!l_voting->voting_params.vote_changing_allowed_offset || - !*(bool*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.vote_changing_allowed_offset)){ - dap_cli_server_cmd_set_reply_text(a_str_reply, "The voting doesn't allow change your vote."); - return -113; - } - } - l_temp = l_temp->next; - } - } - - dap_enc_key_t *l_priv_key = NULL; 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); if (!l_wallet_fee) { dap_cli_server_cmd_set_reply_text(a_str_reply, "Wallet %s does not exist", l_wallet_str); return -112; } - l_priv_key = dap_chain_wallet_get_key(l_wallet_fee, 0); - - const dap_chain_addr_t *l_addr_from = (const dap_chain_addr_t *) dap_chain_wallet_get_addr(l_wallet_fee, l_net->pub.id); - - if(!l_addr_from) { - dap_cli_server_cmd_set_reply_text(a_str_reply, "source address is invalid"); - return -113; - } - - const char *l_native_ticker = l_net->pub.native_ticker; - uint256_t l_net_fee = {}, l_total_fee = {}, l_value_transfer; - dap_chain_addr_t l_addr_fee = {}; - bool l_net_fee_used = dap_chain_net_tx_get_fee(l_net->pub.id, &l_net_fee, &l_addr_fee); - SUM_256_256(l_net_fee, l_value_fee, &l_total_fee); - - dap_ledger_t* l_ledger = dap_ledger_by_net_name(l_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) { - dap_cli_server_cmd_set_reply_text(a_str_reply, "Not enough funds to transfer"); - return -113; - } - - // check outputs UTXOs - dap_list_t *l_utxo_temp = l_list_used_out; - uint256_t l_value_transfer_new = {}; - while(l_utxo_temp){ - dap_chain_tx_used_out_item_t *l_out = (dap_chain_tx_used_out_item_t *)l_utxo_temp->data; - if (s_datum_tx_voting_coin_check_spent(l_net, l_voting_hash, l_out->tx_hash_fast, l_out->num_idx_out) != 0 && - (!l_voting->voting_params.vote_changing_allowed_offset || - !*(bool*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.vote_changing_allowed_offset))){ - dap_list_t *l_temp = l_utxo_temp; - l_utxo_temp = l_utxo_temp->next; - dap_list_delete_link(l_list_used_out, l_temp); - continue; - } - SUM_256_256(l_value_transfer_new, l_out->value, &l_value_transfer_new); - l_utxo_temp = l_utxo_temp->next; - } - - if (IS_ZERO_256(l_value_transfer_new) || compare256(l_value_transfer_new, l_total_fee) <= 0){ - dap_cli_server_cmd_set_reply_text(a_str_reply, "You have not unspent UTXO for participation in this voting."); - return -113; - } - - l_value_transfer = l_value_transfer_new; - - // create empty transaction - dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create(); - - // Add vote item uint64_t l_option_idx_count = atoll(l_option_idx_str); - if (l_option_idx_count > dap_list_length(l_voting->voting_params.option_offsets_list)){ - dap_chain_datum_tx_delete(l_tx); - dap_cli_server_cmd_set_reply_text(a_str_reply, "Invalid option index."); - return -114; - } - dap_chain_tx_vote_t* l_vote_item = dap_chain_datum_tx_item_vote_create(&l_voting_hash, &l_option_idx_count); - if(!l_vote_item){ - dap_chain_datum_tx_delete(l_tx); - dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't create vote item."); - return -114; - } - 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, l_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(l_net->pub.ledger, &l_out_item->tx_hash_fast, l_out_item->num_idx_out, NULL) || - s_datum_tx_voting_coin_check_cond_out(l_net, l_voting_hash, l_out_item->tx_hash_fast, l_out_item->num_idx_out ) != 0){ - l_temp = l_temp->next; - 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); + + 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, + l_net, l_hash_out_type, &l_hash_tx); + switch (res) { + case DAP_CHAIN_NET_VOTE_VOTING_OK: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Datum %s successfully added to mempool", l_hash_tx); + DAP_DELETE(l_hash_tx); + return DAP_CHAIN_NET_VOTE_CREATE_OK; + } break; + case DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_FIND_VOTE: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't find voting with hash %s", l_hash_str); + } break; + case DAP_CHAIN_NET_VOTE_VOTING_THIS_VOTING_HAVE_MAX_VALUE_VOTES: { + dap_cli_server_cmd_set_reply_text(a_str_reply, + "This voting already received the required number of votes."); + } break; + case DAP_CHAIN_NET_VOTE_VOTING_ALREADY_EXPIRED: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "This voting already expired."); + } break; + case DAP_CHAIN_NET_VOTE_VOTING_NO_KEY_FOUND_IN_CERT: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "No key found in \"%s\" certificate", l_cert_name); + } break; + case DAP_CHAIN_NET_VOTE_VOTING_NO_PUBLIC_KEY_IN_CERT: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't serialize public key of certificate \"%s\"", + l_cert_name); + } break; + case DAP_CHAIN_NET_VOTE_VOTING_KEY_IS_NOT_DELEGATED: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Your key is not delegated."); + } break; + case DAP_CHAIN_NET_VOTE_VOTING_DOES_NOT_ALLOW_CHANGE_YOUR_VOTE: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "The voting doesn't allow change your vote."); + } break; + case DAP_CHAIN_NET_VOTE_VOTING_SOURCE_ADDRESS_INVALID: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "source address is invalid"); + } break; + case DAP_CHAIN_NET_VOTE_VOTING_NOT_ENOUGH_FUNDS_TO_TRANSFER: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Not enough funds to transfer"); + } break; + case DAP_CHAIN_NET_VOTE_VOTING_UNSPENT_UTX0_FOR_PARTICIPATION_THIS_VOTING: { + dap_cli_server_cmd_set_reply_text(a_str_reply, + "You have not unspent UTXO for participation in this voting."); + } break; + case DAP_CHAIN_NET_VOTE_VOTING_INVALID_OPTION_INDEX: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Invalid option index."); + } break; + case DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_CREATE_VOTE_ITEM: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't create vote item."); + } break; + case DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_CREATE_TSD_TX_COND_ITEM: { dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't create tsd tx cond item."); - dap_list_free_full(l_outs, NULL); - return -114; - } - 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); - dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't add net fee out."); - return -114; - } - } - // Validator's fee - if (!IS_ZERO_256(l_value_fee)) { - if (dap_chain_datum_tx_add_fee_item(&l_tx, l_value_fee) == 1) - SUM_256_256(l_value_pack, l_value_fee, &l_value_pack); - else { - dap_chain_datum_tx_delete(l_tx); + } break; + case DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_NET_FEE_OUT: { dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't add net fee out."); - return -115; - } - } - // 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); + } break; + case DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_OUT_WITH_VALUE_BACK: { dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't add out with value back"); - return -116; } - } - - // add 'sign' items with wallet sign - if(dap_chain_datum_tx_add_sign_item(&l_tx, l_priv_key) != 1) { - dap_chain_datum_tx_delete(l_tx); - dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't sign tx"); - return -117; - } - - // add 'sign' items with delegated key if needed - if(l_cert){ - if(dap_chain_datum_tx_add_sign_item(&l_tx, l_cert->enc_key) != 1) { - dap_chain_datum_tx_delete(l_tx); + break; + case DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_SIGN_TX: { dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't sign tx"); - return -117; } + break; + case DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_POOL_IN_MEMPOOL: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't add datum to mempool"); + } + break; + default: { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Undefined error code: %d", res); + } break; } - - size_t l_tx_size = dap_chain_datum_tx_get_size(l_tx); - dap_hash_fast_t l_tx_hash; - dap_hash_fast(l_tx, l_tx_size, &l_tx_hash); - dap_chain_datum_t *l_datum = dap_chain_datum_create(DAP_CHAIN_DATUM_TX, l_tx, l_tx_size); - DAP_DELETE(l_tx); - dap_chain_t* l_chain = dap_chain_net_get_default_chain_by_chain_type(l_net, CHAIN_TYPE_TX); - - char *l_ret = dap_chain_mempool_datum_add(l_datum, l_chain, l_hash_out_type); - if (l_ret) - dap_cli_server_cmd_set_reply_text(a_str_reply, "Datum %s successfully added to mempool", l_ret); - else - dap_cli_server_cmd_set_reply_text(a_str_reply, "Can't add datum to mempool"); - DAP_DELETE(l_datum); - - + return res; }break; case CMD_LIST:{ dap_string_t *l_str_out = dap_string_new(NULL); @@ -1357,3 +1123,476 @@ static int s_datum_tx_voting_coin_check_cond_out(dap_chain_net_t *a_net, dap_has return 1; } + +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) { + + if (strlen(a_question) > DAP_CHAIN_DATUM_TX_VOTING_QUESTION_MAX_LENGTH){ + return DAP_CHAIN_NET_VOTE_CREATE_LENGTH_QUESTION_OVERSIZE_MAX; + } + + // Parse options list + + if(dap_list_length(a_options) > DAP_CHAIN_DATUM_TX_VOTING_OPTION_MAX_COUNT){ + return DAP_CHAIN_NET_VOTE_CREATE_COUNT_OPTION_OVERSIZE_MAX; + } + + if (IS_ZERO_256(a_fee)) { + return DAP_CHAIN_NET_VOTE_CREATE_FEE_IS_ZERO; + } + + dap_enc_key_t *l_priv_key = NULL; + l_priv_key = dap_chain_wallet_get_key(a_wallet, 0); + + const dap_chain_addr_t *l_addr_from = (const dap_chain_addr_t *) dap_chain_wallet_get_addr(a_wallet, a_net->pub.id); + + if(!l_addr_from) { + return DAP_CHAIN_NET_VOTE_CREATE_SOURCE_ADDRESS_IS_INVALID; + } + + const char *l_native_ticker = a_net->pub.native_ticker; + uint256_t l_net_fee = {}, l_total_fee = {}, l_value_transfer; + dap_chain_addr_t l_addr_fee = {}; + bool l_net_fee_used = dap_chain_net_tx_get_fee(a_net->pub.id, &l_net_fee, &l_addr_fee); + SUM_256_256(l_net_fee, a_fee, &l_total_fee); + + dap_ledger_t* l_ledger = a_net->pub.ledger; + dap_list_t *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; + } + // create empty transaction + dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create(); + + // Add Voting item + dap_chain_tx_voting_t* l_voting_item = dap_chain_datum_tx_item_voting_create(); + + dap_chain_datum_tx_add_item(&l_tx, l_voting_item); + DAP_DELETE(l_voting_item); + + // Add question to tsd data + dap_chain_tx_tsd_t* l_question_tsd = dap_chain_datum_voting_question_tsd_create(a_question, strlen(a_question)); + dap_chain_datum_tx_add_item(&l_tx, l_question_tsd); + + // Add options to tsd + dap_list_t *l_temp = a_options; + while(l_temp){ + if(strlen((char*)l_temp->data) > DAP_CHAIN_DATUM_TX_VOTING_OPTION_MAX_LENGTH){ + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_CREATE_MAX_COUNT_OPTION_EXCEEDED; + } + dap_chain_tx_tsd_t* l_option = dap_chain_datum_voting_answer_tsd_create((char*)l_temp->data, strlen((char*)l_temp->data)); + if(!l_option){ + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_OPTION_TSD_ITEM; + } + dap_chain_datum_tx_add_item(&l_tx, l_option); + DAP_DEL_Z(l_option); + + l_temp = l_temp->next; + } + + // add voting expire time if needed + if(a_expire_vote){ + dap_time_t l_expired_vote = *a_expire_vote; + if (*a_expire_vote < dap_time_now()){ + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_CREATE_INPUT_TIME_MORE_CURRENT_TIME; + } + + dap_chain_tx_tsd_t* l_expired_item = dap_chain_datum_voting_expire_tsd_create(l_expired_vote); + if(!l_expired_item){ + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_CREATE_TSD_EXPIRE_TIME; + } + dap_chain_datum_tx_add_item(&l_tx, l_expired_item); + DAP_DEL_Z(l_expired_item); + } + + // Add vote max count if needed + if(a_max_vote){ + dap_chain_tx_tsd_t* l_max_votes_item = dap_chain_datum_voting_max_votes_count_tsd_create(*a_max_vote); + if(!l_max_votes_item){ + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_CREATE_TSD_EXPIRE_TIME; + } + dap_chain_datum_tx_add_item(&l_tx, l_max_votes_item); + DAP_DEL_Z(l_max_votes_item); + } + + if (a_delegated_key_required) { + dap_chain_tx_tsd_t* l_delegated_key_req_item = dap_chain_datum_voting_delegated_key_required_tsd_create(true); + if(!l_delegated_key_req_item){ + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_CREATE_TSD_DELEGATE_KEY; + } + dap_chain_datum_tx_add_item(&l_tx, l_delegated_key_req_item); + DAP_DEL_Z(l_delegated_key_req_item); + } + + if(a_vote_changing_allowed){ + dap_chain_tx_tsd_t* l_vote_changing_item = dap_chain_datum_voting_vote_changing_allowed_tsd_create(true); + if(!l_vote_changing_item){ + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_CREATE_TSD_DELEGATE_KEY; + } + dap_chain_datum_tx_add_item(&l_tx, l_vote_changing_item); + DAP_DEL_Z(l_vote_changing_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)); + 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_CREATE_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_CREATE_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_CREATE_CAN_NOT_ADD_OUT_WITH_VALUE_BACK; + } + } + + // add 'sign' items + if(dap_chain_datum_tx_add_sign_item(&l_tx, l_priv_key) != 1) { + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_SIGNED_TX; + } + + size_t l_tx_size = dap_chain_datum_tx_get_size(l_tx); + dap_hash_fast_t l_tx_hash; + dap_hash_fast(l_tx, l_tx_size, &l_tx_hash); + dap_chain_datum_t *l_datum = dap_chain_datum_create(DAP_CHAIN_DATUM_TX, l_tx, l_tx_size); + DAP_DELETE(l_tx); + dap_chain_t* l_chain = dap_chain_net_get_default_chain_by_chain_type(a_net, CHAIN_TYPE_TX); + + char *l_ret = dap_chain_mempool_datum_add(l_datum, l_chain, a_hash_out_type); + DAP_DELETE(l_datum); + if (l_ret) { + *a_hash_output = l_ret; + return DAP_CHAIN_NET_VOTE_CREATE_OK; + } else { + return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_POOL_DATUM_IN_MEMPOOL; + } +} + +int dap_chain_net_vote_voting(dap_cert_t *a_cert, uint256_t a_fee, dap_chain_wallet_t *a_wallet, dap_hash_fast_t a_hash, + uint64_t a_option_idx, dap_chain_net_t *a_net, const char *a_hash_out_type, + char **a_hash_tx_out) { + + + dap_chain_net_votings_t *l_voting = NULL; + pthread_rwlock_rdlock(&s_votings_rwlock); + HASH_FIND(hh, s_votings, &a_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){ + return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_FIND_VOTE; + } + + if(l_voting->voting_params.votes_max_count_offset){ + uint64_t l_max_count = *(uint64_t*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.votes_max_count_offset); + if (l_max_count && dap_list_length(l_voting->votes) >= l_max_count){ + return DAP_CHAIN_NET_VOTE_VOTING_THIS_VOTING_HAVE_MAX_VALUE_VOTES; + } + } + + if(l_voting->voting_params.voting_expire_offset){ + dap_time_t l_expire = *(dap_time_t*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.voting_expire_offset); + dap_time_t l_time_now = dap_time_now(); + if (l_expire && l_time_now > l_expire){ + return DAP_CHAIN_NET_VOTE_VOTING_ALREADY_EXPIRED; + } + } + + if(l_voting->voting_params.delegate_key_required_offset && + *(bool*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.delegate_key_required_offset) ){ + if (!a_cert) {} else { + if (a_cert->enc_key == NULL) { + 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_t l_pkey_hash = {0}; + + dap_hash_fast(l_pub_key, l_pub_key_size, &l_pkey_hash); + if (!dap_chain_net_srv_stake_check_pkey_hash(&l_pkey_hash)) { + return DAP_CHAIN_NET_VOTE_VOTING_KEY_IS_NOT_DELEGATED; + } + dap_list_t *l_temp = l_voting->votes; + while(l_temp){ + if (dap_hash_fast_compare(&((dap_chain_net_vote_t *)l_temp->data)->pkey_hash, &l_pkey_hash)){ + if(!l_voting->voting_params.vote_changing_allowed_offset || + !*(bool*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.vote_changing_allowed_offset)){ + return DAP_CHAIN_NET_VOTE_VOTING_DOES_NOT_ALLOW_CHANGE_YOUR_VOTE; + } + } + l_temp = l_temp->next; + } + } + } + + dap_enc_key_t *l_priv_key = NULL; + + l_priv_key = dap_chain_wallet_get_key(a_wallet, 0); + + const dap_chain_addr_t *l_addr_from = (const dap_chain_addr_t *) dap_chain_wallet_get_addr(a_wallet, a_net->pub.id); + + if(!l_addr_from) { + return DAP_CHAIN_NET_VOTE_VOTING_SOURCE_ADDRESS_INVALID; + } + + const char *l_native_ticker = a_net->pub.native_ticker; + uint256_t l_net_fee = {}, l_total_fee = {}, l_value_transfer; + dap_chain_addr_t l_addr_fee = {}; + bool l_net_fee_used = dap_chain_net_tx_get_fee(a_net->pub.id, &l_net_fee, &l_addr_fee); + SUM_256_256(l_net_fee, a_fee, &l_total_fee); + + dap_ledger_t* l_ledger = dap_ledger_by_net_name(a_net->pub.name); + dap_list_t *l_list_used_out = dap_ledger_get_list_tx_outs(l_ledger, l_native_ticker, l_addr_from, &l_value_transfer); + if (!l_list_used_out || compare256(l_value_transfer, l_total_fee) <= 0) { + return DAP_CHAIN_NET_VOTE_VOTING_NOT_ENOUGH_FUNDS_TO_TRANSFER; + } + + + // check outputs UTXOs + dap_list_t *l_utxo_temp = l_list_used_out; + uint256_t l_value_transfer_new = {}; + while(l_utxo_temp){ + dap_chain_tx_used_out_item_t *l_out = (dap_chain_tx_used_out_item_t *)l_utxo_temp->data; + if (s_datum_tx_voting_coin_check_spent(a_net, a_hash, l_out->tx_hash_fast, l_out->num_idx_out) != 0 && + (!l_voting->voting_params.vote_changing_allowed_offset || + !*(bool*)((byte_t*)l_voting->voting_params.voting_tx + l_voting->voting_params.vote_changing_allowed_offset))){ + dap_list_t *l_temp = l_utxo_temp; + l_utxo_temp = l_utxo_temp->next; + dap_list_delete_link(l_list_used_out, l_temp); + continue; + } + SUM_256_256(l_value_transfer_new, l_out->value, &l_value_transfer_new); + l_utxo_temp = l_utxo_temp->next; + } + + if (IS_ZERO_256(l_value_transfer_new) || 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(); + + // Add vote item + if (a_option_idx > dap_list_length(l_voting->voting_params.option_offsets_list)){ + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_VOTING_INVALID_OPTION_INDEX; + } + dap_chain_tx_vote_t* l_vote_item = dap_chain_datum_tx_item_vote_create(&a_hash, &a_option_idx); + if(!l_vote_item){ + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_CREATE_VOTE_ITEM; + } + 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; + 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); + + dap_list_free_full(l_outs, NULL); + return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_CREATE_TSD_TX_COND_ITEM; + } + 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; + } + } + // 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; + } + } + // 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; + } + } + + // add 'sign' items with wallet sign + if(dap_chain_datum_tx_add_sign_item(&l_tx, l_priv_key) != 1) { + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_SIGN_TX; + } + + // 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; + } + } + + size_t l_tx_size = dap_chain_datum_tx_get_size(l_tx); + dap_hash_fast_t l_tx_hash; + dap_hash_fast(l_tx, l_tx_size, &l_tx_hash); + dap_chain_datum_t *l_datum = dap_chain_datum_create(DAP_CHAIN_DATUM_TX, l_tx, l_tx_size); + DAP_DELETE(l_tx); + dap_chain_t* l_chain = dap_chain_net_get_default_chain_by_chain_type(a_net, CHAIN_TYPE_TX); + + char *l_ret = dap_chain_mempool_datum_add(l_datum, l_chain, a_hash_out_type); + DAP_DELETE(l_datum); + if (l_ret) { + *a_hash_tx_out = l_ret; + return DAP_CHAIN_NET_VOTE_VOTING_OK; + } else { + return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_POOL_IN_MEMPOOL; + } +} + +dap_chain_net_vote_info_t *s_dap_chain_net_vote_extract_info(dap_chain_net_votings_t *a_voting) { + if (!a_voting) { + return NULL; + } + dap_chain_net_vote_info_t *l_info = DAP_NEW(dap_chain_net_vote_info_t); + + l_info->question.question_size = a_voting->voting_params.voting_question_length; + l_info->question.question_str = (char*)((byte_t*)a_voting->voting_params.voting_tx + a_voting->voting_params.voting_question_offset); + l_info->hash = a_voting->voting_hash; + + l_info->is_expired = a_voting->voting_params.voting_expire_offset; + if(a_voting->voting_params.voting_expire_offset){ + l_info->expired = *(dap_time_t*)((byte_t*)a_voting->voting_params.voting_tx + a_voting->voting_params.voting_expire_offset); + } + l_info->is_max_count_votes = a_voting->voting_params.votes_max_count_offset; + if (a_voting->voting_params.votes_max_count_offset){ + l_info->max_count_votes = *(uint64_t*)((byte_t*)a_voting->voting_params.voting_tx + a_voting->voting_params.votes_max_count_offset); + } + l_info->is_changing_allowed = a_voting->voting_params.vote_changing_allowed_offset; + l_info->is_delegate_key_required = a_voting->voting_params.delegate_key_required_offset; + l_info->options.count_option = dap_list_length(a_voting->voting_params.option_offsets_list); + dap_chain_net_vote_info_option_t **l_options = DAP_NEW_Z_COUNT(dap_chain_net_vote_info_option_t*, l_info->options.count_option); + for (uint64_t i = 0; i < l_info->options.count_option; i++){ + dap_list_t* l_option = dap_list_nth(a_voting->voting_params.option_offsets_list, (uint64_t)i); + dap_chain_net_vote_option_t* l_vote_option = (dap_chain_net_vote_option_t*)l_option->data; + dap_chain_net_vote_info_option_t *l_option_info = DAP_NEW(dap_chain_net_vote_info_option_t); + l_option_info->option_idx = i; + l_option_info->description_size = l_vote_option->vote_option_length; + l_option_info->description = (char*)((byte_t*)a_voting->voting_params.voting_tx + l_vote_option->vote_option_offset); + l_option_info->votes_count = 0; + l_option_info->weight = uint256_0; + l_option_info->hashes_tx_votes = NULL; + for (dap_list_t *it = a_voting->votes; it; it = it->next) { + dap_chain_net_vote_t *l_vote = it->data; + if (l_option_info->option_idx != l_vote->answer_idx) { + continue; + } + l_option_info->votes_count++; + SUM_256_256(l_option_info->weight, l_vote->weight, &l_option_info->weight); + l_option_info->hashes_tx_votes = dap_list_append(l_option_info->hashes_tx_votes, &l_vote->vote_hash); + } + l_options[i] = l_option_info; + } + l_info->options.options = l_options; + return l_info; +} + +dap_list_t *dap_chain_net_vote_list(dap_chain_net_t *a_net) { + if (!a_net) + return NULL; + dap_chain_net_votings_t *l_voting = NULL, *l_tmp; + dap_list_t *l_list = NULL; + pthread_rwlock_rdlock(&s_votings_rwlock); + HASH_ITER(hh, s_votings, l_voting, l_tmp){ + if (l_voting->net_id.uint64 != a_net->pub.id.uint64) + continue; + dap_chain_net_vote_info_t *l_info = s_dap_chain_net_vote_extract_info(l_voting); + if (!l_info) + continue; + l_list = dap_list_append(l_list, l_info); + } + pthread_rwlock_unlock(&s_votings_rwlock); + return l_list; +} + +dap_chain_net_vote_info_t *dap_chain_net_vote_extract_info(dap_chain_net_t *a_net, dap_hash_fast_t *a_voting) { + if (!a_net || !a_voting) + return NULL; + dap_chain_net_votings_t *l_voting = NULL; + pthread_rwlock_rdlock(&s_votings_rwlock); + HASH_FIND(hh, s_votings, &a_voting, sizeof(dap_hash_fast_t), l_voting); + pthread_rwlock_unlock(&s_votings_rwlock); + if(!l_voting){ + return NULL; + } + return s_dap_chain_net_vote_extract_info(l_voting); +} + +void dap_chain_net_vote_info_free(dap_chain_net_vote_info_t *a_info){ + size_t l_count_options = a_info->options.count_option; + for (size_t i = 0; i < l_count_options; i++) { + dap_chain_net_vote_info_option_t *l_option = a_info->options.options[i]; + DAP_DELETE(l_option); + } + DAP_DELETE(a_info->options.options); + DAP_DELETE(a_info); +} diff --git a/modules/net/include/dap_chain_net_voting.h b/modules/net/include/dap_chain_net_voting.h index be180c501537576cfb75ddb5dfe2440656729978..9abec85abe37898ead8feec02b0476eaa3c7fc34 100644 --- a/modules/net/include/dap_chain_net_voting.h +++ b/modules/net/include/dap_chain_net_voting.h @@ -26,14 +26,88 @@ #include "dap_chain_ledger.h" #include "dap_chain_net.h" #include "dap_chain_common.h" +#include "dap_chain_wallet.h" - -typedef struct dap_chain_net_voting_result { - uint64_t answer_idx; +typedef struct dap_chain_net_vote_info_option{ + uint64_t option_idx; uint64_t votes_count; -} dap_chain_net_voting_result_t; + uint256_t weight; + uint64_t description_size; + char *description; + dap_list_t *hashes_tx_votes; +}dap_chain_net_vote_info_option_t; + +typedef struct dap_chain_net_vote_info{ + dap_hash_fast_t hash; + dap_chain_net_id_t net_id; + bool is_expired; + dap_time_t expired; + bool is_max_count_votes; + uint64_t max_count_votes; + bool is_changing_allowed; + bool is_delegate_key_required; + struct { + size_t question_size; + char *question_str; + } question; + struct { + uint64_t count_option; + dap_chain_net_vote_info_option_t **options; + } options; +}dap_chain_net_vote_info_t; + int dap_chain_net_voting_init(); uint64_t* dap_chain_net_voting_get_result(dap_ledger_t* a_ledger, dap_chain_hash_fast_t* a_voting_hash); + +enum DAP_CHAIN_NET_VOTE_CREATE_ERROR { + DAP_CHAIN_NET_VOTE_CREATE_OK, + DAP_CHAIN_NET_VOTE_CREATE_LENGTH_QUESTION_OVERSIZE_MAX, + DAP_CHAIN_NET_VOTE_CREATE_COUNT_OPTION_OVERSIZE_MAX, + DAP_CHAIN_NET_VOTE_CREATE_FEE_IS_ZERO, + DAP_CHAIN_NET_VOTE_CREATE_SOURCE_ADDRESS_IS_INVALID, + DAP_CHAIN_NET_VOTE_CREATE_NOT_ENOUGH_FUNDS_TO_TRANSFER, + DAP_CHAIN_NET_VOTE_CREATE_MAX_COUNT_OPTION_EXCEEDED, + DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_OPTION_TSD_ITEM, + DAP_CHAIN_NET_VOTE_CREATE_INPUT_TIME_MORE_CURRENT_TIME, + DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_CREATE_TSD_EXPIRE_TIME, + DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_CREATE_TSD_DELEGATE_KEY, + DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_ADD_NET_FEE_OUT, + DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_ADD_OUT_WITH_VALUE_BACK, + DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_SIGNED_TX, + DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_POOL_DATUM_IN_MEMPOOL +}; +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); + +enum DAP_CHAIN_NET_VOTE_VOTING_ERROR{ + DAP_CHAIN_NET_VOTE_VOTING_OK, + DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_FIND_VOTE, + DAP_CHAIN_NET_VOTE_VOTING_THIS_VOTING_HAVE_MAX_VALUE_VOTES, + DAP_CHAIN_NET_VOTE_VOTING_ALREADY_EXPIRED, + DAP_CHAIN_NET_VOTE_VOTING_NO_KEY_FOUND_IN_CERT, + DAP_CHAIN_NET_VOTE_VOTING_NO_PUBLIC_KEY_IN_CERT, + DAP_CHAIN_NET_VOTE_VOTING_KEY_IS_NOT_DELEGATED, + DAP_CHAIN_NET_VOTE_VOTING_DOES_NOT_ALLOW_CHANGE_YOUR_VOTE, + DAP_CHAIN_NET_VOTE_VOTING_SOURCE_ADDRESS_INVALID, + DAP_CHAIN_NET_VOTE_VOTING_NOT_ENOUGH_FUNDS_TO_TRANSFER, + DAP_CHAIN_NET_VOTE_VOTING_UNSPENT_UTX0_FOR_PARTICIPATION_THIS_VOTING, + DAP_CHAIN_NET_VOTE_VOTING_INVALID_OPTION_INDEX, + DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_CREATE_VOTE_ITEM, + DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_CREATE_TSD_TX_COND_ITEM, + DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_NET_FEE_OUT, + DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_OUT_WITH_VALUE_BACK, + DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_SIGN_TX, + DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_POOL_IN_MEMPOOL +}; +int dap_chain_net_vote_voting(dap_cert_t *a_cert, uint256_t a_fee, dap_chain_wallet_t *a_wallet, dap_hash_fast_t a_hash, + uint64_t a_option_idx, dap_chain_net_t *a_net, const char *a_hash_out_type, + char **a_hash_tx_out); + +dap_list_t *dap_chain_net_vote_list(dap_chain_net_t *a_net); +dap_chain_net_vote_info_t *dap_chain_net_vote_extract_info(dap_chain_net_t *a_net, dap_hash_fast_t *a_vote_hash); +void dap_chain_net_vote_info_free(dap_chain_net_vote_info_t *a_info);