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 60e5308c78f33e8a02743a542d476d6c03fcc2e9..2ca475517f3ba75ee7097a4603e1fa8a97be091d 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 @@ -73,6 +73,21 @@ typedef enum s_cli_srv_stake_err { DAP_CHAIN_NODE_CLI_SRV_STAKE_MIN_STAKE_SET_FAILED_ERR, DAP_CHAIN_NODE_CLI_SRV_STAKE_MAX_WEIGHT_SET_FAILED_ERR, DAP_CHAIN_NODE_CLI_SRV_STAKE_PERCENT_ERR, + + DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_PARAM_ERR, + DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_NET_PARAM_ERR, + DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_NODE_ADDR_ERR, + DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_ID_NET_ADDR_DIF_ERR, + DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_ADDR_WALLET_DIF_ERR, + DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_WALLET_ERR, + DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_NET_ERR, + DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_CHAIN_PARAM_ERR, + DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_ERR, + DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_INVALID_PKEY_ERR, + + /* add custom codes here */ + + //DAP_CHAIN_NODE_CLI_COM_TX_UNKNOWN /* MAX */ DAP_CHAIN_NODE_CLI_SRV_STAKE_UNRECOGNIZE_COM_ERR } s_cli_srv_stake_err_t; @@ -104,6 +119,9 @@ static void s_stake_deleted_callback(dap_ledger_t *a_ledger, dap_chain_datum_tx_ static void s_cache_data(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_chain_addr_t *a_signing_addr); static void s_uncache_data(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_chain_addr_t *a_signing_addr); +static json_object* s_dap_chain_net_srv_stake_reward_all(json_object* a_json_arr_reply, dap_chain_node_info_t *a_node_info, dap_chain_t *a_chain, + dap_chain_net_t *a_net, dap_time_t a_time_form, dap_time_t a_time_to, + size_t a_limit, size_t a_offset, bool a_brief, bool a_head); static bool s_debug_more = false; @@ -192,7 +210,10 @@ int dap_chain_net_srv_stake_pos_delegate_init() "srv_stake max_weight -net <net_name> [-chain <chain_name>] -poa_cert <poa_cert_name> -percent <value>\n" "\tSets maximum validator related weight (in percent)\n" "srv_stake check -net <net_name> -tx <tx_hash>\n" - "\tCheck remote validator\n\n" + "\tCheck remote validator\n" + "srv_stake reward -net <net_name> {-node_addr <node_address> | -all} [-date_from <YYMMDD> -date_to <YYMMDD>] [-brief] [-limit] [-offset] [-head]\n" + "\tShow the number of rewards for the validators\n" + "Hint:\n" "\texample coins amount syntax (only natural) 1.0 123.4567\n" "\texample datoshi amount syntax (only integer) 1 20 0.4321e+4\n" @@ -3000,7 +3021,7 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, void **a_str_reply) { json_object **a_json_arr_reply = (json_object **)a_str_reply; enum { - CMD_NONE, CMD_ORDER, CMD_DELEGATE, CMD_UPDATE, CMD_APPROVE, CMD_LIST, CMD_INVALIDATE, CMD_MIN_VALUE, CMD_CHECK, CMD_MAX_WEIGHT + CMD_NONE, CMD_ORDER, CMD_DELEGATE, CMD_UPDATE, CMD_APPROVE, CMD_LIST, CMD_INVALIDATE, CMD_MIN_VALUE, CMD_CHECK, CMD_MAX_WEIGHT, CMD_REWARD }; int l_arg_index = 1; @@ -3047,6 +3068,9 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, void **a_str_reply) else if(dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, dap_min(a_argc, l_arg_index + 1), "check", NULL)) { l_cmd_num = CMD_CHECK; } + else if(dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, dap_min(a_argc, l_arg_index + 1), "reward", NULL)) { + l_cmd_num = CMD_REWARD; + } switch (l_cmd_num) { @@ -3484,6 +3508,102 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, void **a_str_reply) return DAP_CHAIN_NODE_CLI_SRV_STAKE_MAX_WEIGHT_SET_FAILED_ERR; } } break; + case CMD_REWARD: { + const char *l_net_str = NULL, + *l_addr_str = NULL, + *l_limit_str = NULL, + *l_pkey_str = NULL, + *l_offset_str = NULL, + *l_d_from_str = NULL, + *l_d_to_str = NULL, + *l_head_str = NULL; + + + dap_chain_t * l_chain = NULL; + dap_chain_net_t * l_net = NULL; + dap_time_t l_from_time = 0, l_to_time = 0; + dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-net", &l_net_str); + dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-node_addr", &l_addr_str); + dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-pkey", &l_pkey_str); + dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-limit", &l_limit_str); + dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-offset", &l_offset_str); + dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-date_from", &l_d_from_str); + dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-date_to", &l_d_to_str); + bool l_head = dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-head", &l_head_str) ? true : false; + size_t l_limit = l_limit_str ? strtoul(l_limit_str, NULL, 10) : 0; + size_t l_offset = l_offset_str ? strtoul(l_offset_str, NULL, 10) : 0; + + bool l_brief = (dap_cli_server_cmd_check_option(a_argv, l_arg_index, a_argc, "-brief") != -1) ? true : false; + + uint32_t l_info_size = sizeof(dap_chain_node_info_t); + dap_chain_node_info_t *l_node_info = NULL; + + if (!l_net_str && !l_addr_str) { + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_PARAM_ERR, + "reward requires parameter '-net' or '-node_addr'"); + return DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_PARAM_ERR; + } + + // Select chain network + if (l_net_str) { + l_net = dap_chain_net_by_name(l_net_str); + if (!l_net) { // Can't find such network + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_NET_PARAM_ERR, + "reward requires parameter '-net' to be valid chain network name"); + return DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_NET_PARAM_ERR; + } + } + + if (l_d_from_str) { + l_from_time = dap_time_from_str_simplified(l_d_from_str); + if (!l_from_time) { + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_ERR, "Can't convert \"%s\" to date", l_d_from_str); + return DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_ERR; + } + } + + if (l_d_to_str) { + l_to_time = dap_time_from_str_simplified(l_d_to_str); + if (!l_to_time) { + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_ERR, "Can't convert \"%s\" to date", l_d_to_str); + return DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_ERR; + } + struct tm *l_localtime = localtime((time_t *)&l_to_time); + l_localtime->tm_mday += 1; // + 1 day to end date, got it inclusive + l_to_time = mktime(l_localtime); + } + + // Get chain address + if (l_addr_str) { + l_node_info = DAP_NEW_STACK_SIZE(dap_chain_node_info_t, l_info_size); + memset(l_node_info, 0, l_info_size); + if (dap_chain_node_addr_from_str(&l_node_info->address, l_addr_str)) { + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_NODE_ADDR_ERR, + "Can't parse node address %s", l_addr_str); + return DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_NODE_ADDR_ERR; + } + + } + + dap_chain_hash_fast_t l_hash_public_key = {0}; + + if (!l_net) { + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_NET_ERR, "Could not determine the network from which to " + "extract data for the reward command to work."); + return DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_NET_ERR; + } + + l_chain = dap_chain_net_get_default_chain_by_chain_type(l_net, CHAIN_TYPE_TX); + + if (!l_chain) { + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_CHAIN_PARAM_ERR, + "can't find the required chain"); + return DAP_CHAIN_NODE_CLI_SRV_STAKE_REWARD_CHAIN_PARAM_ERR; + } + json_object* l_json_arr_reply = s_dap_chain_net_srv_stake_reward_all(*a_json_arr_reply, l_node_info, l_chain, + l_net, l_from_time, l_to_time, l_limit, l_offset, l_brief, l_head); + json_object_array_add(*a_json_arr_reply, l_json_arr_reply); + } break; default: { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_UNRECOGNIZE_COM_ERR, "Command %s not recognized", a_argv[l_arg_index]); @@ -3493,6 +3613,187 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, void **a_str_reply) return 0; } +static json_object* s_dap_chain_net_srv_stake_reward_all(json_object* a_json_arr_reply, dap_chain_node_info_t *a_node_info, dap_chain_t *a_chain, + dap_chain_net_t *a_net, dap_time_t a_time_form, dap_time_t a_time_to, + size_t a_limit, size_t a_offset, bool a_brief, bool a_head) +{ + json_object* json_obj_reward = json_object_new_array(); + if (!json_obj_reward){ + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + dap_json_rpc_error_add(a_json_arr_reply, -44, "Memory allocation error"); + return NULL; + } + + const char *l_native_ticker = a_net->pub.native_ticker; + if (!a_chain->callback_datum_iter_create) { + log_it(L_WARNING, "Not defined callback_datum_iter_create for chain \"%s\"", a_chain->name); + dap_json_rpc_error_add(a_json_arr_reply, -1, "Not defined callback_datum_iter_create for chain \"%s\"", a_chain->name); + json_object_put(json_obj_reward); + return NULL; + } + + size_t l_arr_start = 0; + size_t l_arr_end = 0; + dap_chain_set_offset_limit_json(json_obj_reward, &l_arr_start, &l_arr_end, a_limit, a_offset, a_chain->callback_count_tx(a_chain)); + + if (a_node_info){ + json_object* json_obj_addr = json_object_new_object(); + char *l_addr_valid = dap_strdup_printf(NODE_ADDR_FP_STR,NODE_ADDR_FP_ARGS_S(a_node_info->address)); + json_object_object_add(json_obj_addr, "validator addr", json_object_new_string(l_addr_valid)); + DAP_DELETE(l_addr_valid); + json_object_array_add(json_obj_reward, json_obj_addr); + } + + size_t i_tmp = 0; + uint256_t l_value_total = uint256_0; + uint256_t l_value_total_calc = uint256_0; + struct srv_stake *l_srv_stake = s_srv_stake_by_net_id(a_chain->net_id); + dap_chain_net_srv_stake_item_t *l_stake_valid = NULL; + + // load transactions + dap_chain_datum_iter_t *l_datum_iter = a_chain->callback_datum_iter_create(a_chain); + + dap_chain_datum_callback_iters iter_begin; + dap_chain_datum_callback_iters iter_direc; + iter_begin = a_head ? a_chain->callback_datum_iter_get_first + : a_chain->callback_datum_iter_get_last; + iter_direc = a_head ? a_chain->callback_datum_iter_get_next + : a_chain->callback_datum_iter_get_prev; + + for (dap_chain_datum_t *l_datum = iter_begin(l_datum_iter); + l_datum; + l_datum = iter_direc(l_datum_iter)) + { + dap_hash_fast_t l_ttx_hash = {0}; + dap_chain_hash_fast_t l_datum_hash; + dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t *)l_datum->data; + dap_hash_fast(l_tx, l_datum->header.data_size, &l_ttx_hash); + const char *l_tx_token_ticker = NULL; + if (a_limit && i_tmp >= l_arr_end) + break; + if (l_datum->header.type_id != DAP_CHAIN_DATUM_TX) + // go to next datum + continue; + l_tx_token_ticker = l_datum_iter ? l_datum_iter->token_ticker + : dap_ledger_tx_get_token_ticker_by_hash(a_net->pub.ledger, &l_ttx_hash); + //dap_ledger_tx_get_token_ticker_by_hash(l_ledger, &l_datum_hash); + if (!l_tx_token_ticker)//DECLINED transaction + continue; + if (a_time_form && l_datum->header.ts_create < a_time_form) + continue; + if (a_time_to && l_datum->header.ts_create >= a_time_to) + continue; + if (i_tmp >= l_arr_end) + break; + + dap_list_t *l_list_in_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN_REWARD, NULL); + if (!l_list_in_items) // a bad tx + continue; + if (i_tmp < l_arr_start) { + i_tmp++; + continue; + } + // all in items should be from the same address + if (a_node_info) { + dap_hash_fast_t pkey_hash_tx = {}; + int l_item_cnt = 0; + dap_list_t *l_signs_list = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_SIG, &l_item_cnt); + bool l_flag_continue = true; + + if (!l_signs_list) { + log_it(L_WARNING, "Can't get signs from tx %s", dap_chain_hash_fast_to_str_static(&l_ttx_hash)); + continue; + } + while(l_signs_list) { + dap_chain_tx_sig_t *l_vote_sig = (dap_chain_tx_sig_t *)(l_signs_list->data); + dap_sign_get_pkey_hash((dap_sign_t*)l_vote_sig->sig, &pkey_hash_tx); + + char l_pkey_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE]; + dap_chain_hash_fast_to_str(&pkey_hash_tx, l_pkey_hash_str, sizeof(l_pkey_hash_str)); + l_stake_valid = NULL; + HASH_FIND(hh, l_srv_stake->itemlist, &pkey_hash_tx, sizeof(dap_hash_fast_t), l_stake_valid); + if (l_stake_valid && (a_node_info->address.uint64 == l_stake_valid->node_addr.uint64)) { + l_flag_continue = false; + break; + } + l_signs_list = l_signs_list->next; + } + if (l_flag_continue) continue; + dap_list_free(l_signs_list); + } + json_object* json_obj_hash = json_object_new_object(); + json_object_object_add(json_obj_hash, "tx_hash", + json_object_new_string(dap_chain_hash_fast_to_str_static(&l_ttx_hash))); + json_object_array_add(json_obj_reward, json_obj_hash); + json_object* json_arr_sign_out = NULL; + json_object* json_block_hash = NULL; + uint256_t l_value_reward = uint256_0, l_value_out = uint256_0; + l_value_total_calc = uint256_0; + dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT, NULL); + for(dap_list_t *it = l_list_out_items; it; it = it->next) { + dap_chain_tx_out_t *l_tx_out = (dap_chain_tx_out_t *)it->data; + SUM_256_256(l_value_out, l_tx_out->header.value, &l_value_out); + } + dap_list_free(l_list_out_items); + if (!a_brief) { + for(dap_list_t *it = l_list_in_items; it; it = it->next) + { + dap_chain_tx_in_reward_t *l_in_reward = (dap_chain_tx_in_reward_t *) it->data; + dap_chain_block_cache_t *l_block_cache = dap_chain_block_cache_get_by_hash(DAP_CHAIN_CS_BLOCKS(a_chain), &l_in_reward->block_hash); + json_arr_sign_out = json_object_new_array(); + json_block_hash = json_object_new_object(); + json_object_object_add(json_block_hash, "block hash", json_object_new_string(dap_chain_hash_fast_to_str_static(&l_in_reward->block_hash))); + dap_sign_t *l_sign = dap_chain_block_sign_get(l_block_cache->block, l_block_cache->block_size, 0); + size_t l_sign_size = dap_sign_get_size(l_sign); + dap_chain_hash_fast_t l_pkey_hash; + dap_sign_get_pkey_hash(l_sign, &l_pkey_hash); + char l_pkey_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE]; + dap_chain_hash_fast_to_str(&l_pkey_hash, l_pkey_hash_str, sizeof(l_pkey_hash_str)); + json_object* json_obj_sign = json_object_new_object(); + json_object_object_add(json_obj_sign, "pkey_hash",json_object_new_string(l_pkey_hash_str)); + dap_pkey_t * l_block_sign_pkey = dap_pkey_get_from_sign(l_sign); + l_value_reward = a_chain->callback_calc_reward(a_chain, &l_block_cache->block_hash, l_block_sign_pkey); + DAP_DELETE(l_block_sign_pkey); + const char *l_coins_str, + *l_value_str = dap_uint256_to_char(l_value_reward, &l_coins_str); + json_object_object_add(json_obj_sign, "reward value", json_object_new_string(l_value_str)); + json_object_object_add(json_obj_sign, "reward coins", json_object_new_string(l_coins_str)); + if (json_object_object_length(json_obj_sign)) + json_object_array_add(json_arr_sign_out, json_obj_sign); + SUM_256_256(l_value_total_calc, l_value_reward, &l_value_total_calc); + l_value_reward = uint256_0; + json_object_array_add(json_obj_reward, json_block_hash); + json_object_array_add(json_obj_reward, json_arr_sign_out); + } + const char *l_coins_t_out_str, *l_value_t_str; + json_object* json_value_t_out = json_object_new_object(); + l_value_t_str = dap_uint256_to_char(l_value_total_calc, &l_coins_t_out_str); + json_object_object_add(json_value_t_out, "Rewards value (calculated)", json_object_new_string(l_value_t_str)); + json_object_object_add(json_value_t_out, "Rewards coins (calculated)", json_object_new_string(l_coins_t_out_str)); + json_object_array_add(json_obj_reward, json_value_t_out); + } + + const char *l_coins_out_str, *l_value_str; + json_object* json_value_out = json_object_new_object(); + SUM_256_256(l_value_total, l_value_out, &l_value_total); + l_value_str = dap_uint256_to_char(l_value_out, &l_coins_out_str); + json_object_object_add(json_value_out, "Rewards value (tx_out)", json_object_new_string(l_value_str)); + json_object_object_add(json_value_out, "Rewards coins (tx_out)", json_object_new_string(l_coins_out_str)); + json_object_array_add(json_obj_reward, json_value_out); + i_tmp++; + dap_list_free(l_list_in_items); + } + const char *l_coins_out_str, *l_value_str; + json_object* json_value_out = json_object_new_object(); + l_value_str = dap_uint256_to_char(l_value_total, &l_coins_out_str); + json_object_object_add(json_value_out, "Rewards value (total)", json_object_new_string(l_value_str)); + json_object_object_add(json_value_out, "Rewards coins (total)", json_object_new_string(l_coins_out_str)); + json_object_array_add(json_obj_reward, json_value_out); + a_chain->callback_datum_iter_delete(l_datum_iter); + return json_obj_reward; + +} + bool dap_chain_net_srv_stake_get_fee_validators(dap_chain_net_t *a_net, uint256_t *a_max_fee, uint256_t *a_average_fee, uint256_t *a_min_fee, uint256_t *a_median_fee) {