diff --git a/dap-sdk b/dap-sdk index 0b186bf875b9eac4d0df092f6bb849f73994251b..d8d47d8372fe8e7db7fb1dee542d6cc307a1a88b 160000 --- a/dap-sdk +++ b/dap-sdk @@ -1 +1 @@ -Subproject commit 0b186bf875b9eac4d0df092f6bb849f73994251b +Subproject commit d8d47d8372fe8e7db7fb1dee542d6cc307a1a88b diff --git a/modules/datum/include/dap_chain_datum_token.h b/modules/datum/include/dap_chain_datum_token.h index 79e58edb19978f3c97b20ff772b7c26187d92652..4553659447d45d60ed751ea29b77e5cf574ca50c 100644 --- a/modules/datum/include/dap_chain_datum_token.h +++ b/modules/datum/include/dap_chain_datum_token.h @@ -413,6 +413,7 @@ typedef struct dap_chain_datum_token_emission { #define DAP_CHAIN_DATUM_TOKEN_EMISSION_SOURCE_SUBTYPE_BRIDGE_COMMISSION_OLD "COMISSION" #define DAP_CHAIN_DATUM_TOKEN_EMISSION_SOURCE_SUBTYPE_BRIDGE_COMMISSION "COMMISSION" #define DAP_CHAIN_DATUM_TOKEN_EMISSION_SOURCE_SUBTYPE_BRIDGE_CROSSCHAIN "CROSSCHAIN" +#define DAP_CHAIN_DATUM_TOKEN_EMISSION_SOURCE_SUBTYPE_BRIDGE_OUT "OUT" DAP_STATIC_INLINE const char *dap_chain_datum_emission_type_str(uint8_t a_type) { diff --git a/modules/net/dap_chain_net.c b/modules/net/dap_chain_net.c index 9610d60b58ef9f8caf74a63397846bb3b6cc88a7..6e69d4d70d94118643432733a456f8be7e74f125 100644 --- a/modules/net/dap_chain_net.c +++ b/modules/net/dap_chain_net.c @@ -203,6 +203,7 @@ static void s_link_manager_callback_error(dap_link_t *a_link, uint64_t a_net_id, static bool s_link_manager_callback_disconnected(dap_link_t *a_link, uint64_t a_net_id, int a_links_count); static int s_link_manager_fill_net_info(dap_link_t *a_link); static int s_link_manager_link_request(uint64_t a_net_id); +static int s_link_manager_link_count_changed(); static const dap_link_manager_callbacks_t s_link_manager_callbacks = { .connected = s_link_manager_callback_connected, @@ -210,6 +211,7 @@ static const dap_link_manager_callbacks_t s_link_manager_callbacks = { .error = s_link_manager_callback_error, .fill_net_info = s_link_manager_fill_net_info, .link_request = s_link_manager_link_request, + .link_count_changed = s_link_manager_link_count_changed, }; // State machine switchs here @@ -405,11 +407,6 @@ static void s_link_manager_callback_connected(dap_link_t *a_link, uint64_t a_net NODE_ADDR_FP_ARGS_S(a_link->addr)); struct json_object *l_json = dap_chain_net_states_json_collect(l_net); - char l_err_str[128] = { }; - snprintf(l_err_str, sizeof(l_err_str) - , "Established connection with link " NODE_ADDR_FP_STR - , NODE_ADDR_FP_ARGS_S(a_link->addr)); - json_object_object_add(l_json, "errorMessage", json_object_new_string(l_err_str)); dap_notify_server_send(json_object_get_string(l_json)); json_object_put(l_json); if(l_net_pvt->state == NET_STATE_LINKS_CONNECTING ){ @@ -512,6 +509,16 @@ int s_link_manager_link_request(uint64_t a_net_id) return dap_worker_exec_callback_on(dap_worker_get_auto(), dap_chain_net_balancer_request, l_arg), 0; } + +static int s_link_manager_link_count_changed() +{ + struct json_object *l_json = dap_chain_nets_info_json_collect(); + json_object_object_add(l_json, "errorMessage", json_object_new_string(" ")); // regular notify has no error + dap_notify_server_send(json_object_get_string(l_json)); + json_object_put(l_json); + return 0; +} + struct request_link_info *s_get_permanent_link_info(dap_chain_net_t *a_net, dap_chain_node_addr_t *a_address) { dap_chain_net_pvt_t *l_net_pvt = PVT(a_net); @@ -670,6 +677,18 @@ static void s_net_states_notify(dap_chain_net_t *a_net) json_object_put(l_json); } +static bool s_net_states_notify_timer_callback(UNUSED_ARG void *a_arg) +{ + for (dap_chain_net_t *net = s_nets_by_name; net; net = net->hh.next) { + struct json_object *l_json = dap_chain_net_states_json_collect(net); + json_object_object_add(l_json, "errorMessage", json_object_new_string(" ")); // regular notify has no error + dap_notify_server_send(json_object_get_string(l_json)); + json_object_put(l_json); + } + + return true; +} + /** * @brief dap_chain_net_get_role * @param a_net @@ -775,6 +794,7 @@ bool s_net_disk_load_notify_callback(UNUSED_ARG void *a_arg) json_object_object_add(json_obj, "nets", l_jobj_nets); dap_notify_server_send(json_object_get_string(json_obj)); json_object_put(json_obj); + s_net_states_notify_timer_callback(NULL); return true; } diff --git a/modules/net/dap_chain_net_balancer.c b/modules/net/dap_chain_net_balancer.c index 92a335681b9712fe7a3e7a13181e7c0db8c6430d..34a5f5985c59299886309a90c6f7af1f34bbceea 100644 --- a/modules/net/dap_chain_net_balancer.c +++ b/modules/net/dap_chain_net_balancer.c @@ -155,10 +155,6 @@ static void s_balancer_link_prepare_success(dap_chain_net_t* a_net, dap_net_link if (dap_chain_net_link_add(a_net, &l_link_info->node_addr, l_link_info->uplink_addr, l_link_info->uplink_port)) continue; l_json = s_balancer_states_json_collect(a_net, a_host_addr, a_host_port); - snprintf(l_err_str, sizeof(l_err_str) - , "Link " NODE_ADDR_FP_STR " prepared" - , NODE_ADDR_FP_ARGS_S(l_link_info->node_addr)); - json_object_object_add(l_json, "errorMessage", json_object_new_string(l_err_str)); dap_notify_server_send(json_object_get_string(l_json)); json_object_put(l_json); } diff --git a/modules/node-cli/dap_chain_node_cli_cmd.c b/modules/node-cli/dap_chain_node_cli_cmd.c index a70606456a0b5e900232970a831b66fe21a500a0..aba036a2148a7f8aa7d3f1d78044e00b7128db5e 100644 --- a/modules/node-cli/dap_chain_node_cli_cmd.c +++ b/modules/node-cli/dap_chain_node_cli_cmd.c @@ -83,10 +83,14 @@ #include "dap_json_rpc.h" #include "dap_json_rpc_request.h" #include "dap_client_pvt.h" +#include "dap_notify_srv.h" +#include "dap_chain_wallet_cache.h" #define LOG_TAG "chain_node_cli_cmd" int _cmd_mempool_add_ca(dap_chain_net_t *a_net, dap_chain_t *a_chain, dap_cert_t *a_cert, void **a_str_reply); +static void s_new_wallet_info_notify(const char *a_wallet_name); +struct json_object *wallet_list_json_collect(); dap_chain_t *s_get_chain_with_datum(dap_chain_net_t *a_net, const char *a_datum_hash) { dap_chain_t *l_chain = NULL; @@ -1713,7 +1717,19 @@ int l_arg_index = 1, l_rc, cmd_num = CMD_NONE; return DAP_CHAIN_NODE_CLI_COM_TX_WALLET_NET_PARAM_ERR; } l_wallet = dap_chain_wallet_open(l_wallet_name, c_wallets_path, NULL); + if (!l_wallet){ + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_TX_WALLET_NET_PARAM_ERR, + "Can't find wallet (%s)", l_wallet_name); + json_object_put(json_arr_out); + return DAP_CHAIN_NODE_CLI_COM_TX_WALLET_NET_PARAM_ERR; + } l_addr = (dap_chain_addr_t *) dap_chain_wallet_get_addr(l_wallet, l_net->pub.id ); + if (!l_addr){ + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_TX_WALLET_NET_PARAM_ERR, + "Can't get addr from wallet (%s)", l_wallet_name); + json_object_put(json_arr_out); + return DAP_CHAIN_NODE_CLI_COM_TX_WALLET_NET_PARAM_ERR; + } } else { l_addr = dap_chain_addr_from_str(l_addr_str); } @@ -1737,26 +1753,35 @@ int l_arg_index = 1, l_rc, cmd_num = CMD_NONE; uint256_t l_value_datoshi = dap_chain_balance_scan(l_value_str); l_outs_list = dap_chain_wallet_get_list_tx_outs_with_val(l_net->pub.ledger, l_token_tiker, l_addr, l_value_datoshi, &l_value_sum); } else { - l_outs_list = dap_ledger_get_list_tx_outs(l_net->pub.ledger, l_token_tiker, l_addr, &l_value_sum); + 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, "total_value", json_object_new_string(dap_uint256_to_char(l_value_sum, NULL))); + 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_datoshi_print(l_value_sum); + const char *l_out_total_value_coins_str = dap_chain_balance_coins_print(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){ 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_uint256_to_char(l_item->value, NULL); + const char *l_out_value_str = dap_chain_balance_datoshi_print(l_item->value); + const char *l_out_value_coins_str = dap_chain_balance_coins_print(l_item->value); json_object_object_add(json_obj_item,"item_type", json_object_new_string("unspent_out")); - json_object_object_add(json_obj_item,"value", json_object_new_string(l_out_value_str)); + 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_array_add(l_json_outs_arr, json_obj_item); + DAP_DEL_Z(l_out_value_str); + DAP_DEL_Z(l_out_value_coins_str); } dap_list_free_full(l_outs_list, NULL); json_object_object_add(json_obj_wall, "outs", l_json_outs_arr); json_object_array_add(json_arr_out, json_obj_wall); - - } break; default: { if( !l_wallet_name ) { @@ -1795,6 +1820,11 @@ int l_arg_index = 1, l_rc, cmd_num = CMD_NONE; json_object_object_add(json_obj_wall, "Wallet name", json_object_new_string(l_wallet_name)); json_object_object_add(json_obj_wall, "protection", cmd_num == CMD_WALLET_ACTIVATE ? json_object_new_string("is activated") : json_object_new_string("is deactivated")); + // Notify about wallet + s_new_wallet_info_notify(l_wallet_name); + struct json_object *l_json_wallets = wallet_list_json_collect(); + dap_notify_server_send(json_object_get_string(l_json_wallets)); + json_object_put(l_json_wallets); break; case -EBUSY: dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_TX_WALLET_ALREADY_ERR, @@ -2008,6 +2038,11 @@ int l_arg_index = 1, l_rc, cmd_num = CMD_NONE; DAP_DEL_Z(l_addr); json_object_array_add(json_arr_out, json_obj_wall); dap_chain_wallet_close(l_wallet); + // Notify about wallet + s_new_wallet_info_notify(l_wallet_name); + struct json_object *l_json_wallets = wallet_list_json_collect(); + dap_notify_server_send(json_object_get_string(l_json_wallets)); + json_object_put(l_json_wallets); break; } } @@ -5458,6 +5493,17 @@ void dap_notify_new_client_send_info(dap_events_socket_t *a_es, UNUSED_ARG void } } +static void s_new_wallet_info_notify(const char *a_wallet_name) +{ + struct json_object *l_json = json_object_new_object(); + json_object_object_add(l_json, "class", json_object_new_string("WalletInfo")); + struct json_object *l_json_wallet_info = json_object_new_object(); + json_object_object_add(l_json_wallet_info, a_wallet_name, dap_chain_wallet_info_to_json(a_wallet_name, dap_chain_wallet_get_path(g_config))); + json_object_object_add(l_json, "wallet", l_json_wallet_info); + dap_notify_server_send(json_object_get_string(l_json)); + json_object_put(l_json); +} + static void s_stage_connected_callback(dap_client_t* a_client, void * a_arg) { dap_chain_node_client_t *l_node_client = DAP_CHAIN_NODE_CLIENT(a_client); UNUSED(a_arg); diff --git a/modules/node-cli/dap_chain_node_cli_cmd_tx.c b/modules/node-cli/dap_chain_node_cli_cmd_tx.c index 486a2182e81cad2cad726c59d3b9b7d01f824853..32abe86479941ee114c04513e27af28d7c24aa58 100644 --- a/modules/node-cli/dap_chain_node_cli_cmd_tx.c +++ b/modules/node-cli/dap_chain_node_cli_cmd_tx.c @@ -1494,10 +1494,43 @@ int s_json_rpc_tx_parse_json(dap_chain_net_t *a_net, dap_chain_t *a_chain, json_ size_t l_items_ready = 0; dap_list_t *l_sign_list = NULL;// list 'sing' items dap_list_t *l_in_list = NULL;// list 'in' items - dap_list_t *l_tsd_list = NULL;// list tsd sections uint256_t l_value_need = { };// how many tokens are needed in the 'out' item uint256_t l_value_need_fee = {}; json_object *l_jobj_errors = json_object_new_array(); + bool l_multichanel = false; + + // First iteration in input file. Check the tx will be multichannel or not + for(size_t i = 0; i < l_items_count; ++i) { + struct json_object *l_json_item_obj = json_object_array_get_idx(a_items, i); + if(!l_json_item_obj || !json_object_is_type(l_json_item_obj, json_type_object)) { + continue; + } + struct json_object *l_json_item_type = json_object_object_get(l_json_item_obj, "type"); + if(!l_json_item_type && json_object_is_type(l_json_item_type, json_type_string)) { + log_it(L_WARNING, "Item %zu without type", i); + continue; + } + const char *l_item_type_str = json_object_get_string(l_json_item_type); + dap_chain_tx_item_type_t l_item_type = dap_chain_datum_tx_item_str_to_type(l_item_type_str); + if(l_item_type == TX_ITEM_TYPE_UNKNOWN) { + log_it(L_WARNING, "Item %zu has invalid type '%s'", i, l_item_type_str); + continue; + } + + switch (l_item_type) { + case TX_ITEM_TYPE_IN: { + const char *l_json_item_token = s_json_get_text(l_json_item_obj, "token"); + if (dap_strcmp(l_json_item_token, l_native_token)){ + l_multichanel = true; + l_main_token = l_json_item_token; + } + }break; + default: continue; + } + if(l_multichanel) + break; + } + // Creating and adding items to the transaction for(size_t i = 0; i < l_items_count; ++i) { struct json_object *l_json_item_obj = json_object_array_get_idx(a_items, i); @@ -1559,44 +1592,59 @@ int s_json_rpc_tx_parse_json(dap_chain_net_t *a_net, dap_chain_t *a_chain, json_ uint256_t l_value = { }; const char *l_json_item_addr_str = s_json_get_text(l_json_item_obj, "addr"); bool l_is_value = s_json_get_uint256(l_json_item_obj, "value", &l_value); + const char *l_token = s_json_get_text(l_json_item_obj, "token"); if(l_is_value && l_json_item_addr_str) { dap_chain_addr_t *l_addr = dap_chain_addr_from_str(l_json_item_addr_str); if(l_addr && !IS_ZERO_256(l_value)) { if(l_item_type == TX_ITEM_TYPE_OUT) { // Create OUT item - dap_chain_tx_out_t *l_out_item = dap_chain_datum_tx_item_out_create(l_addr, l_value); + const uint8_t *l_out_item = NULL; + if(l_multichanel) + l_out_item = (const uint8_t *)dap_chain_datum_tx_item_out_ext_create(l_addr, l_value, l_token ? l_token : (l_main_token ? l_main_token : l_native_token)); + else + l_out_item = (const uint8_t *)dap_chain_datum_tx_item_out_create(l_addr, l_value); if (!l_out_item) { json_object *l_jobj_err = json_object_new_string("Failed to create transaction out. " "There may not be enough funds in the wallet."); json_object_array_add(l_jobj_errors, l_jobj_err); } l_item = (const uint8_t*) l_out_item; - } - else if(l_item_type == TX_ITEM_TYPE_OUT_EXT) { + if (l_item){ + if (l_multichanel && !dap_strcmp(((dap_chain_tx_out_ext_t*)l_out_item)->token, l_native_token)) + SUM_256_256(l_value_need_fee, l_value, &l_value_need_fee); + else + SUM_256_256(l_value_need, l_value, &l_value_need); + } + + } else if(l_item_type == TX_ITEM_TYPE_OUT_EXT) { // Read address and value - const char *l_token = s_json_get_text(l_json_item_obj, "token"); - l_main_token = l_token; if(l_token) { // Create OUT_EXT item - dap_chain_tx_out_ext_t *l_out_ext_item = dap_chain_datum_tx_item_out_ext_create(l_addr, l_value, l_token); - if (!l_out_ext_item) { + const uint8_t *l_out_item = NULL; + if(l_multichanel) + l_out_item = (const uint8_t *)dap_chain_datum_tx_item_out_ext_create(l_addr, l_value, l_token); + else + l_out_item = (const uint8_t *)dap_chain_datum_tx_item_out_create(l_addr, l_value); + if (!l_out_item) { json_object *l_jobj_err = json_object_new_string("Failed to create a out ext" "for a transaction. There may not be enough funds " "on the wallet or the wrong ticker token " "is indicated."); json_object_array_add(l_jobj_errors, l_jobj_err); } - l_item = (const uint8_t*) l_out_ext_item; + l_item = (const uint8_t*) l_out_item; + if (l_item){ + if (!dap_strcmp(l_token, l_native_token)) + SUM_256_256(l_value_need_fee, l_value, &l_value_need_fee); + else + SUM_256_256(l_value_need, l_value, &l_value_need); + } } else { log_it(L_WARNING, "Invalid 'out_ext' item %zu", i); continue; } } - // Save value for using in In item - if(l_item) { - SUM_256_256(l_value_need, l_value, &l_value_need); - } } else { if(l_item_type == TX_ITEM_TYPE_OUT) { log_it(L_WARNING, "Invalid 'out' item %zu", i); @@ -2036,7 +2084,10 @@ int s_json_rpc_tx_parse_json(dap_chain_net_t *a_net, dap_chain_t *a_chain, json_ uint256_t l_value_back; SUBTRACT_256_256(l_value_got, l_value_need, &l_value_back); if(!IS_ZERO_256(l_value_back)) { - dap_chain_datum_tx_add_out_item(&l_tx, l_addr_from, l_value_back); + if (l_multichanel) + dap_chain_datum_tx_add_out_ext_item(&l_tx, l_addr_from, l_value_back, l_main_token); + else + dap_chain_datum_tx_add_out_item(&l_tx, l_addr_from, l_value_back); } } } diff --git a/modules/service/bridge/dap_chain_net_srv_bridge.c b/modules/service/bridge/dap_chain_net_srv_bridge.c index 6dbd2019c19ce932ce0de830ed17f04aacf6a76d..75584bff2d877fd745d19c4b59bb4acf4e37b1b7 100644 --- a/modules/service/bridge/dap_chain_net_srv_bridge.c +++ b/modules/service/bridge/dap_chain_net_srv_bridge.c @@ -135,14 +135,38 @@ static bool s_tag_check_bridge(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_t //crosschain bridge AUTH emissions - if (!a_items_grp->items_in_ems) + if (a_items_grp->items_in_ems){ + dap_chain_tx_in_ems_t *l_tx_in_ems = a_items_grp->items_in_ems->data; + dap_hash_fast_t ems_hash = l_tx_in_ems->header.token_emission_hash; + dap_chain_datum_token_emission_t *l_emission = dap_ledger_token_emission_find(a_ledger, &ems_hash); + if(l_emission) + return s_get_ems_bridge_action(l_emission, a_action); + } + + if (!a_items_grp->items_tsd) return false; - dap_chain_tx_in_ems_t *l_tx_in_ems = a_items_grp->items_in_ems->data; - dap_hash_fast_t ems_hash = l_tx_in_ems->header.token_emission_hash; - dap_chain_datum_token_emission_t *l_emission = dap_ledger_token_emission_find(a_ledger, &ems_hash); - if(l_emission) - return s_get_ems_bridge_action(l_emission, a_action); + bool src_bridge = false; + bool subtype_out = false; + for (dap_list_t *it = a_items_grp->items_tsd; it; it = it->next) { + dap_chain_tx_tsd_t *l_tx_tsd = it->data; + int l_type; + size_t l_size; + byte_t *l_data = dap_chain_datum_tx_item_get_data(l_tx_tsd, &l_type, &l_size); + + + if (l_type == DAP_CHAIN_DATUM_EMISSION_TSD_TYPE_SOURCE && s_tsd_str_cmp(l_data, l_size, DAP_CHAIN_DATUM_TOKEN_EMISSION_SOURCE_BRIDGE) == 0) + src_bridge = true; + + if (l_type == DAP_CHAIN_DATUM_EMISSION_TSD_TYPE_SOURCE_SUBTYPE && s_tsd_str_cmp(l_data, l_size, DAP_CHAIN_DATUM_TOKEN_EMISSION_SOURCE_SUBTYPE_BRIDGE_OUT) == 0) + subtype_out = true; + } + if (src_bridge && subtype_out) + { + if(a_action) *a_action = DAP_CHAIN_TX_TAG_ACTION_CLOSE; + return true; + } + return false; } diff --git a/modules/wallet/dap_chain_wallet.c b/modules/wallet/dap_chain_wallet.c index 238d39171587bb37856df777658faae9d6afaf8c..38edb47b0df03119b6da031e90c725e16cd43688 100644 --- a/modules/wallet/dap_chain_wallet.c +++ b/modules/wallet/dap_chain_wallet.c @@ -1265,12 +1265,12 @@ json_object *dap_chain_wallet_info_to_json(const char *a_name, const char *a_pat json_object_object_add(l_balance_data, "description", l_description ? json_object_new_string(l_description) : json_object_new_null()); - json_object_object_add(l_balance_data, "coin", json_object_new_string(l_balance_coins)); + json_object_object_add(l_balance_data, "coins", json_object_new_string(l_balance_coins)); json_object_object_add(l_balance_data, "datoshi", json_object_new_string(l_balance_datoshi)); json_object_array_add(l_arr_balance, l_balance_data); DAP_DELETE(l_addr_tokens[i]); } - json_object_object_add(l_jobj_net, "balance", l_arr_balance); + json_object_object_add(l_jobj_net, "tokens", l_arr_balance); DAP_DEL_MULTY(l_addr_tokens, l_wallet_addr_in_net); } json_object_object_add(l_json_ret, "networks", l_jobj_network); diff --git a/modules/wallet/dap_chain_wallet_cache.c b/modules/wallet/dap_chain_wallet_cache.c index 1f72a4d931de18804067de3bcab8de8179101717..6f69cba6369bb928bede51841b23963cbbdab340 100644 --- a/modules/wallet/dap_chain_wallet_cache.c +++ b/modules/wallet/dap_chain_wallet_cache.c @@ -84,6 +84,7 @@ typedef struct dap_wallet_tx_cache { typedef struct dap_s_wallets_cache { dap_chain_addr_t wallet_addr; dap_wallet_tx_cache_t *wallet_txs; + _Atomic bool is_loading; UT_hash_handle hh; } dap_wallet_cache_t; @@ -92,7 +93,7 @@ typedef struct dap_atom_notify_arg { dap_chain_net_t *net; } dap_atom_notify_arg_t; -static dap_s_wallets_cache_type_t s_wallets_cache_type = DAP_WALLET_CACHE_TYPE_ALL; +static dap_s_wallets_cache_type_t s_wallets_cache_type = DAP_WALLET_CACHE_TYPE_LOCAL; static dap_wallet_cache_t *s_wallets_cache = NULL; static pthread_rwlock_t s_wallet_cache_rwlock; @@ -207,7 +208,7 @@ int dap_chain_wallet_cache_tx_find(dap_chain_addr_t *a_addr, char *a_token, dap_ dap_wallet_cache_t *l_wallet_item = NULL; pthread_rwlock_rdlock(&s_wallet_cache_rwlock); HASH_FIND(hh, s_wallets_cache, a_addr, sizeof(dap_chain_addr_t), l_wallet_item); - if (!l_wallet_item){ + if (!l_wallet_item || l_wallet_item->is_loading){ log_it(L_ERROR, "Can't find wallet with address %s", dap_chain_addr_to_str_static(a_addr)); pthread_rwlock_unlock(&s_wallet_cache_rwlock); return -101; @@ -291,7 +292,7 @@ int dap_chain_wallet_cache_tx_find_in_history(dap_chain_addr_t *a_addr, char **a pthread_rwlock_rdlock(&s_wallet_cache_rwlock); HASH_FIND(hh, s_wallets_cache, a_addr, sizeof(dap_chain_addr_t), l_wallet_item); - if (!l_wallet_item){ + if (!l_wallet_item || l_wallet_item->is_loading){ log_it(L_ERROR, "Can't find wallet with address %s", dap_chain_addr_to_str_static(a_addr)); pthread_rwlock_unlock(&s_wallet_cache_rwlock); return -101; @@ -376,7 +377,7 @@ int dap_chain_wallet_cache_tx_find_outs_with_val(dap_chain_net_t *a_net, const c dap_wallet_cache_t *l_wallet_item = NULL; pthread_rwlock_rdlock(&s_wallet_cache_rwlock); HASH_FIND(hh, s_wallets_cache, a_addr, sizeof(dap_chain_addr_t), l_wallet_item); - if (!l_wallet_item){ + if (!l_wallet_item || l_wallet_item->is_loading){ log_it(L_ERROR, "Can't find wallet with address %s", dap_chain_addr_to_str_static(a_addr)); pthread_rwlock_unlock(&s_wallet_cache_rwlock); return -101; @@ -491,6 +492,129 @@ int dap_chain_wallet_cache_tx_find_outs_with_val(dap_chain_net_t *a_net, const c return 0; } +int dap_chain_wallet_cache_tx_find_outs(dap_chain_net_t *a_net, const char *a_token_ticker, const dap_chain_addr_t *a_addr, + dap_list_t **a_outs_list, uint256_t *a_value_transfer) +{ + + dap_list_t *l_list_used_out = NULL; // list of transaction with 'out' items + uint256_t l_value_transfer = { }; + dap_chain_datum_tx_t *l_tx; + + if (!a_token_ticker){ + log_it(L_ERROR, "Token ticker is not specified."); + return -100; + } + + if(!a_addr || dap_chain_addr_is_blank(a_addr)){ + log_it(L_ERROR, "Wallet addr is not specified."); + return -100; + } + + if (a_outs_list == NULL){ + log_it(L_ERROR, "a_outs_list is NULL"); + return -100; + } + + dap_wallet_cache_t *l_wallet_item = NULL; + pthread_rwlock_rdlock(&s_wallet_cache_rwlock); + HASH_FIND(hh, s_wallets_cache, a_addr, sizeof(dap_chain_addr_t), l_wallet_item); + if (!l_wallet_item || l_wallet_item->is_loading){ + log_it(L_ERROR, "Can't find wallet with address %s", dap_chain_addr_to_str_static(a_addr)); + pthread_rwlock_unlock(&s_wallet_cache_rwlock); + return -101; + } + dap_wallet_tx_cache_t *l_current_wallet_tx = l_wallet_item->wallet_txs; + + // Go iterate wallet txs + dap_wallet_tx_cache_t *l_current_wallet_tx_iter = NULL, *l_tmp = NULL; + HASH_ITER(hh, l_current_wallet_tx, l_current_wallet_tx_iter, l_tmp) { + if (l_current_wallet_tx_iter->ret_code != DAP_LEDGER_CHECK_OK) + continue; + + + if (*l_current_wallet_tx_iter->token_ticker && + dap_strcmp(l_current_wallet_tx_iter->token_ticker, a_token_ticker) && + !l_current_wallet_tx_iter->multichannel) + continue; + else if (*l_current_wallet_tx_iter->token_ticker && dap_strcmp(l_current_wallet_tx_iter->token_ticker, a_token_ticker) && + l_current_wallet_tx_iter->multichannel){ + + bool skip = true; + for (dap_list_t *l_temp = l_current_wallet_tx_iter->tx_wallet_outputs; l_temp; l_temp=l_temp->next){ + dap_wallet_tx_cache_output_t *l_out_cur = (dap_wallet_tx_cache_output_t*)l_temp->data; + dap_chain_tx_item_type_t l_type = *(dap_chain_tx_item_type_t*)l_out_cur->tx_out; + uint256_t l_value = { }; + switch (l_type) { + case TX_ITEM_TYPE_OUT_EXT: { + dap_chain_tx_out_ext_t *l_out_ext = (dap_chain_tx_out_ext_t*)l_out_cur->tx_out; + if (dap_strcmp(l_out_ext->token, a_token_ticker)) + continue; + if (IS_ZERO_256(l_out_ext->header.value) ) + continue; + l_value = l_out_ext->header.value; + } break; + default: + continue; + } + // Check whether used 'out' items + if ( !dap_ledger_tx_hash_is_used_out_item (a_net->pub.ledger, &l_current_wallet_tx_iter->tx_hash, l_out_cur->tx_out_idx, NULL) ) { + 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_current_wallet_tx_iter->tx_hash, (uint32_t)l_out_cur->tx_out_idx, l_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); + } + } + } else { + bool skip = true; + for (dap_list_t *l_temp = l_current_wallet_tx_iter->tx_wallet_outputs; l_temp; l_temp=l_temp->next){ + dap_wallet_tx_cache_output_t *l_out_cur = (dap_wallet_tx_cache_output_t*)l_temp->data; + dap_chain_tx_item_type_t l_type = *(dap_chain_tx_item_type_t*)l_out_cur->tx_out; + uint256_t l_value = { }; + switch (l_type) { + case TX_ITEM_TYPE_OUT_OLD: { + dap_chain_tx_out_old_t *l_out = (dap_chain_tx_out_old_t*)l_out_cur->tx_out; + if (!l_out->header.value) + continue; + l_value = GET_256_FROM_64(l_out->header.value); + } break; + case TX_ITEM_TYPE_OUT: { + dap_chain_tx_out_t *l_out = (dap_chain_tx_out_t*)l_out_cur->tx_out; + if (IS_ZERO_256(l_out->header.value) ) + continue; + l_value = l_out->header.value; + } break; + case TX_ITEM_TYPE_OUT_EXT: { + // TODO: check ticker + dap_chain_tx_out_ext_t *l_out_ext = (dap_chain_tx_out_ext_t*)l_out_cur->tx_out; + if (dap_strcmp(l_out_ext->token, a_token_ticker)) + continue; + if (IS_ZERO_256(l_out_ext->header.value) ) + continue; + l_value = l_out_ext->header.value; + } break; + default: + continue; + } + // Check whether used 'out' items + + if ( !dap_ledger_tx_hash_is_used_out_item (a_net->pub.ledger, &l_current_wallet_tx_iter->tx_hash, l_out_cur->tx_out_idx, NULL) ) { + 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_current_wallet_tx_iter->tx_hash, (uint32_t)l_out_cur->tx_out_idx, l_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); + } + } + } + } + pthread_rwlock_unlock(&s_wallet_cache_rwlock); + + *a_outs_list = l_list_used_out; + if (a_value_transfer) + *a_value_transfer = l_value_transfer; + + return 0; +} + static int s_save_tx_into_wallet_cache(dap_chain_t *a_chain, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash, dap_hash_fast_t *a_atom_hash, int a_ret_code, char* a_main_token_ticker, dap_chain_srv_uid_t a_srv_uid, dap_chain_tx_tag_action_type_t a_action) @@ -631,11 +755,6 @@ static int s_save_tx_into_wallet_cache(dap_chain_t *a_chain, dap_chain_datum_tx_ static int s_save_cache_for_addr_in_net(dap_chain_net_t *a_net, dap_chain_addr_t *a_addr) { - // Find chain with transactions - dap_hash_fast_t l_curr_tx_hash = {0}; - if (dap_chain_wallet_cache_tx_find_in_history(a_addr, NULL, NULL, NULL, NULL, NULL, &l_curr_tx_hash) == 0) - return 0; - dap_chain_t *l_chain = a_net->pub.chains; while (l_chain){ for(int i = 0; i < l_chain->datum_types_count; i++) { @@ -646,8 +765,10 @@ static int s_save_cache_for_addr_in_net(dap_chain_net_t *a_net, dap_chain_addr_t l_datum; l_datum = l_chain->callback_datum_iter_get_next(l_iter)){ - s_save_tx_cache_for_addr(l_chain, a_addr, (dap_chain_datum_tx_t*)l_datum->data, l_iter->cur_hash, l_iter->cur_atom_hash, l_iter->ret_code, l_iter->token_ticker, l_iter->uid, l_iter->action); + if (l_datum->header.type_id == DAP_CHAIN_DATUM_TX) + s_save_tx_cache_for_addr(l_chain, a_addr, (dap_chain_datum_tx_t*)l_datum->data, l_iter->cur_hash, l_iter->cur_atom_hash, l_iter->ret_code, l_iter->token_ticker, l_iter->uid, l_iter->action); } + l_chain->callback_datum_iter_delete(l_iter); break; } } @@ -668,29 +789,64 @@ static void s_callback_datum_notify(void *a_arg, dap_chain_hash_fast_t *a_datum_ return; dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*)l_datum->data; - + const char* l_main_token_ticker = NULL; l_main_token_ticker = dap_ledger_tx_get_token_ticker_by_hash(l_arg->net->pub.ledger, a_datum_hash); s_save_tx_into_wallet_cache(l_arg->chain, l_tx, a_datum_hash, a_atom_hash, a_ret_code, (char*)l_main_token_ticker, a_uid, a_action); } +typedef struct wallet_cache_load_args { + dap_chain_net_t *net; + dap_chain_addr_t addr; + dap_wallet_cache_t *wallet_item; +} wallet_cache_load_args_t; + +static void *s_wallet_load(void *a_arg) +{ + wallet_cache_load_args_t *l_args = (wallet_cache_load_args_t*)a_arg; + + s_save_cache_for_addr_in_net(l_args->net, &l_args->addr); + + l_args->wallet_item->is_loading = false; + DAP_DEL_Z(a_arg); + + return NULL; +} + static void s_wallet_opened_callback(dap_chain_wallet_t *a_wallet, void *a_arg) { for(dap_chain_net_t *l_net = dap_chain_net_iter_start(); l_net; l_net=dap_chain_net_iter_next(l_net)){ + if (dap_chain_net_get_load_mode(l_net)) + continue; // get wallet addr in current net dap_chain_addr_t *l_addr = dap_chain_wallet_get_addr(a_wallet, l_net->pub.id); pthread_rwlock_wrlock(&s_wallet_cache_rwlock); dap_wallet_cache_t *l_wallet_item = NULL; HASH_FIND(hh, s_wallets_cache, l_addr, sizeof(dap_chain_addr_t), l_wallet_item); - if (!l_wallet_item){ - l_wallet_item = DAP_NEW_Z(dap_wallet_cache_t); - memcpy(&l_wallet_item->wallet_addr, l_addr, sizeof(dap_chain_addr_t)); - HASH_ADD(hh, s_wallets_cache, wallet_addr, sizeof(dap_chain_addr_t), l_wallet_item); - pthread_rwlock_unlock(&s_wallet_cache_rwlock); - s_save_cache_for_addr_in_net(l_net, l_addr); - } else + if (l_wallet_item){ pthread_rwlock_unlock(&s_wallet_cache_rwlock); + continue; + } + + l_wallet_item = DAP_NEW_Z(dap_wallet_cache_t); + memcpy (&l_wallet_item->wallet_addr, l_addr, sizeof(dap_chain_addr_t)); + l_wallet_item->is_loading = true; + HASH_ADD(hh, s_wallets_cache, wallet_addr, sizeof(dap_chain_addr_t), l_wallet_item); + pthread_rwlock_unlock(&s_wallet_cache_rwlock); + + wallet_cache_load_args_t *l_args = DAP_NEW_Z(wallet_cache_load_args_t); + l_args->net = l_net; + l_args->addr = *l_addr; + l_args->wallet_item = l_wallet_item; + + pthread_t l_tid; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&l_tid, &attr, s_wallet_load, l_args); + + // s_save_cache_for_addr_in_net(l_net, l_addr); DAP_DEL_Z(l_addr); } } @@ -703,25 +859,28 @@ static int s_save_tx_cache_for_addr(dap_chain_t *a_chain, dap_chain_addr_t *a_ad int l_ret_val = 0; int l_items_cnt = 0; - dap_list_t *l_out_list = dap_chain_datum_tx_items_get(a_tx, TX_ITEM_TYPE_OUT_ALL, &l_items_cnt); bool l_multichannel = false; int l_out_idx = 0; - for (dap_list_t *it=l_out_list; it; it=it->next, l_out_idx++){ - uint8_t l_out_type = *(uint8_t *)it->data; + uint8_t *l_tx_item = NULL; + size_t l_size; int i; + TX_ITEM_ITER_TX_TYPE(l_tx_item, TX_ITEM_TYPE_OUT_ALL, l_size, i, a_tx) { + uint8_t l_out_type = *(uint8_t *)l_tx_item; dap_chain_addr_t l_addr = {}; switch(l_out_type){ case TX_ITEM_TYPE_OUT_OLD: { - l_addr = ((dap_chain_tx_out_old_t*)it->data)->addr; + l_addr = ((dap_chain_tx_out_old_t*)l_tx_item)->addr; } break; case TX_ITEM_TYPE_OUT: { - l_addr = ((dap_chain_tx_out_t*)it->data)->addr; + l_addr = ((dap_chain_tx_out_t*)l_tx_item)->addr; } break; case TX_ITEM_TYPE_OUT_EXT: { - l_addr = ((dap_chain_tx_out_ext_t*)it->data)->addr; + l_addr = ((dap_chain_tx_out_ext_t*)l_tx_item)->addr; l_multichannel = true; } break; - default: + default:{ + l_out_idx++; continue; + } } if(!dap_chain_addr_is_blank(&l_addr) && dap_chain_addr_compare(&l_addr, a_addr) && @@ -749,21 +908,21 @@ static int s_save_tx_cache_for_addr(dap_chain_t *a_chain, dap_chain_addr_t *a_ad } l_wallet_tx_item->multichannel = l_multichannel; dap_wallet_tx_cache_output_t *l_out = DAP_NEW_Z(dap_wallet_tx_cache_output_t); - l_out->tx_out = it->data; + l_out->tx_out = l_tx_item; l_out->tx_out_idx = l_out_idx; l_wallet_tx_item->tx_wallet_outputs = dap_list_append(l_wallet_tx_item->tx_wallet_outputs, l_out); pthread_rwlock_unlock(&s_wallet_cache_rwlock); } } - dap_list_t *l_in_list = dap_chain_datum_tx_items_get(a_tx, TX_ITEM_TYPE_IN_ALL, &l_items_cnt); - for (dap_list_t *it=l_out_list; it; it=it->next ){ - uint8_t l_cond_type = *(uint8_t *)it->data; + l_tx_item = NULL; + TX_ITEM_ITER_TX_TYPE(l_tx_item, TX_ITEM_TYPE_IN_ALL, l_size, i, a_tx) { + uint8_t l_cond_type = *l_tx_item; uint256_t l_value = {}; dap_chain_addr_t l_addr_from = {}; if(l_cond_type == TX_ITEM_TYPE_IN){ - dap_hash_fast_t l_prev_tx_hash = ((dap_chain_tx_in_t*)it->data)->header.tx_prev_hash; - int l_prev_idx = ((dap_chain_tx_in_t*)it->data)->header.tx_out_prev_idx; + dap_hash_fast_t l_prev_tx_hash = ((dap_chain_tx_in_t*)l_tx_item)->header.tx_prev_hash; + int l_prev_idx = ((dap_chain_tx_in_t*)l_tx_item)->header.tx_out_prev_idx; if (dap_hash_fast_is_blank(&l_prev_tx_hash)) continue; dap_chain_datum_tx_t *l_tx_prev = (dap_chain_datum_tx_t *)(a_chain->callback_datum_find_by_hash(a_chain, &l_prev_tx_hash, NULL, NULL)->data); @@ -772,16 +931,16 @@ static int s_save_tx_cache_for_addr(dap_chain_t *a_chain, dap_chain_addr_t *a_ad uint8_t* l_prev_item = dap_chain_datum_tx_item_get_nth(l_tx_prev, TX_ITEM_TYPE_OUT, l_prev_idx); if (!l_prev_item) continue; - uint8_t l_out_type = *(uint8_t *)it->data; + uint8_t l_out_type = *(uint8_t *)l_prev_item; switch(l_out_type){ case TX_ITEM_TYPE_OUT_OLD: { - l_value = GET_256_FROM_64(((dap_chain_tx_out_old_t*)it->data)->header.value); - l_addr_from = ((dap_chain_tx_out_old_t*)it->data)->addr; + l_value = GET_256_FROM_64(((dap_chain_tx_out_old_t*)l_prev_item)->header.value); + l_addr_from = ((dap_chain_tx_out_old_t*)l_prev_item)->addr; } break; case TX_ITEM_TYPE_OUT: case TX_ITEM_TYPE_OUT_EXT: { - l_value = ((dap_chain_tx_out_ext_t*)it->data)->header.value; - l_addr_from = ((dap_chain_tx_out_ext_t*)it->data)->addr; + l_value = ((dap_chain_tx_out_ext_t*)l_prev_item)->header.value; + l_addr_from = ((dap_chain_tx_out_ext_t*)l_prev_item)->addr; } break; default: continue; @@ -822,12 +981,6 @@ static int s_save_tx_cache_for_addr(dap_chain_t *a_chain, dap_chain_addr_t *a_ad } } - if (l_out_list) - dap_list_free(l_out_list); - - if (l_in_list) - dap_list_free(l_in_list); - return l_ret_val; } diff --git a/modules/wallet/include/dap_chain_wallet_cache.h b/modules/wallet/include/dap_chain_wallet_cache.h index b76186b1c2b3c120dcd394fa4bd06e586068fd09..779ec76d99e0f345b80290eb269f6455001dcada 100644 --- a/modules/wallet/include/dap_chain_wallet_cache.h +++ b/modules/wallet/include/dap_chain_wallet_cache.h @@ -96,7 +96,8 @@ int dap_chain_wallet_cache_tx_find_in_history(dap_chain_addr_t *a_addr, char **a int dap_chain_wallet_cache_tx_find_outs_with_val(dap_chain_net_t *a_net, const char *a_token_ticker, const dap_chain_addr_t *a_addr, dap_list_t **a_outs_list, uint256_t a_value_needed, uint256_t *a_value_transfer); - +int dap_chain_wallet_cache_tx_find_outs(dap_chain_net_t *a_net, const char *a_token_ticker, const dap_chain_addr_t *a_addr, + dap_list_t **a_outs_list, uint256_t *a_value_transfer); dap_chain_wallet_cache_iter_t *dap_chain_wallet_cache_iter_create(dap_chain_addr_t a_addr); void dap_chain_wallet_cache_iter_delete(dap_chain_wallet_cache_iter_t *a_iter);