From 2c36fc97be415cfc7ef5bdf843e00b5c34ed67af Mon Sep 17 00:00:00 2001 From: Roman Khlopkov <roman.khlopkov@demlabs.net> Date: Sat, 11 Mar 2023 13:52:52 +0300 Subject: [PATCH] [+] Delegated stake fee for transactions --- .../dap_chain_net_srv_stake_pos_delegate.c | 145 +++++++++++++++--- 1 file changed, 125 insertions(+), 20 deletions(-) diff --git a/modules/service/stake_pos_delegate/dap_chain_net_srv_stake_pos_delegate.c b/modules/service/stake_pos_delegate/dap_chain_net_srv_stake_pos_delegate.c index 6ae002a332..ca0a8eb1e8 100644 --- a/modules/service/stake_pos_delegate/dap_chain_net_srv_stake_pos_delegate.c +++ b/modules/service/stake_pos_delegate/dap_chain_net_srv_stake_pos_delegate.c @@ -29,6 +29,7 @@ #include "dap_enc_base58.h" #include "dap_chain_common.h" #include "dap_chain_mempool.h" +#include "dap_chain_net_tx.h" #include "dap_chain_net_srv.h" #include "dap_chain_cs_block_poa.h" #include "dap_chain_cs_dag_poa.h" @@ -197,21 +198,35 @@ dap_list_t *dap_chain_net_srv_stake_get_validators() // Freeze staker's funds when delegating a key static dap_chain_datum_tx_t *s_stake_tx_create(dap_chain_net_t * a_net, dap_chain_wallet_t *a_wallet, - uint256_t a_value, dap_chain_addr_t *a_signing_addr, - dap_chain_node_addr_t *a_node_addr) + uint256_t a_value, uint256_t a_fee, + dap_chain_addr_t *a_signing_addr, dap_chain_node_addr_t *a_node_addr) { if (!a_net || !a_wallet || IS_ZERO_256(a_value) || !a_signing_addr || !a_node_addr) return NULL; + const char *l_native_ticker = a_net->pub.native_ticker; char l_delegated_ticker[DAP_CHAIN_TICKER_SIZE_MAX]; - dap_chain_datum_token_get_delegated_ticker(l_delegated_ticker, a_net->pub.native_ticker); + dap_chain_datum_token_get_delegated_ticker(l_delegated_ticker, l_native_ticker); dap_ledger_t *l_ledger = dap_chain_ledger_by_net_name(a_net->pub.name); - uint256_t l_value_sell = {}; // how many coins to transfer + uint256_t l_value_transfer = {}, l_fee_transfer = {}; // how many coins to transfer // list of transaction with 'out' items to sell dap_chain_addr_t *l_owner_addr = (dap_chain_addr_t *)dap_chain_wallet_get_addr(a_wallet, a_net->pub.id); - dap_list_t *l_list_used_out = dap_chain_ledger_get_list_tx_outs_with_val(l_ledger, l_delegated_ticker, l_owner_addr, a_value, &l_value_sell); + uint256_t l_net_fee, l_fee_total = a_fee; + dap_chain_addr_t l_net_fee_addr; + bool l_net_fee_used = dap_chain_net_tx_get_fee(a_net->pub.id, NULL, &l_net_fee, &l_net_fee_addr); + if (l_net_fee_used) + SUM_256_256(l_fee_total, l_net_fee, &l_fee_total); + dap_list_t *l_list_used_out = dap_chain_ledger_get_list_tx_outs_with_val(l_ledger, l_delegated_ticker, + l_owner_addr, a_value, &l_value_transfer); if (!l_list_used_out) { - log_it(L_WARNING, "Nothing to delegate (not enough funds)"); + log_it(L_WARNING, "Nothing to pay for delegate (not enough funds)"); + DAP_DELETE(l_owner_addr); + return NULL; + } + dap_list_t *l_list_fee_out = dap_chain_ledger_get_list_tx_outs_with_val(l_ledger, l_native_ticker, + l_owner_addr, l_fee_total, &l_fee_transfer); + if (!l_list_fee_out) { + log_it(L_WARNING, "Nothing to pay for fee (not enough funds)"); DAP_DELETE(l_owner_addr); return NULL; } @@ -219,15 +234,22 @@ static dap_chain_datum_tx_t *s_stake_tx_create(dap_chain_net_t * a_net, dap_chai // create empty transaction dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create(); - // add 'in' items to sell + // add 'in' items to pay for delegate uint256_t l_value_to_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out); dap_list_free_full(l_list_used_out, NULL); - if (!EQUAL_256(l_value_to_items, l_value_sell)) { + if (!EQUAL_256(l_value_to_items, l_value_transfer)) { + log_it(L_ERROR, "Can't compose the transaction input"); + goto tx_fail; + } + // add 'in' items to delegate + uint256_t l_value_fee_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_fee_out); + dap_list_free_full(l_list_fee_out, NULL); + if (!EQUAL_256(l_value_fee_items, l_fee_transfer)) { log_it(L_ERROR, "Can't compose the transaction input"); goto tx_fail; } - // add 'out_cond' & 'out' items + // add 'out_cond' & 'out_ext' items dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_STAKE_POS_DELEGATE_ID }; dap_chain_tx_out_cond_t *l_tx_out = dap_chain_datum_tx_item_out_cond_create_srv_stake(l_uid, a_value, a_signing_addr, a_node_addr); if (!l_tx_out) { @@ -238,14 +260,37 @@ static dap_chain_datum_tx_t *s_stake_tx_create(dap_chain_net_t * a_net, dap_chai DAP_DELETE(l_tx_out); // coin back uint256_t l_value_back = {}; - SUBTRACT_256_256(l_value_sell, a_value, &l_value_back); + SUBTRACT_256_256(l_value_transfer, a_value, &l_value_back); if (!IS_ZERO_256(l_value_back)) { - if (dap_chain_datum_tx_add_out_item(&l_tx, l_owner_addr, l_value_back) != 1) { + if (dap_chain_datum_tx_add_out_ext_item(&l_tx, l_owner_addr, l_value_back, l_delegated_ticker) != 1) { log_it(L_ERROR, "Cant add coin back output"); goto tx_fail; } } + // add fee items + if (l_net_fee_used) { + if (dap_chain_datum_tx_add_out_ext_item(&l_tx, &l_net_fee_addr, l_net_fee, l_native_ticker) != 1) { + dap_chain_datum_tx_delete(l_tx); + return NULL; + } + } + if (!IS_ZERO_256(a_fee)) { + if (dap_chain_datum_tx_add_fee_item(&l_tx, a_fee) != 1) { + dap_chain_datum_tx_delete(l_tx); + return NULL; + } + } + uint256_t l_fee_back = {}; + // fee coin back + SUBTRACT_256_256(l_fee_transfer, l_fee_total, &l_fee_back); + if(!IS_ZERO_256(l_fee_back)) { + if(dap_chain_datum_tx_add_out_ext_item(&l_tx, l_owner_addr, l_fee_back, l_native_ticker) != 1) { + dap_chain_datum_tx_delete(l_tx); + return NULL; + } + } + // add 'sign' item if(dap_chain_datum_tx_add_sign_item(&l_tx, dap_chain_wallet_get_key(a_wallet, 0)) != 1) { log_it(L_ERROR, "Can't add sign output"); @@ -253,6 +298,7 @@ static dap_chain_datum_tx_t *s_stake_tx_create(dap_chain_net_t * a_net, dap_chai } DAP_DELETE(l_owner_addr); return l_tx; + tx_fail: dap_chain_datum_tx_delete(l_tx); DAP_DELETE(l_owner_addr); @@ -344,7 +390,7 @@ static char *s_stake_decree_put(dap_chain_datum_decree_t *a_decree, dap_chain_ne return l_ret; } -static dap_chain_datum_tx_t *s_stake_tx_invalidate(dap_chain_net_t *a_net, dap_hash_fast_t *a_tx_hash, dap_enc_key_t *a_key) +static dap_chain_datum_tx_t *s_stake_tx_invalidate(dap_chain_net_t *a_net, dap_hash_fast_t *a_tx_hash, uint256_t a_fee, dap_enc_key_t *a_key) { dap_ledger_t *l_ledger = dap_chain_ledger_by_net_name(a_net->pub.name); @@ -377,6 +423,21 @@ static dap_chain_datum_tx_t *s_stake_tx_invalidate(dap_chain_net_t *a_net, dap_h log_it(L_WARNING, "Try to invalidate delegating tx with not a owner wallet"); return NULL; } + const char *l_native_ticker = a_net->pub.native_ticker; + const char *l_delegated_ticker = dap_chain_ledger_tx_get_token_ticker_by_hash(l_ledger, a_tx_hash); + uint256_t l_fee_transfer = {}; // how many coins to transfer + // list of transaction with 'out' items to sell + uint256_t l_net_fee, l_fee_total = a_fee; + dap_chain_addr_t l_net_fee_addr; + bool l_net_fee_used = dap_chain_net_tx_get_fee(a_net->pub.id, NULL, &l_net_fee, &l_net_fee_addr); + if (l_net_fee_used) + SUM_256_256(l_fee_total, l_net_fee, &l_fee_total); + dap_list_t *l_list_fee_out = dap_chain_ledger_get_list_tx_outs_with_val(l_ledger, l_native_ticker, + &l_owner_addr, l_fee_total, &l_fee_transfer); + if (!l_list_fee_out) { + log_it(L_WARNING, "Nothing to pay for fee (not enough funds)"); + return NULL; + } // create empty transaction dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create(); @@ -384,13 +445,34 @@ static dap_chain_datum_tx_t *s_stake_tx_invalidate(dap_chain_net_t *a_net, dap_h // add 'in' item to buy from conditional transaction dap_chain_datum_tx_add_in_cond_item(&l_tx, a_tx_hash, l_prev_cond_idx, 0); - // add 'out' item - if (dap_chain_datum_tx_add_out_item(&l_tx, &l_owner_addr, l_tx_out_cond->header.value) == -1) { + // add 'out_ext' item + if (dap_chain_datum_tx_add_out_ext_item(&l_tx, &l_owner_addr, l_tx_out_cond->header.value, l_delegated_ticker) == -1) { dap_chain_datum_tx_delete(l_tx); log_it(L_ERROR, "Cant add returning coins output"); return NULL; } - + // add fee items + if (l_net_fee_used) { + if (dap_chain_datum_tx_add_out_ext_item(&l_tx, &l_net_fee_addr, l_net_fee, l_native_ticker) != 1) { + dap_chain_datum_tx_delete(l_tx); + return NULL; + } + } + if (!IS_ZERO_256(a_fee)) { + if (dap_chain_datum_tx_add_fee_item(&l_tx, a_fee) != 1) { + dap_chain_datum_tx_delete(l_tx); + return NULL; + } + } + // fee coin back + uint256_t l_fee_back = {}; + SUBTRACT_256_256(l_fee_transfer, l_fee_total, &l_fee_back); + if(!IS_ZERO_256(l_fee_back)) { + if(dap_chain_datum_tx_add_out_ext_item(&l_tx, &l_owner_addr, l_fee_back, l_native_ticker) != 1) { + dap_chain_datum_tx_delete(l_tx); + return NULL; + } + } // add 'sign' items if(dap_chain_datum_tx_add_sign_item(&l_tx, a_key) != 1) { dap_chain_datum_tx_delete(l_tx); @@ -751,12 +833,11 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, char **a_str_reply) case CMD_ORDER: return s_cli_srv_stake_order(a_argc, a_argv, l_arg_index + 1, a_str_reply, l_hash_out_type); case CMD_DELEGATE: { - // "srv_stake delegate -cert <pub_cert_name> -net <net_name> -wallet <wallet_name> -value <addr> [-node_addr <node_addr>]\n" - // "\tDelegate tokens with specified order within specified net name. Specify fee address.\n" const char *l_net_str = NULL, *l_wallet_str = NULL, *l_cert_str = NULL, *l_value_str = NULL, + *l_fee_str = NULL, *l_node_addr_str = NULL; l_arg_index++; dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-net", &l_net_str); @@ -828,9 +909,19 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, char **a_str_reply) } } else l_node_addr.uint64 = dap_chain_net_get_cur_addr_int(l_net); + dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-fee", &l_fee_str); + if (!l_fee_str) { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Command 'delegate' required parameter -fee"); + return -15; + } + uint256_t l_fee = dap_chain_balance_scan(l_fee_str); + if (IS_ZERO_256(l_fee)) { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Unrecognized number in '-fee' param"); + return -16; + } // Create conditional transaction - dap_chain_datum_tx_t *l_tx = s_stake_tx_create(l_net, l_wallet, l_value, &l_signing_addr, &l_node_addr); + dap_chain_datum_tx_t *l_tx = s_stake_tx_create(l_net, l_wallet, l_value, l_fee, &l_signing_addr, &l_node_addr); dap_chain_wallet_close(l_wallet); if (!l_tx || !s_stake_tx_put(l_tx, l_net)) { dap_cli_server_cmd_set_reply_text(a_str_reply, "Stake transaction error"); @@ -939,6 +1030,7 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, char **a_str_reply) case CMD_INVALIDATE: { const char *l_net_str = NULL, *l_wallet_str = NULL, + *l_fee_str = NULL, *l_tx_hash_str = NULL, *l_cert_str = NULL, *l_poa_cert_str = NULL, @@ -955,6 +1047,7 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, char **a_str_reply) dap_cli_server_cmd_set_reply_text(a_str_reply, "Network %s not found", l_net_str); return -4; } + uint256_t l_fee; dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-wallet", &l_wallet_str); if (!l_wallet_str) { dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-poa_cert", &l_poa_cert_str); @@ -962,6 +1055,17 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, char **a_str_reply) dap_cli_server_cmd_set_reply_text(a_str_reply, "Command 'invalidate' required parameter -wallet or -poa_cert"); return -17; } + } else { + dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-fee", &l_fee_str); + if (!l_fee_str) { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Command 'delegate' required parameter -fee"); + return -5; + } + l_fee = dap_chain_balance_scan(l_fee_str); + if (IS_ZERO_256(l_fee)) { + dap_cli_server_cmd_set_reply_text(a_str_reply, "Unrecognized number in '-fee' param"); + return -6; + } } dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-tx", &l_tx_hash_str); if (!l_tx_hash_str) { @@ -1028,7 +1132,7 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, char **a_str_reply) dap_cli_server_cmd_set_reply_text(a_str_reply, "Specified wallet not found"); return -18; } - dap_chain_datum_tx_t *l_tx = s_stake_tx_invalidate(l_net, l_final_tx_hash, dap_chain_wallet_get_key(l_wallet, 0)); + dap_chain_datum_tx_t *l_tx = s_stake_tx_invalidate(l_net, l_final_tx_hash, l_fee, dap_chain_wallet_get_key(l_wallet, 0)); dap_chain_wallet_close(l_wallet); if (l_tx && s_stake_tx_put(l_tx, l_net)) { dap_cli_server_cmd_set_reply_text(a_str_reply, "All m-tokens successfully returned to owner"); @@ -1052,7 +1156,8 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, char **a_str_reply) } dap_chain_datum_decree_t *l_decree = s_stake_decree_invalidate(l_net, l_final_tx_hash, l_poa_cert->enc_key); if (l_decree && s_stake_decree_put(l_decree, l_net)) { - dap_cli_server_cmd_set_reply_text(a_str_reply, "Specified delageted key invalidated. Try to execute this command with -wallet to return m-tokens to owner"); + dap_cli_server_cmd_set_reply_text(a_str_reply, "Specified delageted key invalidated. " + "Try to execute this command with -wallet to return m-tokens to owner"); DAP_DELETE(l_decree); } else { char *l_final_tx_hash_str = dap_chain_hash_fast_to_str_new(l_final_tx_hash); -- GitLab