From 924faae17366bb17b4de5318429811a80dd62c80 Mon Sep 17 00:00:00 2001 From: "daniil.frolov" <daniil.frolov@demlabs.net> Date: Sun, 12 Jan 2025 15:07:11 +0300 Subject: [PATCH 1/8] [*] hotfix-15033 ported --- dap-sdk | 2 +- modules/net/dap_chain_net_tx.c | 1060 +++++++++++++++++ modules/net/include/dap_chain_net_tx.h | 41 + modules/node-cli/dap_chain_node_cli_cmd_tx.c | 886 +------------- .../node-cli/include/dap_chain_node_cli_cmd.h | 13 - 5 files changed, 1154 insertions(+), 848 deletions(-) diff --git a/dap-sdk b/dap-sdk index 5c58976798..f13ef6cbb1 160000 --- a/dap-sdk +++ b/dap-sdk @@ -1 +1 @@ -Subproject commit 5c58976798909feee535cbff8b2b7e80028aad1c +Subproject commit f13ef6cbb1d78563cc78a8aefc6335ade43a33c2 diff --git a/modules/net/dap_chain_net_tx.c b/modules/net/dap_chain_net_tx.c index 33032a2b09..1abe11db08 100644 --- a/modules/net/dap_chain_net_tx.c +++ b/modules/net/dap_chain_net_tx.c @@ -31,6 +31,14 @@ #include "dap_chain_tx.h" #include "dap_list.h" +#include "dap_chain_datum_tx_receipt.h" +#include "dap_chain_wallet.h" +#include "dap_chain_datum_tx_voting.h" +#include "json.h" +#include "dap_chain_srv.h" +#include "dap_chain_net_srv.h" +#include "dap_enc_base64.h" + #define LOG_TAG "dap_chain_net_tx" typedef struct cond_all_with_spends_by_srv_uid_arg{ @@ -537,3 +545,1055 @@ bool dap_chain_net_tx_set_fee(dap_chain_net_id_t a_net_id, uint256_t a_value, da return true; } + +static const char* s_json_get_text(struct json_object *a_json, const char *a_key) +{ + if(!a_json || !a_key) + return NULL; + struct json_object *l_json = json_object_object_get(a_json, a_key); + if(l_json && json_object_is_type(l_json, json_type_string)) { + // Read text + return json_object_get_string(l_json); + } + return NULL; +} + +static bool s_json_get_int64(struct json_object *a_json, const char *a_key, int64_t *a_out) +{ + if(!a_json || !a_key || !a_out) + return false; + struct json_object *l_json = json_object_object_get(a_json, a_key); + if(l_json) { + if(json_object_is_type(l_json, json_type_int)) { + // Read number + *a_out = json_object_get_int64(l_json); + return true; + } + } + return false; +} + +static bool s_json_get_unit(struct json_object *a_json, const char *a_key, dap_chain_net_srv_price_unit_uid_t *a_out) +{ + const char *l_unit_str = s_json_get_text(a_json, a_key); + if(!l_unit_str || !a_out) + return false; + dap_chain_net_srv_price_unit_uid_t l_unit = dap_chain_net_srv_price_unit_uid_from_str(l_unit_str); + if(l_unit.enm == SERV_UNIT_UNDEFINED) + return false; + a_out->enm = l_unit.enm; + return true; +} + +static bool s_json_get_uint256(struct json_object *a_json, const char *a_key, uint256_t *a_out) +{ + const char *l_uint256_str = s_json_get_text(a_json, a_key); + if(!a_out || !l_uint256_str) + return false; + uint256_t l_value = dap_chain_balance_scan(l_uint256_str); + if(!IS_ZERO_256(l_value)) { + memcpy(a_out, &l_value, sizeof(uint256_t)); + return true; + } + return false; +} + +// service names: srv_stake, srv_vpn, srv_xchange +static bool s_json_get_srv_uid(struct json_object *a_json, const char *a_key_service_id, const char *a_key_service, uint64_t *a_out) +{ + uint64_t l_srv_id; + if(!a_out) + return false; + // Read service id + if(s_json_get_int64(a_json, a_key_service_id, (int64_t*) &l_srv_id)) { + *a_out = l_srv_id; + return true; + } + else { + // Read service as name + const char *l_service = s_json_get_text(a_json, a_key_service); + if (l_service) + *a_out = dap_chain_srv_get_uid_by_name(l_service).uint64; + } + return false; +} + +static dap_chain_wallet_t* s_json_get_wallet(struct json_object *a_json, const char *a_key) +{ + return dap_chain_wallet_open(s_json_get_text(a_json, a_key), dap_chain_wallet_get_path(g_config), NULL); +} + +static const dap_cert_t* s_json_get_cert(struct json_object *a_json, const char *a_key) +{ + return dap_cert_find_by_name(s_json_get_text(a_json, a_key)); +} + +// Read pkey from wallet or cert +static dap_pkey_t* s_json_get_pkey(struct json_object *a_json) +{ + dap_pkey_t *l_pub_key = NULL; + // From wallet + dap_chain_wallet_t *l_wallet = s_json_get_wallet(a_json, "wallet"); + if(l_wallet) { + l_pub_key = dap_chain_wallet_get_pkey(l_wallet, 0); + dap_chain_wallet_close(l_wallet); + if(l_pub_key) { + return l_pub_key; + } + } + // From cert + const dap_cert_t *l_cert = s_json_get_cert(a_json, "cert"); + if(l_cert) { + l_pub_key = dap_pkey_from_enc_key(l_cert->enc_key); + } + return l_pub_key; +} + +int dap_chain_net_tx_create_by_json(json_object *a_tx_json, dap_chain_net_t *a_net, json_object *a_json_obj_error, + dap_chain_datum_tx_t** a_out_tx, size_t* a_items_count, size_t *a_items_ready) +{ + + json_object *l_json = a_tx_json; + json_object *l_jobj_errors = a_json_obj_error ? a_json_obj_error : NULL; + + if (!a_tx_json) + return log_it(L_ERROR, "Empty json"), DAP_CHAIN_NET_TX_CREATE_JSON_CAN_NOT_OPEN_JSON_FILE; + + if(!a_out_tx){ + log_it(L_ERROR, "a_out_tx is NULL"); + return DAP_CHAIN_NET_TX_CREATE_JSON_WRONG_ARGUMENTS; + } + + const char *l_native_token = a_net ? a_net->pub.native_ticker : NULL; + const char *l_main_token = NULL; + bool l_multichanel = false; + + + // Read items from json file + struct json_object *l_json_items = json_object_object_get(l_json, "items"); + size_t l_items_count; + if(!l_json_items || !json_object_is_type(l_json_items, json_type_array) || !(l_items_count = json_object_array_length(l_json_items))) { + json_object_put(l_json); + return DAP_CHAIN_NET_TX_CREATE_JSON_NOT_FOUNT_ARRAY_ITEMS; + } + + log_it(L_ERROR, "Json TX: found %lu items", l_items_count); + // Create transaction + dap_chain_datum_tx_t *l_tx = DAP_NEW_Z_SIZE(dap_chain_datum_tx_t, sizeof(dap_chain_datum_tx_t)); + if(!l_tx) { + json_object_put(l_json); + return DAP_JSON_RPC_ERR_CODE_MEMORY_ALLOCATED; + } + + struct json_object *l_json_timestamp = json_object_object_get(l_json, "ts_created"); + if (l_json_timestamp) + l_tx->header.ts_created = json_object_get_int64(l_json_timestamp); + else + l_tx->header.ts_created = time(NULL); + + size_t l_items_ready = 0; + dap_list_t *l_in_list = NULL;// list 'in' items + dap_list_t *l_sign_list = NULL;// list 'sign' items + uint256_t l_value_need = { };// how many tokens are needed in the 'out' item + uint256_t l_value_need_fee = {}; + + if(a_net){ // if composition is not offline + // 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(l_json_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 (l_json_item_token && 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(l_json_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; + } + + log_it(L_DEBUG, "Json TX: process item %s", json_object_get_string(l_json_item_type)); + // Create an item depending on its type + const uint8_t *l_item = NULL; + switch (l_item_type) { + case TX_ITEM_TYPE_IN: { + // Save item obj for in + // Read prev_hash and out_prev_idx + const char *l_prev_hash_str = s_json_get_text(l_json_item_obj, "prev_hash"); + int64_t l_out_prev_idx; + bool l_is_out_prev_idx = s_json_get_int64(l_json_item_obj, "out_prev_idx", &l_out_prev_idx); + // If prev_hash and out_prev_idx were read + if(l_prev_hash_str && l_is_out_prev_idx) { + dap_chain_hash_fast_t l_tx_prev_hash; + if(!dap_chain_hash_fast_from_str(l_prev_hash_str, &l_tx_prev_hash)) { + // Create IN item + dap_chain_tx_in_t *l_in_item = dap_chain_datum_tx_item_in_create(&l_tx_prev_hash, (uint32_t) l_out_prev_idx); + if (!l_in_item) { + json_object *l_jobj_err = json_object_new_string("Unable to create in for transaction."); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + } + l_item = (const uint8_t*) l_in_item; + } else { + log_it(L_WARNING, "Invalid 'in' item, bad prev_hash %s", l_prev_hash_str); + char *l_str_err = dap_strdup_printf("Unable to create in for transaction. Invalid 'in' item, " + "bad prev_hash %s", l_prev_hash_str); + json_object *l_jobj_err = json_object_new_string(l_str_err); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + } + } + // Read addr_from + else { + l_in_list = dap_list_append(l_in_list, l_json_item_obj); + } + } + break; + + case TX_ITEM_TYPE_OUT: + case TX_ITEM_TYPE_OUT_EXT: { + // Read address and value + 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 + const uint8_t *l_out_item = NULL; + if (a_net) {// if composition is not offline + 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."); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + } + if (l_out_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 { + 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."); + if (l_jobj_errors) 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) { + // Read address and value + if(l_token) { + // Create OUT_EXT item + const uint8_t *l_out_item = NULL; + if (a_net){ // if composition is not offline + 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."); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + } + if (l_out_item){ + if (l_multichanel && !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 { + l_out_item = (const uint8_t *)dap_chain_datum_tx_item_out_ext_create(l_addr, l_value, l_token); + 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."); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + } + } + l_item = (const uint8_t*) l_out_item; + } + else { + log_it(L_WARNING, "Invalid 'out_ext' item %zu", i); + continue; + } + } + } else { + if(l_item_type == TX_ITEM_TYPE_OUT) { + log_it(L_WARNING, "Invalid 'out' item %zu", i); + } + else if(l_item_type == TX_ITEM_TYPE_OUT_EXT) { + log_it(L_WARNING, "Invalid 'out_ext' item %zu", i); + } + char *l_str_err = dap_strdup_printf("For item %zu of type 'out' or 'out_ext' the " + "string representation of the address could not be converted, " + "or the size of the output sum is 0.", i); + json_object *l_jobj_err = json_object_new_string(l_str_err); + DAP_DELETE(l_str_err); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + continue; + } + } + } + break; + case TX_ITEM_TYPE_OUT_COND: { + // Read subtype of item + const char *l_subtype_str = s_json_get_text(l_json_item_obj, "subtype"); + dap_chain_tx_out_cond_subtype_t l_subtype = dap_chain_tx_out_cond_subtype_from_str(l_subtype_str); + switch (l_subtype) { + + case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY:{ + uint256_t l_value = { }; + bool l_is_value = s_json_get_uint256(l_json_item_obj, "value", &l_value); + if(!l_is_value || IS_ZERO_256(l_value)) { + log_it(L_ERROR, "Json TX: bad value in OUT_COND_SUBTYPE_SRV_PAY"); + break; + } + uint256_t l_value_max_per_unit = { }; + l_is_value = s_json_get_uint256(l_json_item_obj, "value_max_per_unit", &l_value_max_per_unit); + if(!l_is_value || IS_ZERO_256(l_value_max_per_unit)) { + log_it(L_ERROR, "Json TX: bad value_max_per_unit in OUT_COND_SUBTYPE_SRV_PAY"); + break; + } + dap_chain_net_srv_price_unit_uid_t l_price_unit; + if(!s_json_get_unit(l_json_item_obj, "price_unit", &l_price_unit)) { + log_it(L_ERROR, "Json TX: bad price_unit in OUT_COND_SUBTYPE_SRV_PAY"); + break; + } + dap_chain_srv_uid_t l_srv_uid; + uint64_t l_srv_id_ui64; + if(!s_json_get_srv_uid(l_json_item_obj, "service_id", "service", &l_srv_id_ui64)){ + // Default service DAP_CHAIN_NET_SRV_VPN_ID + l_srv_uid.uint64 = 0x0000000000000001; + } else { + l_srv_uid.uint64 = l_srv_id_ui64; + } + + // From "wallet" or "cert" + dap_pkey_t *l_pkey = s_json_get_pkey(l_json_item_obj); + if(!l_pkey) { + log_it(L_ERROR, "Json TX: bad pkey in OUT_COND_SUBTYPE_SRV_PAY"); + break; + } + const char *l_params_str = s_json_get_text(l_json_item_obj, "params"); + size_t l_params_size = dap_strlen(l_params_str); + dap_chain_tx_out_cond_t *l_out_cond_item = dap_chain_datum_tx_item_out_cond_create_srv_pay(l_pkey, l_srv_uid, l_value, l_value_max_per_unit, + l_price_unit, l_params_str, l_params_size); + l_item = (const uint8_t*) l_out_cond_item; + // Save value for using in In item + if(l_item) { + SUM_256_256(l_value_need, l_value, &l_value_need); + } else { + char *l_str_err = dap_strdup_printf("Unable to create conditional out for transaction " + "can of type %s described in item %zu.\n", l_subtype_str, i); + json_object *l_jobj_err = json_object_new_string(l_str_err); + DAP_DELETE(l_str_err); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + } + DAP_DELETE(l_pkey); + } + break; + case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE: { + + dap_chain_srv_uid_t l_srv_uid; + uint64_t l_srv_id_ui64; + if(!s_json_get_srv_uid(l_json_item_obj, "service_id", "service", &l_srv_id_ui64)) { + // Default service DAP_CHAIN_NET_SRV_XCHANGE_ID + l_srv_uid.uint64 = 0x2; + } else { + l_srv_uid.uint64 = l_srv_id_ui64; + } + + dap_chain_net_t *l_net = dap_chain_net_by_name(s_json_get_text(l_json_item_obj, "net")); + if(!l_net) { + log_it(L_ERROR, "Json TX: bad net in OUT_COND_SUBTYPE_SRV_XCHANGE"); + break; + } + const char *l_token = s_json_get_text(l_json_item_obj, "token"); + if(!l_token) { + log_it(L_ERROR, "Json TX: bad token in OUT_COND_SUBTYPE_SRV_XCHANGE"); + break; + } + uint256_t l_value = { }; + if(!s_json_get_uint256(l_json_item_obj, "value", &l_value) || IS_ZERO_256(l_value)) { + log_it(L_ERROR, "Json TX: bad value in OUT_COND_SUBTYPE_SRV_XCHANGE"); + break; + } + //const char *l_params_str = s_json_get_text(l_json_item_obj, "params"); + //size_t l_params_size = dap_strlen(l_params_str); + dap_chain_tx_out_cond_t *l_out_cond_item = NULL; //dap_chain_datum_tx_item_out_cond_create_srv_xchange(l_srv_uid, l_net->pub.id, l_token, l_value, l_params_str, l_params_size); + l_item = (const uint8_t*) l_out_cond_item; + // Save value for using in In item + if(l_item) { + SUM_256_256(l_value_need, l_value, &l_value_need); + } else { + char *l_str_err = dap_strdup_printf("Unable to create conditional out for transaction " + "can of type %s described in item %zu.", l_subtype_str, i); + json_object *l_jobj_err = json_object_new_string(l_str_err); + DAP_DELETE(l_str_err); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + } + } + break; + case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE:{ + dap_chain_srv_uid_t l_srv_uid; + uint64_t l_srv_id_ui64 = 0; + if(!s_json_get_srv_uid(l_json_item_obj, "service_id", "service", &l_srv_id_ui64)) { + // Default service DAP_CHAIN_NET_SRV_STAKE_ID + l_srv_uid.uint64 = 0x13; + } else { + l_srv_uid.uint64 = l_srv_id_ui64; + } + uint256_t l_value = { }; + if(!s_json_get_uint256(l_json_item_obj, "value", &l_value) || IS_ZERO_256(l_value)) { + log_it(L_ERROR, "Json TX: bad value in OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE"); + break; + } + uint256_t l_fee_value = { }; + if(!s_json_get_uint256(l_json_item_obj, "fee", &l_fee_value) || IS_ZERO_256(l_fee_value)) { + break; + } + + const char *l_signing_addr_str = s_json_get_text(l_json_item_obj, "signing_addr"); + dap_chain_addr_t *l_signing_addr = dap_chain_addr_from_str(l_signing_addr_str); + if(!l_signing_addr) { + { + log_it(L_ERROR, "Json TX: bad signing_addr in OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE"); + break; + } + dap_chain_node_addr_t l_signer_node_addr; + const char *l_node_addr_str = s_json_get_text(l_json_item_obj, "node_addr"); + if(!l_node_addr_str || dap_chain_node_addr_from_str(&l_signer_node_addr, l_node_addr_str)) { + log_it(L_ERROR, "Json TX: bad node_addr in OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE"); + break; + } + + dap_pkey_t *l_pkey = NULL; + const char *l_pkey_full_str = s_json_get_text(l_json_item_obj, "pkey_full"); + if(l_pkey_full_str) { + l_pkey = dap_pkey_get_from_str(l_pkey_full_str); + debug_if(!l_pkey, L_ERROR, "Json TX: bad pkey in OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE"); + } + + dap_chain_tx_out_cond_t *l_out_cond_item = dap_chain_datum_tx_item_out_cond_create_srv_stake(l_srv_uid, l_value, l_signing_addr, + &l_signer_node_addr, NULL, uint256_0, l_pkey); + l_item = (const uint8_t*) l_out_cond_item; + // Save value for using in In item + if(l_item) { + SUM_256_256(l_value_need, l_value, &l_value_need); + } else { + char *l_err_str = dap_strdup_printf("Unable to create conditional out for transaction " + "can of type %s described in item %zu.", l_subtype_str, i); + json_object *l_jobj_err = json_object_new_string(l_err_str); + DAP_DELETE(l_err_str); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + } + } + } + break; + case DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE: { + uint256_t l_value = { }; + s_json_get_uint256(l_json_item_obj, "value", &l_value); + if(!IS_ZERO_256(l_value)) { + dap_chain_tx_out_cond_t *l_out_cond_item = dap_chain_datum_tx_item_out_cond_create_fee(l_value); + l_item = (const uint8_t*) l_out_cond_item; + // Save value for using in In item + if(l_item) { + SUM_256_256(l_value_need_fee, l_value, &l_value_need_fee); + } else { + char *l_str_err = dap_strdup_printf("Unable to create conditional out for transaction " + "can of type %s described in item %zu.", l_subtype_str, i); + json_object *l_jobj_err = json_object_new_string(l_str_err); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + DAP_DELETE(l_str_err); + } + } + else + log_it(L_ERROR, "Json TX: zero value in OUT_COND_SUBTYPE_FEE"); + } + break; + case DAP_CHAIN_TX_OUT_COND_SUBTYPE_UNDEFINED: + log_it(L_WARNING, "Undefined subtype: '%s' of 'out_cond' item %zu ", l_subtype_str, i); + char *l_str_err = dap_strdup_printf("Specified unknown sub type %s of conditional out on item %zu.", + l_subtype_str, i); + json_object *l_jobj_err = json_object_new_string(l_str_err); + DAP_DELETE(l_str_err); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + break; + } + } + break; + case TX_ITEM_TYPE_SIG:{ + const char *l_sign_type_str = s_json_get_text(l_json_item_obj, "sig_type"); + if (l_sign_type_str) { + dap_sign_type_t l_sign_type = dap_sign_type_from_str(l_sign_type_str); + if (l_sign_type.type == SIG_TYPE_NULL) { + json_object *l_jobj_err = json_object_new_string("Can't define sign type"); + json_object_array_add(l_jobj_errors, l_jobj_err); + log_it(L_ERROR, "Json TX: Can't define sign type \"%s\"", l_sign_type_str); + break; + } + int64_t l_pkey_size, l_sig_size, l_hash_type = 0; + + s_json_get_int64(l_json_item_obj, "hash_type", &l_hash_type); + s_json_get_int64(l_json_item_obj, "pub_key_size", &l_pkey_size); + s_json_get_int64(l_json_item_obj, "sig_size", &l_sig_size); + debug_if(!l_pkey_size || !l_sig_size, L_WARNING, + "\"pub_key_size\" or \"sig_size\" not provided! Will be calculated automatically"); + + json_object *l_jobj_pub_key = json_object_object_get(l_json_item_obj, "pub_key_b64"), + *l_jobj_sign = json_object_object_get(l_json_item_obj, "sig_b64"); + if (!l_jobj_pub_key || !l_jobj_sign) { + json_object *l_jobj_err = json_object_new_string("Can't get base64-encoded sign or pkey!"); + json_object_array_add(l_jobj_errors, l_jobj_err); + log_it(L_ERROR, "Json TX: Can't get base64-encoded sign or pkey!"); + break; + } + const char *l_pub_key_str = json_object_get_string(l_jobj_pub_key), + *l_sign_str = json_object_get_string(l_jobj_sign); + int64_t l_pkey_decoded_size = DAP_ENC_BASE64_DECODE_SIZE(strlen(l_pub_key_str)), + l_sign_decoded_size = DAP_ENC_BASE64_DECODE_SIZE(strlen(l_sign_str)); + + dap_sign_t *l_sign = DAP_NEW_SIZE(dap_sign_t, sizeof(dap_sign_t) + l_pkey_decoded_size + l_sign_decoded_size); + *l_sign = (dap_sign_t) { + .header.type = l_sign_type, + .header.hash_type = (uint8_t)l_hash_type, + }; + l_pkey_decoded_size = dap_enc_base64_decode(l_pub_key_str, strlen(l_pub_key_str), + l_sign->pkey_n_sign, DAP_ENC_DATA_TYPE_B64_URLSAFE); + debug_if(l_pkey_size != l_pkey_decoded_size, L_ERROR, "Json TX: pkey size mismatch, %zu != %zu", + l_pkey_size, l_pkey_decoded_size); + + l_sign_decoded_size = dap_enc_base64_decode(l_sign_str, strlen(l_sign_str), + l_sign->pkey_n_sign + l_pkey_decoded_size, DAP_ENC_DATA_TYPE_B64_URLSAFE); + debug_if(l_sig_size != l_sign_decoded_size, L_ERROR, "Json TX: sign size mismatch, %zu != %zu", + l_sig_size, l_sign_decoded_size); + + l_sign->header.sign_size = l_sign_decoded_size; + l_sign->header.sign_pkey_size = l_pkey_decoded_size; + size_t l_sign_full_size = dap_sign_get_size(l_sign); + + dap_chain_tx_sig_t *l_tx_sig = DAP_NEW_Z_SIZE(dap_chain_tx_sig_t, sizeof(dap_chain_tx_sig_t) + l_sign_full_size); + l_tx_sig->header.type = TX_ITEM_TYPE_SIG; + l_tx_sig->header.version = 1; + l_tx_sig->header.sig_size = (uint32_t)l_sign_full_size; + memcpy(l_tx_sig->sig, l_sign, l_sign_full_size); + l_item = (const uint8_t*)l_tx_sig; + DAP_DELETE(l_sign); + break; + } else + l_sign_list = dap_list_append(l_sign_list,l_json_item_obj); + } + break; + case TX_ITEM_TYPE_RECEIPT: { + dap_chain_srv_uid_t l_srv_uid; + uint64_t l_srv_id_ui64 = 0; + if(!s_json_get_srv_uid(l_json_item_obj, "service_id", "service", &l_srv_id_ui64)) { + log_it(L_ERROR, "Json TX: bad service_id in TYPE_RECEIPT"); + break; + } else { + l_srv_uid.uint64 = l_srv_id_ui64; + } + dap_chain_net_srv_price_unit_uid_t l_price_unit; + if(!s_json_get_unit(l_json_item_obj, "price_unit", &l_price_unit)) { + log_it(L_ERROR, "Json TX: bad price_unit in TYPE_RECEIPT"); + break; + } + int64_t l_units; + if(!s_json_get_int64(l_json_item_obj, "units", &l_units)) { + log_it(L_ERROR, "Json TX: bad units in TYPE_RECEIPT"); + break; + } + uint256_t l_value = { }; + if(!s_json_get_uint256(l_json_item_obj, "value", &l_value) || IS_ZERO_256(l_value)) { + log_it(L_ERROR, "Json TX: bad value in TYPE_RECEIPT"); + break; + } + const char *l_params_str = s_json_get_text(l_json_item_obj, "params"); + size_t l_params_size = dap_strlen(l_params_str); + dap_chain_datum_tx_receipt_t *l_receipt = dap_chain_datum_tx_receipt_create(l_srv_uid, l_price_unit, l_units, l_value, l_params_str, l_params_size); + l_item = (const uint8_t*) l_receipt; + if (!l_item) { + char *l_str_err = dap_strdup_printf("Unable to create receipt out for transaction " + "described by item %zu.", i); + json_object *l_jobj_err = json_object_new_string(l_str_err); + DAP_DELETE(l_str_err); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + } + } + break; + case TX_ITEM_TYPE_TSD: { + int64_t l_tsd_type; + if(!s_json_get_int64(l_json_item_obj, "type_tsd", &l_tsd_type)) { + log_it(L_ERROR, "Json TX: bad type_tsd in TYPE_TSD"); + break; + } + const char *l_tsd_data = s_json_get_text(l_json_item_obj, "data"); + if (!l_tsd_data) { + log_it(L_ERROR, "Json TX: bad data in TYPE_TSD"); + break; + } + size_t l_data_size = dap_strlen(l_tsd_data); + dap_chain_tx_tsd_t *l_tsd = dap_chain_datum_tx_item_tsd_create((void*)l_tsd_data, (int)l_tsd_type, l_data_size); + l_item = (const uint8_t*) l_tsd; + // l_tsd_list = dap_list_append(l_tsd_list, l_tsd); + } + break; + //case TX_ITEM_TYPE_PKEY: + //break; + //case TX_ITEM_TYPE_IN_EMS: + //break; + //case TX_ITEM_TYPE_IN_EMS_EXT: + //break; + } + // Add item to transaction + if(l_item) { + dap_chain_datum_tx_add_item(&l_tx, (const uint8_t*) l_item); + l_items_ready++; + DAP_DELETE(l_item); + } + } + + dap_list_t *l_list; + // Add In items + if(a_net){ + l_list = l_in_list; + while(l_list) { + struct json_object *l_json_item_obj = (struct json_object*) l_list->data; + + const char *l_json_item_addr_str = s_json_get_text(l_json_item_obj, "addr_from"); + const char *l_json_item_token = s_json_get_text(l_json_item_obj, "token"); + l_main_token = l_json_item_token; + dap_chain_addr_t *l_addr_from = NULL; + if(l_json_item_addr_str) { + l_addr_from = dap_chain_addr_from_str(l_json_item_addr_str); + if (!l_addr_from) { + log_it(L_WARNING, "Invalid element 'in', unable to convert string representation of addr_from: '%s' " + "to binary.", l_json_item_addr_str); + char *l_str_err = dap_strdup_printf("Invalid element 'to', unable to convert string representation " + "of addr_from: '%s' to binary.", l_json_item_addr_str); + json_object *l_jobj_err = json_object_new_string(l_str_err); + DAP_DELETE(l_str_err); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + // Go to the next item + l_list = dap_list_next(l_list); + continue; + } + } + else { + log_it(L_WARNING, "Invalid 'in' item, incorrect addr_from: '%s'", l_json_item_addr_str ? l_json_item_addr_str : "[null]"); + char *l_str_err = dap_strdup_printf("Invalid 'in' item, incorrect addr_from: '%s'", + l_json_item_addr_str ? l_json_item_addr_str : "[null]"); + json_object *l_jobj_err = json_object_new_string(l_str_err); + DAP_DELETE(l_str_err); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + // Go to the next item + l_list = dap_list_next(l_list); + continue; + } + if(!l_json_item_token) { + log_it(L_WARNING, "Invalid 'in' item, not found token name"); + json_object *l_jobj_err = json_object_new_string("Invalid 'in' item, not found token name"); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + // Go to the next item + l_list = dap_list_next(l_list); + continue; + } + if(IS_ZERO_256(l_value_need)) { + log_it(L_WARNING, "Invalid 'in' item, not found value in out items"); + json_object *l_jobj_err = json_object_new_string("Invalid 'in' item, not found value in out items"); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + // Go to the next item + l_list = dap_list_next(l_list); + continue; + } + + if(l_addr_from){ + // find the transactions from which to take away coins + dap_list_t *l_list_used_out = NULL; + dap_list_t *l_list_used_out_fee = NULL; + uint256_t l_value_transfer = { }; // how many coins to transfer + uint256_t l_value_transfer_fee = { }; // how many coins to transfer + //SUM_256_256(a_value, a_value_fee, &l_value_need); + uint256_t l_value_need_check = {}; + if (!dap_strcmp(l_native_token, l_main_token)) { + SUM_256_256(l_value_need_check, l_value_need, &l_value_need_check); + SUM_256_256(l_value_need_check, l_value_need_fee, &l_value_need_check); + l_list_used_out = dap_chain_wallet_get_list_tx_outs_with_val(a_net->pub.ledger, l_json_item_token, + l_addr_from, l_value_need_check, &l_value_transfer); + if(!l_list_used_out) { + log_it(L_WARNING, "Not enough funds in previous tx to transfer"); + json_object *l_jobj_err = json_object_new_string("Can't create in transaction. Not enough funds in previous tx " + "to transfer"); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + // Go to the next item + l_list = dap_list_next(l_list); + continue; + } + } else { + //CHECK value need + l_list_used_out = dap_chain_wallet_get_list_tx_outs_with_val(a_net->pub.ledger, l_json_item_token, + l_addr_from, l_value_need, &l_value_transfer); + if(!l_list_used_out) { + log_it(L_WARNING, "Not enough funds in previous tx to transfer"); + json_object *l_jobj_err = json_object_new_string("Can't create in transaction. Not enough funds " + "in previous tx to transfer"); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + // Go to the next item + l_list = dap_list_next(l_list); + continue; + } + //CHECK value fee + l_list_used_out_fee = dap_chain_wallet_get_list_tx_outs_with_val(a_net->pub.ledger, l_native_token, + l_addr_from, l_value_need_fee, &l_value_transfer_fee); + if(!l_list_used_out_fee) { + log_it(L_WARNING, "Not enough funds in previous tx to transfer"); + json_object *l_jobj_err = json_object_new_string("Can't create in transaction. Not enough funds " + "in previous tx to transfer"); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + // Go to the next item + l_list = dap_list_next(l_list); + continue; + } + } + // add 'in' items + uint256_t l_value_got = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out); + assert(EQUAL_256(l_value_got, l_value_transfer)); + if (l_list_used_out_fee) { + uint256_t l_value_got_fee = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out_fee); + assert(EQUAL_256(l_value_got_fee, l_value_transfer_fee)); + dap_list_free_full(l_list_used_out_fee, free); + // add 'out' item for coin fee back + uint256_t l_value_back; + SUBTRACT_256_256(l_value_got_fee, l_value_need_fee, &l_value_back); + if (!IS_ZERO_256(l_value_back)) { + dap_chain_datum_tx_add_out_ext_item(&l_tx, l_addr_from, l_value_back, l_native_token); + l_items_ready++; + } + } else { + SUM_256_256(l_value_need, l_value_need_fee, &l_value_need); + } + dap_list_free_full(l_list_used_out, free); + if(!IS_ZERO_256(l_value_got)) { + // add 'out' item for coin back + uint256_t l_value_back; + SUBTRACT_256_256(l_value_got, l_value_need, &l_value_back); + if(!IS_ZERO_256(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); + l_items_ready++; + } + } + } + // Go to the next 'in' item + l_list = dap_list_next(l_list); + } + } + dap_list_free(l_in_list); + + // Add signs + l_list = l_sign_list; + while(l_list) { + struct json_object *l_json_item_obj = (struct json_object*) l_list->data; + dap_enc_key_t * l_enc_key = NULL; + + //get wallet or cert + dap_chain_wallet_t *l_wallet = s_json_get_wallet(l_json_item_obj, "wallet"); + const dap_cert_t *l_cert = s_json_get_cert(l_json_item_obj, "cert"); + + int64_t l_pkey_size; + int64_t l_sig_size; + uint8_t *l_pkey = NULL; + int64_t l_hash_type = 0; + dap_sign_t *l_sign = NULL; + + + //wallet goes first + if (l_wallet) { + l_enc_key = dap_chain_wallet_get_key(l_wallet, 0); + } else if (l_cert && l_cert->enc_key) { + l_enc_key = l_cert->enc_key; + } else { + json_object *l_jobj_err = json_object_new_string("Can't create sign for transactions."); + json_object_array_add(l_jobj_errors, l_jobj_err); + log_it(L_ERROR, "Json TX: Item sign has no wallet or cert of they are invalid "); + l_list = dap_list_next(l_list); + continue; + } + + if (l_sign) { + size_t l_chain_sign_size = dap_sign_get_size(l_sign); // sign data + + dap_chain_tx_sig_t *l_tx_sig = DAP_NEW_Z_SIZE(dap_chain_tx_sig_t, + sizeof(dap_chain_tx_sig_t) + l_chain_sign_size); + l_tx_sig->header.type = TX_ITEM_TYPE_SIG; + l_tx_sig->header.sig_size =(uint32_t) l_chain_sign_size; + memcpy(l_tx_sig->sig, l_sign, l_chain_sign_size); + dap_chain_datum_tx_add_item(&l_tx, l_tx_sig); + DAP_DELETE(l_sign); + } + + if(l_enc_key && dap_chain_datum_tx_add_sign_item(&l_tx, l_enc_key) > 0) { + l_items_ready++; + } else { + log_it(L_ERROR, "Json TX: Item sign has invalid enc_key."); + l_list = dap_list_next(l_list); + continue; + } + + if (l_wallet) { + dap_chain_wallet_close(l_wallet); + dap_enc_key_delete(l_enc_key); + } + l_list = dap_list_next(l_list); + } + + dap_list_free(l_sign_list); + json_object_put(l_json); + + *a_out_tx = l_tx; + + if(a_items_count) + *a_items_count = l_items_count; + + if(a_items_ready) + *a_items_ready = l_items_ready; + + return DAP_CHAIN_NET_TX_CREATE_JSON_OK; +} + +int dap_chain_net_tx_to_json(dap_chain_datum_tx_t *a_tx, json_object *a_out_json) +{ + if(!a_tx || !a_out_json) + return log_it(L_ERROR, "Empty transaction"), DAP_CHAIN_NET_TX_CREATE_JSON_WRONG_ARGUMENTS; + + json_object* json_obj_out = a_out_json; + json_object* l_json_arr_reply = NULL; + dap_hash_fast_t l_hash_tmp = { }; + byte_t *item; size_t l_size; + char *l_hash_str = NULL; + char l_tmp_buf[DAP_TIME_STR_SIZE]; + json_object* json_arr_items = json_object_new_array(); + + char *l_tx_hash_str = dap_hash_fast_str_new(a_tx, dap_chain_datum_tx_get_size(a_tx)); + + json_object_object_add(json_obj_out, "datum_hash", json_object_new_string(l_tx_hash_str)); + json_object_object_add(json_obj_out, "ts_created", json_object_new_int64(a_tx->header.ts_created)); + json_object_object_add(json_obj_out, "datum_type", json_object_new_string("tx")); + + TX_ITEM_ITER_TX(item, l_size, a_tx) { + json_object* json_obj_item = json_object_new_object(); + switch (*item) { + case TX_ITEM_TYPE_IN: + l_hash_tmp = ((dap_chain_tx_in_t*)item)->header.tx_prev_hash; + l_hash_str = dap_hash_fast_to_str_static(&l_hash_tmp); + json_object_object_add(json_obj_item,"type", json_object_new_string("in")); + json_object_object_add(json_obj_item,"prev_hash", json_object_new_string(l_hash_str)); + json_object_object_add(json_obj_item,"out_prev_idx", json_object_new_uint64(((dap_chain_tx_in_t*)item)->header.tx_out_prev_idx)); + break; + case TX_ITEM_TYPE_OUT: { // 256 + const char *l_coins_str, + *l_value_str = dap_uint256_to_char(((dap_chain_tx_out_t*)item)->header.value, &l_coins_str), + *l_addr_str = dap_chain_addr_to_str_static(&((dap_chain_tx_out_t*)item)->addr); + json_object_object_add(json_obj_item,"type", json_object_new_string("out")); + json_object_object_add(json_obj_item,"value", json_object_new_string(l_value_str)); + json_object_object_add(json_obj_item,"addr", json_object_new_string(l_addr_str)); + } break; + case TX_ITEM_TYPE_SIG: { + dap_sign_t *l_sign = dap_chain_datum_tx_item_sign_get_sig((dap_chain_tx_sig_t*)item); + json_object_object_add(json_obj_item,"type", json_object_new_string("sign")); + dap_chain_hash_fast_t l_hash_pkey; + json_object_object_add(json_obj_item,"sig_type",json_object_new_string(dap_sign_type_to_str(l_sign->header.type))); + json_object_object_add(json_obj_item,"pub_key_size",json_object_new_uint64(l_sign->header.sign_pkey_size)); + json_object_object_add(json_obj_item,"sig_size",json_object_new_uint64(l_sign->header.sign_size)); + json_object_object_add(json_obj_item,"hash_type",json_object_new_uint64(l_sign->header.hash_type)); + + char l_pkey_base64[DAP_ENC_BASE64_ENCODE_SIZE(l_sign->header.sign_pkey_size) + 1]; + size_t l_pkey_base64_size = dap_enc_base64_encode(l_sign->pkey_n_sign, l_sign->header.sign_pkey_size, l_pkey_base64, DAP_ENC_DATA_TYPE_B64_URLSAFE); + l_pkey_base64[l_pkey_base64_size] = '\0'; + json_object_object_add(json_obj_item,"pub_key_b64", json_object_new_string(l_pkey_base64)); + + char l_sign_base64[DAP_ENC_BASE64_ENCODE_SIZE(l_sign->header.sign_size) + 1]; + size_t l_sign_base64_size = dap_enc_base64_encode(l_sign->pkey_n_sign + l_sign->header.sign_pkey_size, l_sign->header.sign_size, l_sign_base64, DAP_ENC_DATA_TYPE_B64_URLSAFE); + l_sign_base64[l_sign_base64_size] = '\0'; + json_object_object_add(json_obj_item,"sig_b64", json_object_new_string(l_sign_base64)); + + } break; + case TX_ITEM_TYPE_TSD: { + json_object_object_add(json_obj_item,"type", json_object_new_string("data")); + json_object_object_add(json_obj_item,"type", json_object_new_uint64(((dap_chain_tx_tsd_t*)item)->header.type)); + json_object_object_add(json_obj_item,"size", json_object_new_uint64(((dap_chain_tx_tsd_t*)item)->header.size)); + } break; + case TX_ITEM_TYPE_IN_COND: + json_object_object_add(json_obj_item,"type", json_object_new_string("in_cond")); + l_hash_tmp = ((dap_chain_tx_in_cond_t*)item)->header.tx_prev_hash; + l_hash_str = dap_hash_fast_to_str_static(&l_hash_tmp); + json_object_object_add(json_obj_item,"receipt_idx", json_object_new_int(((dap_chain_tx_in_cond_t*)item)->header.receipt_idx)); + json_object_object_add(json_obj_item,"out_prev_idx", json_object_new_string(l_hash_str)); + json_object_object_add(json_obj_item,"prev_hash", json_object_new_uint64(((dap_chain_tx_in_cond_t*)item)->header.tx_out_prev_idx)); + break; + case TX_ITEM_TYPE_OUT_COND: { + char l_tmp_buff[70]={0}; + json_object_object_add(json_obj_item,"type", json_object_new_string("out_cond")); + const char *l_coins_str, *l_value_str = dap_uint256_to_char(((dap_chain_tx_out_cond_t*)item)->header.value, &l_coins_str); + dap_time_t l_ts_exp = ((dap_chain_tx_out_cond_t*)item)->header.ts_expires; + dap_time_to_str_rfc822(l_tmp_buf, DAP_TIME_STR_SIZE, l_ts_exp); + json_object_object_add(json_obj_item,"ts_expires", l_ts_exp ? json_object_new_string(l_tmp_buf) : json_object_new_string("never")); + json_object_object_add(json_obj_item,"value", json_object_new_string(l_value_str)); + sprintf(l_tmp_buff,"0x%016"DAP_UINT64_FORMAT_x"",((dap_chain_tx_out_cond_t*)item)->header.srv_uid.uint64); + json_object_object_add(json_obj_item,"service_id", json_object_new_string(l_tmp_buff)); + switch (((dap_chain_tx_out_cond_t*)item)->header.subtype) { + case DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE: + json_object_object_add(json_obj_item,"subtype", json_object_new_string("fee")); + break; + case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY: { + const char *l_coins_str, *l_value_str = + dap_uint256_to_char( ((dap_chain_tx_out_cond_t*)item)->subtype.srv_pay.unit_price_max_datoshi, &l_coins_str ); + l_hash_tmp = ((dap_chain_tx_out_cond_t*)item)->subtype.srv_pay.pkey_hash; + l_hash_str = dap_hash_fast_to_str_static(&l_hash_tmp); + sprintf(l_tmp_buff,"0x%08x",((dap_chain_tx_out_cond_t*)item)->subtype.srv_pay.unit.uint32); + json_object_object_add(json_obj_item,"price_unit", json_object_new_string(l_tmp_buff)); + json_object_object_add(json_obj_item,"pkey", json_object_new_string(l_hash_str)); + json_object_object_add(json_obj_item,"value_max_per_unit", json_object_new_string(l_value_str)); + json_object_object_add(json_obj_item,"subtype", json_object_new_string("srv_pay")); + } break; + case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE: { + dap_chain_node_addr_t *l_signer_node_addr = &((dap_chain_tx_out_cond_t*)item)->subtype.srv_stake_pos_delegate.signer_node_addr; + dap_chain_addr_t *l_signing_addr = &((dap_chain_tx_out_cond_t*)item)->subtype.srv_stake_pos_delegate.signing_addr; + l_hash_tmp = l_signing_addr->data.hash_fast; + l_hash_str = dap_hash_fast_to_str_static(&l_hash_tmp); + json_object_object_add(json_obj_item,"signing_addr", json_object_new_string(dap_chain_addr_to_str_static(l_signing_addr))); + sprintf(l_tmp_buff,""NODE_ADDR_FP_STR"",NODE_ADDR_FP_ARGS(l_signer_node_addr)); + json_object_object_add(json_obj_item,"signer_node_addr", json_object_new_string(l_tmp_buff)); + json_object_object_add(json_obj_item,"subtype", json_object_new_string("srv_stake_pos_delegate")); + } break; + case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE: { + const char *l_rate_str, *l_tmp_str = + dap_uint256_to_char( (((dap_chain_tx_out_cond_t*)item)->subtype.srv_xchange.rate), &l_rate_str ); + sprintf(l_tmp_buff,"0x%016"DAP_UINT64_FORMAT_x"",((dap_chain_tx_out_cond_t*)item)->subtype.srv_xchange.buy_net_id.uint64); + json_object_object_add(json_obj_item,"net_id", json_object_new_string(l_tmp_buff)); + json_object_object_add(json_obj_item,"token", json_object_new_string(((dap_chain_tx_out_cond_t*)item)->subtype.srv_xchange.buy_token)); + json_object_object_add(json_obj_item,"rate", json_object_new_string(l_rate_str)); + json_object_object_add(json_obj_item,"subtype", json_object_new_string("srv_xchange")); + } break; + case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK: { + dap_time_t l_ts_unlock = ((dap_chain_tx_out_cond_t*)item)->subtype.srv_stake_lock.time_unlock; + dap_time_to_str_rfc822(l_tmp_buf, DAP_TIME_STR_SIZE, l_ts_unlock); + json_object_object_add(json_obj_item,"time_unlock", json_object_new_string(l_tmp_buf)); + json_object_object_add(json_obj_item,"subtype", json_object_new_string("srv_stake_lock")); + } break; + default: break; + } + } break; + case TX_ITEM_TYPE_OUT_EXT: { + const char *l_coins_str, *l_value_str = dap_uint256_to_char( ((dap_chain_tx_out_ext_t*)item)->header.value, &l_coins_str ); + json_object_object_add(json_obj_item,"type", json_object_new_string("out_ext")); + json_object_object_add(json_obj_item,"addr", json_object_new_string(dap_chain_addr_to_str_static(&((dap_chain_tx_out_ext_t*)item)->addr))); + json_object_object_add(json_obj_item,"token", json_object_new_string(((dap_chain_tx_out_ext_t*)item)->token)); + json_object_object_add(json_obj_item,"value", json_object_new_string(l_value_str)); + + } break; + case TX_ITEM_TYPE_VOTING:{ + size_t l_tsd_size = 0; + dap_chain_tx_tsd_t *l_item = (dap_chain_tx_tsd_t *)dap_chain_datum_tx_item_get(a_tx, NULL, (byte_t*)item + l_size, TX_ITEM_TYPE_TSD, &l_tsd_size); + if (!l_item || !l_tsd_size) + break; + dap_chain_datum_tx_voting_params_t *l_voting_params = dap_chain_datum_tx_voting_parse_tsd(a_tx); + json_object_object_add(json_obj_item,"type", json_object_new_string("voting")); + json_object_object_add(json_obj_item,"voting_question", json_object_new_string(l_voting_params->question)); + json_object_object_add(json_obj_item,"answer_options", json_object_new_string("")); + + dap_list_t *l_temp = l_voting_params->options; + uint8_t l_index = 0; + while (l_temp) { + json_object_object_add(json_obj_item, dap_itoa(l_index), json_object_new_string((char *)l_temp->data)); + l_index++; + l_temp = l_temp->next; + } + if (l_voting_params->voting_expire) { + dap_time_to_str_rfc822(l_tmp_buf, DAP_TIME_STR_SIZE, l_voting_params->voting_expire); + json_object_object_add(json_obj_item,"Voting expire", json_object_new_string(l_tmp_buf)); + } + if (l_voting_params->votes_max_count) { + json_object_object_add(json_obj_item, "Votes max count", json_object_new_uint64(l_voting_params->votes_max_count)); + } + json_object_object_add(json_obj_item,"Changing vote is", l_voting_params->vote_changing_allowed ? json_object_new_string("available") : + json_object_new_string("not available")); + l_voting_params->delegate_key_required ? + json_object_object_add(json_obj_item,"Delegated key for participating in voting", json_object_new_string("required")): + json_object_object_add(json_obj_item,"Delegated key for participating in voting", json_object_new_string("not required")); + + dap_list_free_full(l_voting_params->options, NULL); + DAP_DELETE(l_voting_params->question); + DAP_DELETE(l_voting_params); + } break; + case TX_ITEM_TYPE_VOTE:{ + dap_chain_tx_vote_t *l_vote_item = (dap_chain_tx_vote_t *)item; + const char *l_hash_str = dap_chain_hash_fast_to_str_static(&l_vote_item->voting_hash); + json_object_object_add(json_obj_item,"type", json_object_new_string("vote")); + json_object_object_add(json_obj_item,"voting_hash", json_object_new_string(l_hash_str)); + json_object_object_add(json_obj_item,"vote_answer_idx", json_object_new_uint64(l_vote_item->answer_idx)); + + } break; + default: + json_object_object_add(json_obj_item,"type", json_object_new_string("This transaction have unknown item type")); + break; + } + json_object_array_add(json_arr_items, json_obj_item); + } + + json_object_object_add(json_obj_out, "items", json_arr_items); + + if(a_out_json) + a_out_json = json_obj_out; + + + return 0; +} \ No newline at end of file diff --git a/modules/net/include/dap_chain_net_tx.h b/modules/net/include/dap_chain_net_tx.h index 3a189e7600..fd9ad3b61e 100644 --- a/modules/net/include/dap_chain_net_tx.h +++ b/modules/net/include/dap_chain_net_tx.h @@ -26,6 +26,22 @@ #include "dap_chain_net.h" #include "dap_chain_datum_tx_items.h" + +#include "dap_json_rpc_errors.h" + +typedef enum s_com_tx_create_json_err { + DAP_CHAIN_NET_TX_CREATE_JSON_OK = 0, + DAP_CHAIN_NET_TX_CREATE_JSON_REQUIRE_PARAMETER_JSON = DAP_JSON_RPC_ERR_CODE_METHOD_ERR_START, + DAP_CHAIN_NET_TX_CREATE_JSON_CAN_NOT_OPEN_JSON_FILE, + DAP_CHAIN_NET_TX_CREATE_JSON_WRONG_JSON_FORMAT, + DAP_CHAIN_NET_TX_CREATE_JSON_REQUIRE_PARAMETER_NET, + DAP_CHAIN_NET_TX_CREATE_JSON_NOT_FOUNT_NET_BY_NAME, + DAP_CHAIN_NET_TX_CREATE_JSON_NOT_FOUNT_CHAIN_BY_NAME, + DAP_CHAIN_NET_TX_CREATE_JSON_NOT_FOUNT_ARRAY_ITEMS, + DAP_CHAIN_NET_TX_CREATE_JSON_INVALID_ITEMS, + DAP_CHAIN_NET_TX_CREATE_JSON_CAN_NOT_ADD_TRANSACTION_TO_MEMPOOL, + DAP_CHAIN_NET_TX_CREATE_JSON_WRONG_ARGUMENTS, +}s_com_tx_create_json_err_t; typedef enum dap_chain_net_tx_search_type { /// Search local, in memory, possible load data from drive to memory TX_SEARCH_TYPE_LOCAL, @@ -93,3 +109,28 @@ void dap_chain_datum_tx_spends_items_free(dap_chain_datum_tx_spends_items_t * a_ bool dap_chain_net_tx_get_fee(dap_chain_net_id_t a_net_id, uint256_t *a_value, dap_chain_addr_t *a_addr); bool dap_chain_net_tx_set_fee(dap_chain_net_id_t a_net_id, uint256_t a_value, dap_chain_addr_t a_addr); + + +/** + * @brief Compose transaction from json. If a_net is NULL it means offline tx creation and + * tx will be created from json as is without any checks and conversions. + * @param a_tx_json input json + * @param a_net network. If NULL it means offline tx creation + * @param a_json_obj_error json object for tx items errors messages + * @param a_out_tx pointer to output transaction pointer + * @param a_items_count count of total items in input json transaction + * @param a_items_ready count of valid items in output transaction + * + * @return s_com_tx_create_json_err_t status code + */ +int dap_chain_net_tx_create_by_json(json_object *a_tx_json, dap_chain_net_t *a_net, json_object *a_json_obj_error, + dap_chain_datum_tx_t** a_out_tx, size_t* a_items_count, size_t *a_items_ready); + +/** + * @brief Convert binary transaction to json + * @param a_tx input transaction + * @param a_out_json pointer to json object created by json_object_new_object() + * + * @return s_com_tx_create_json_err_t status code + */ +int dap_chain_net_tx_to_json(dap_chain_datum_tx_t *a_tx, json_object *a_out_json); 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 7a00029d9d..4c66a15f4a 100644 --- a/modules/node-cli/dap_chain_node_cli_cmd_tx.c +++ b/modules/node-cli/dap_chain_node_cli_cmd_tx.c @@ -49,6 +49,7 @@ #include "dap_chain_wallet.h" #include "dap_chain_wallet_cache.h" #include "dap_enc_base64.h" +#include "dap_chain_net_tx.h" #define LOG_TAG "chain_node_cli_cmd_tx" @@ -1370,791 +1371,6 @@ int com_token(int a_argc, char ** a_argv, void **a_str_reply) return -DAP_CHAIN_NODE_CLI_COM_TOKEN_UNKNOWN; } -static const char* s_json_get_text(struct json_object *a_json, const char *a_key) -{ - if(!a_json || !a_key) - return NULL; - struct json_object *l_json = json_object_object_get(a_json, a_key); - if(l_json && json_object_is_type(l_json, json_type_string)) { - // Read text - return json_object_get_string(l_json); - } - return NULL; -} - -static bool s_json_get_int64(struct json_object *a_json, const char *a_key, int64_t *a_out) -{ - if(!a_json || !a_key || !a_out) - return false; - struct json_object *l_json = json_object_object_get(a_json, a_key); - if(l_json) { - if(json_object_is_type(l_json, json_type_int)) { - // Read number - *a_out = json_object_get_int64(l_json); - return true; - } - } - return false; -} - -static bool s_json_get_unit(struct json_object *a_json, const char *a_key, dap_chain_net_srv_price_unit_uid_t *a_out) -{ - const char *l_unit_str = s_json_get_text(a_json, a_key); - if(!l_unit_str || !a_out) - return false; - dap_chain_net_srv_price_unit_uid_t l_unit = dap_chain_net_srv_price_unit_uid_from_str(l_unit_str); - if(l_unit.enm == SERV_UNIT_UNDEFINED) - return false; - a_out->enm = l_unit.enm; - return true; -} - -static bool s_json_get_uint256(struct json_object *a_json, const char *a_key, uint256_t *a_out) -{ - const char *l_uint256_str = s_json_get_text(a_json, a_key); - if(!a_out || !l_uint256_str) - return false; - uint256_t l_value = dap_chain_balance_scan(l_uint256_str); - if(!IS_ZERO_256(l_value)) { - memcpy(a_out, &l_value, sizeof(uint256_t)); - return true; - } - return false; -} - -// service names: srv_stake, srv_vpn, srv_xchange -static bool s_json_get_srv_uid(struct json_object *a_json, const char *a_key_service_id, const char *a_key_service, uint64_t *a_out) -{ - uint64_t l_srv_id; - if(!a_out) - return false; - // Read service id - if(s_json_get_int64(a_json, a_key_service_id, (int64_t*) &l_srv_id)) { - *a_out = l_srv_id; - return true; - } - else { - // Read service as name - const char *l_service = s_json_get_text(a_json, a_key_service); - if (l_service) - *a_out = dap_chain_srv_get_uid_by_name(l_service).uint64; - } - return false; -} - -static dap_chain_wallet_t* s_json_get_wallet(struct json_object *a_json, const char *a_key) -{ - return dap_chain_wallet_open(s_json_get_text(a_json, a_key), dap_chain_wallet_get_path(g_config), NULL); -} - -static const dap_cert_t* s_json_get_cert(struct json_object *a_json, const char *a_key) -{ - return dap_cert_find_by_name(s_json_get_text(a_json, a_key)); -} - -// Read pkey from wallet or cert -static dap_pkey_t* s_json_get_pkey(struct json_object *a_json) -{ - dap_pkey_t *l_pub_key = NULL; - // From wallet - dap_chain_wallet_t *l_wallet = s_json_get_wallet(a_json, "wallet"); - if(l_wallet) { - l_pub_key = dap_chain_wallet_get_pkey(l_wallet, 0); - dap_chain_wallet_close(l_wallet); - if(l_pub_key) { - return l_pub_key; - } - } - // From cert - const dap_cert_t *l_cert = s_json_get_cert(a_json, "cert"); - if(l_cert) { - l_pub_key = dap_pkey_from_enc_key(l_cert->enc_key); - } - return l_pub_key; -} - -int s_json_rpc_tx_parse_json(dap_chain_net_t *a_net, dap_chain_t *a_chain, json_object *a_items, - dap_chain_datum_tx_t **a_out_tx, size_t *a_out_items_ready, json_object **a_out_jobj_error) { - size_t l_items_count = json_object_array_length(a_items); - log_it(L_NOTICE, "Json TX: found %lu items", l_items_count); - if (!l_items_count) { - dap_json_rpc_error_add(*a_out_jobj_error, DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_NOT_FOUNT_ARRAY_ITEMS, - "Wrong json format: not found array 'items' or array is empty"); - return DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_NOT_FOUNT_ARRAY_ITEMS; - } - const char *l_native_token = a_net->pub.native_ticker; - const char *l_main_token = NULL; - // Create transaction - dap_chain_datum_tx_t *l_tx = DAP_NEW_Z(dap_chain_datum_tx_t); - if(!l_tx) { - dap_json_rpc_allocation_error(*a_out_jobj_error); - return DAP_JSON_RPC_ERR_CODE_MEMORY_ALLOCATED; - } - l_tx->header.ts_created = time(NULL); - 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 - 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); - 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; - } - - log_it(L_DEBUG, "Json TX: process item %s", json_object_get_string(l_json_item_type)); - // Create an item depending on its type - const uint8_t *l_item = NULL; - switch (l_item_type) { - case TX_ITEM_TYPE_IN: { - // Save item obj for in - l_in_list = dap_list_append(l_in_list, l_json_item_obj); - // Read prev_hash and out_prev_idx - const char *l_prev_hash_str = s_json_get_text(l_json_item_obj, "prev_hash"); - int64_t l_out_prev_idx; - bool l_is_out_prev_idx = s_json_get_int64(l_json_item_obj, "out_prev_idx", &l_out_prev_idx); - // If prev_hash and out_prev_idx were read - if(l_prev_hash_str && l_is_out_prev_idx) { - dap_chain_hash_fast_t l_tx_prev_hash; - if(!dap_chain_hash_fast_from_str(l_prev_hash_str, &l_tx_prev_hash)) { - // Create IN item - dap_chain_tx_in_t *l_in_item = dap_chain_datum_tx_item_in_create(&l_tx_prev_hash, (uint32_t) l_out_prev_idx); - if (!l_in_item) { - json_object *l_jobj_err = json_object_new_string("Unable to create in for transaction."); - json_object_array_add(l_jobj_errors, l_jobj_err); - } - l_item = (const uint8_t*) l_in_item; - } else { - log_it(L_WARNING, "Invalid 'in' item, bad prev_hash %s", l_prev_hash_str); - char *l_str_err = dap_strdup_printf("Unable to create in for transaction. Invalid 'in' item, " - "bad prev_hash %s", l_prev_hash_str); - json_object *l_jobj_err = json_object_new_string(l_str_err); - json_object_array_add(l_jobj_errors, l_jobj_err); - } - } - // Read addr_from - else { - l_in_list = dap_list_append(l_in_list, l_json_item_obj); - } - } - break; - - case TX_ITEM_TYPE_OUT: - case TX_ITEM_TYPE_OUT_EXT: { - // Read address and value - 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 - 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; - 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 - if(l_token) { - // Create 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_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; - } - } - } else { - if(l_item_type == TX_ITEM_TYPE_OUT) { - log_it(L_WARNING, "Invalid 'out' item %zu", i); - } - else if(l_item_type == TX_ITEM_TYPE_OUT_EXT) { - log_it(L_WARNING, "Invalid 'out_ext' item %zu", i); - } - char *l_str_err = dap_strdup_printf("For item %zu of type 'out' or 'out_ext' the " - "string representation of the address could not be converted, " - "or the size of the output sum is 0.", i); - json_object *l_jobj_err = json_object_new_string(l_str_err); - DAP_DELETE(l_str_err); - json_object_array_add(l_jobj_errors, l_jobj_err); - continue; - } - } - } - break; - case TX_ITEM_TYPE_OUT_COND: { - // Read subtype of item - const char *l_subtype_str = s_json_get_text(l_json_item_obj, "subtype"); - dap_chain_tx_out_cond_subtype_t l_subtype = dap_chain_tx_out_cond_subtype_from_str(l_subtype_str); - switch (l_subtype) { - - case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY:{ - uint256_t l_value = { }; - bool l_is_value = s_json_get_uint256(l_json_item_obj, "value", &l_value); - if(!l_is_value || IS_ZERO_256(l_value)) { - log_it(L_ERROR, "Json TX: bad value in OUT_COND_SUBTYPE_SRV_PAY"); - break; - } - uint256_t l_value_max_per_unit = { }; - l_is_value = s_json_get_uint256(l_json_item_obj, "value_max_per_unit", &l_value_max_per_unit); - if(!l_is_value || IS_ZERO_256(l_value_max_per_unit)) { - log_it(L_ERROR, "Json TX: bad value_max_per_unit in OUT_COND_SUBTYPE_SRV_PAY"); - break; - } - dap_chain_net_srv_price_unit_uid_t l_price_unit; - if(!s_json_get_unit(l_json_item_obj, "price_unit", &l_price_unit)) { - log_it(L_ERROR, "Json TX: bad price_unit in OUT_COND_SUBTYPE_SRV_PAY"); - break; - } - // Default service DAP_CHAIN_NET_SRV_VPN_ID - uint64_t l_64 = 0x0000000000000001; - s_json_get_srv_uid(l_json_item_obj, "service_id", "service", &l_64); - dap_chain_srv_uid_t l_srv_uid = { .uint64 = l_64}; - // From "wallet" or "cert" - dap_pkey_t *l_pkey = s_json_get_pkey(l_json_item_obj); - if(!l_pkey) { - log_it(L_ERROR, "Json TX: bad pkey in OUT_COND_SUBTYPE_SRV_PAY"); - break; - } - const char *l_params_str = s_json_get_text(l_json_item_obj, "params"); - size_t l_params_size = dap_strlen(l_params_str); - dap_chain_tx_out_cond_t *l_out_cond_item = dap_chain_datum_tx_item_out_cond_create_srv_pay(l_pkey, l_srv_uid, l_value, l_value_max_per_unit, - l_price_unit, l_params_str, l_params_size); - l_item = (const uint8_t*) l_out_cond_item; - // Save value for using in In item - if(l_item) { - SUM_256_256(l_value_need, l_value, &l_value_need); - } else { - char *l_str_err = dap_strdup_printf("Unable to create conditional out for transaction " - "can of type %s described in item %zu.\n", l_subtype_str, i); - json_object *l_jobj_err = json_object_new_string(l_str_err); - DAP_DELETE(l_str_err); - json_object_array_add(l_jobj_errors, l_jobj_err); - } - DAP_DELETE(l_pkey); - } - break; - case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE: { - // Default service DAP_CHAIN_NET_SRV_XCHANGE_ID - uint64_t l_64 = 0x0000000000000002; - s_json_get_srv_uid(l_json_item_obj, "service_id", "service", &l_64); - dap_chain_srv_uid_t l_srv_uid = { .uint64 = l_64}; - dap_chain_net_t *l_net = dap_chain_net_by_name(s_json_get_text(l_json_item_obj, "net")); - if(!l_net) { - log_it(L_ERROR, "Json TX: bad net in OUT_COND_SUBTYPE_SRV_XCHANGE"); - break; - } - const char *l_token = s_json_get_text(l_json_item_obj, "token"); - if(!l_token) { - log_it(L_ERROR, "Json TX: bad token in OUT_COND_SUBTYPE_SRV_XCHANGE"); - break; - } - uint256_t l_value = { }; - if(!s_json_get_uint256(l_json_item_obj, "value", &l_value) || IS_ZERO_256(l_value)) { - log_it(L_ERROR, "Json TX: bad value in OUT_COND_SUBTYPE_SRV_XCHANGE"); - break; - } - //const char *l_params_str = s_json_get_text(l_json_item_obj, "params"); - //size_t l_params_size = dap_strlen(l_params_str); - dap_chain_tx_out_cond_t *l_out_cond_item = NULL; //dap_chain_datum_tx_item_out_cond_create_srv_xchange(l_srv_uid, l_net->pub.id, l_token, l_value, l_params_str, l_params_size); - l_item = (const uint8_t*) l_out_cond_item; - // Save value for using in In item - if(l_item) { - SUM_256_256(l_value_need, l_value, &l_value_need); - } else { - char *l_str_err = dap_strdup_printf("Unable to create conditional out for transaction " - "can of type %s described in item %zu.", l_subtype_str, i); - json_object *l_jobj_err = json_object_new_string(l_str_err); - DAP_DELETE(l_str_err); - json_object_array_add(l_jobj_errors, l_jobj_err); - } - } - break; - case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE:{ - // Default service DAP_CHAIN_NET_SRV_STAKE_ID - uint64_t l_64 = 0x0000000000000013; - s_json_get_srv_uid(l_json_item_obj, "service_id", "service", &l_64); - dap_chain_srv_uid_t l_srv_uid = { .uint64 = l_64}; - uint256_t l_value = { }; - if(!s_json_get_uint256(l_json_item_obj, "value", &l_value) || IS_ZERO_256(l_value)) { - log_it(L_ERROR, "Json TX: bad value in OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE"); - break; - } - uint256_t l_fee_value = { }; - if(!s_json_get_uint256(l_json_item_obj, "fee", &l_fee_value) || IS_ZERO_256(l_fee_value)) { - break; - } - const char *l_signing_addr_str = s_json_get_text(l_json_item_obj, "signing_addr"); - dap_chain_addr_t *l_signing_addr = dap_chain_addr_from_str(l_signing_addr_str); - if(!l_signing_addr) { - { - log_it(L_ERROR, "Json TX: bad signing_addr in OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE"); - break; - } - dap_chain_node_addr_t l_signer_node_addr; - const char *l_node_addr_str = s_json_get_text(l_json_item_obj, "node_addr"); - if(!l_node_addr_str || dap_chain_node_addr_from_str(&l_signer_node_addr, l_node_addr_str)) { - log_it(L_ERROR, "Json TX: bad node_addr in OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE"); - break; - } - // Maybe should get pkey and tronsform to dap_pkey_t - dap_pkey_t *l_pkey = NULL; - const char *l_pkey_full_str = s_json_get_text(l_json_item_obj, "pkey_full"); - if(l_pkey_full_str) { - l_pkey = dap_pkey_get_from_str(l_pkey_full_str); - debug_if(!l_pkey, L_ERROR, "Json TX: bad pkey in OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE"); - } - - dap_chain_tx_out_cond_t *l_out_cond_item = dap_chain_datum_tx_item_out_cond_create_srv_stake(l_srv_uid, l_value, l_signing_addr, - &l_signer_node_addr, NULL, uint256_0, l_pkey); - l_item = (const uint8_t*) l_out_cond_item; - // Save value for using in In item - if(l_item) { - SUM_256_256(l_value_need, l_value, &l_value_need); - } else { - char *l_err_str = dap_strdup_printf("Unable to create conditional out for transaction " - "can of type %s described in item %zu.", l_subtype_str, i); - json_object *l_jobj_err = json_object_new_string(l_err_str); - DAP_DELETE(l_err_str); - json_object_array_add(l_jobj_errors, l_jobj_err); - } - } - } - break; - case DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE: { - uint256_t l_value = { }; - s_json_get_uint256(l_json_item_obj, "value", &l_value); - if(!IS_ZERO_256(l_value)) { - dap_chain_tx_out_cond_t *l_out_cond_item = dap_chain_datum_tx_item_out_cond_create_fee(l_value); - l_item = (const uint8_t*) l_out_cond_item; - // Save value for using in In item - if(l_item) { - SUM_256_256(l_value_need_fee, l_value, &l_value_need_fee); - } else { - char *l_str_err = dap_strdup_printf("Unable to create conditional out for transaction " - "can of type %s described in item %zu.", l_subtype_str, i); - json_object *l_jobj_err = json_object_new_string(l_str_err); - json_object_array_add(l_jobj_errors, l_jobj_err); - DAP_DELETE(l_str_err); - } - } - else - log_it(L_ERROR, "Json TX: zero value in OUT_COND_SUBTYPE_FEE"); - } - break; - case DAP_CHAIN_TX_OUT_COND_SUBTYPE_UNDEFINED: - log_it(L_WARNING, "Undefined subtype: '%s' of 'out_cond' item %zu ", l_subtype_str, i); - char *l_str_err = dap_strdup_printf("Specified unknown sub type %s of conditional out on item %zu.", - l_subtype_str, i); - json_object *l_jobj_err = json_object_new_string(l_str_err); - DAP_DELETE(l_str_err); - json_object_array_add(l_jobj_errors, l_jobj_err); - break; - } - } - - break; - case TX_ITEM_TYPE_SIG:{ - const char *l_sign_type_str = s_json_get_text(l_json_item_obj, "sig_type"); - if (l_sign_type_str) { - int64_t l_pkey_size; - int64_t l_sig_size; - int64_t l_hash_type = 0; - dap_sign_t *l_sign = NULL; - dap_sign_type_t l_type = dap_sign_type_from_str(l_sign_type_str); - - s_json_get_int64(l_json_item_obj, "hash_type", &l_hash_type); - s_json_get_int64(l_json_item_obj, "pub_key_size", &l_pkey_size); - s_json_get_int64(l_json_item_obj, "sig_size", &l_sig_size); - if (l_pkey_size == 0 || l_sig_size == 0){ - json_object *l_jobj_err = json_object_new_string("Can't get sign for transactions. Sign or pkey length is 0."); - json_object_array_add(l_jobj_errors, l_jobj_err); - log_it(L_ERROR, "Json TX: Can't get sign for transactions. Sign or pkey length is 0."); - break; - } - - l_sign = DAP_NEW_Z_SIZE(dap_sign_t, sizeof(dap_sign_t) + l_pkey_size + l_sig_size); - - - json_object *l_jobj_sign = json_object_object_get(l_json_item_obj, "sig_b64"); - const char *l_sign_str = json_object_get_string(l_jobj_sign); - void *l_sig_buf = DAP_NEW_SIZE(void,DAP_ENC_BASE64_ENCODE_SIZE(l_sig_size)); - size_t l_sign_decoded_size = dap_enc_base64_decode(l_sign_str, dap_strlen(l_sign_str), l_sig_buf, DAP_ENC_DATA_TYPE_B64_URLSAFE); - - - json_object *l_jobj_pub_key = json_object_object_get(l_json_item_obj, "pub_key_b64"); - const char *l_pub_key_str = json_object_get_string(l_jobj_pub_key); - void *l_pkey_buf = DAP_NEW_SIZE(void,DAP_ENC_BASE64_ENCODE_SIZE(l_pkey_size)); - size_t l_pkey_decoded_size = dap_enc_base64_decode(l_pub_key_str, strlen(l_pub_key_str), l_pkey_buf, DAP_ENC_DATA_TYPE_B64_URLSAFE); - - l_sign->header.sign_pkey_size = l_pkey_size; - l_sign->header.sign_size = l_sig_size; - l_sign->header.type = l_type; - l_sign->header.hash_type = (uint8_t)l_hash_type; - memcpy(l_sign->pkey_n_sign, l_pkey_buf, l_pkey_decoded_size); - memcpy(l_sign->pkey_n_sign + l_pkey_decoded_size, l_sig_buf, l_sign_decoded_size); - DAP_DELETE(l_pkey_buf); - DAP_DELETE(l_sig_buf); - - size_t l_chain_sign_size = dap_sign_get_size(l_sign); // sign data - - dap_chain_tx_sig_t *l_tx_sig = DAP_NEW_Z_SIZE(dap_chain_tx_sig_t, - sizeof(dap_chain_tx_sig_t) + l_chain_sign_size); - l_tx_sig->header.type = TX_ITEM_TYPE_SIG; - l_tx_sig->header.sig_size =(uint32_t) l_chain_sign_size; - memcpy(l_tx_sig->sig, l_sign, l_chain_sign_size); - // dap_chain_datum_tx_add_item(&l_tx, l_tx_sig); - l_item = (const uint8_t*)l_tx_sig; - DAP_DELETE(l_sign); - break; - } - else - l_sign_list = dap_list_append(l_sign_list,l_json_item_obj); - } - break; - case TX_ITEM_TYPE_RECEIPT: { - uint64_t l_64 = 0; - if (!s_json_get_srv_uid(l_json_item_obj, "service_id", "service", &l_64)) { - log_it(L_ERROR, "Json TX: bad service_id in TYPE_RECEIPT"); - break; - } - dap_chain_srv_uid_t l_srv_uid = { .uint64 = l_64 }; - dap_chain_net_srv_price_unit_uid_t l_price_unit; - if(!s_json_get_unit(l_json_item_obj, "price_unit", &l_price_unit)) { - log_it(L_ERROR, "Json TX: bad price_unit in TYPE_RECEIPT"); - break; - } - int64_t l_units; - if(!s_json_get_int64(l_json_item_obj, "units", &l_units)) { - log_it(L_ERROR, "Json TX: bad units in TYPE_RECEIPT"); - break; - } - uint256_t l_value = { }; - if(!s_json_get_uint256(l_json_item_obj, "value", &l_value) || IS_ZERO_256(l_value)) { - log_it(L_ERROR, "Json TX: bad value in TYPE_RECEIPT"); - break; - } - const char *l_params_str = s_json_get_text(l_json_item_obj, "params"); - size_t l_params_size = dap_strlen(l_params_str); - dap_chain_datum_tx_receipt_t *l_receipt = dap_chain_datum_tx_receipt_create(l_srv_uid, l_price_unit, l_units, l_value, l_params_str, l_params_size); - l_item = (const uint8_t*) l_receipt; - if (!l_item) { - char *l_str_err = dap_strdup_printf("Unable to create receipt out for transaction " - "described by item %zu.", i); - json_object *l_jobj_err = json_object_new_string(l_str_err); - DAP_DELETE(l_str_err); - json_object_array_add(l_jobj_errors, l_jobj_err); - } - } - break; - case TX_ITEM_TYPE_TSD: { - int64_t l_tsd_type; - if(!s_json_get_int64(l_json_item_obj, "type_tsd", &l_tsd_type)) { - log_it(L_ERROR, "Json TX: bad type_tsd in TYPE_TSD"); - break; - } - const char *l_tsd_data = s_json_get_text(l_json_item_obj, "data"); - if (!l_tsd_data) { - log_it(L_ERROR, "Json TX: bad data in TYPE_TSD"); - break; - } - size_t l_data_size = dap_strlen(l_tsd_data); - dap_chain_tx_tsd_t *l_tsd = dap_chain_datum_tx_item_tsd_create((void*)l_tsd_data, (int)l_tsd_type, l_data_size); - l_item = (const uint8_t*) l_tsd; - } - break; - //case TX_ITEM_TYPE_PKEY: - //break; - //case TX_ITEM_TYPE_IN_EMS: - //break; - //case TX_ITEM_TYPE_IN_EMS_EXT: - //break; - } - // Add item to transaction - if(l_item) { - dap_chain_datum_tx_add_item(&l_tx, (const uint8_t*) l_item); - l_items_ready++; - DAP_DELETE(l_item); - } - } - - dap_list_t *l_list; - // Add In items - l_list = l_in_list; - while(l_list) { - struct json_object *l_json_item_obj = (struct json_object*) l_list->data; - // Read prev_hash and out_prev_idx - const char *l_json_item_addr_str = s_json_get_text(l_json_item_obj, "addr_from"); - const char *l_json_item_token = s_json_get_text(l_json_item_obj, "token"); - l_main_token = l_json_item_token; - dap_chain_addr_t *l_addr_from = NULL; - if(l_json_item_addr_str) { - l_addr_from = dap_chain_addr_from_str(l_json_item_addr_str); - if (!l_addr_from) { - log_it(L_WARNING, "Invalid element 'in', unable to convert string representation of addr_from: '%s' " - "to binary.", l_json_item_addr_str); - char *l_str_err = dap_strdup_printf("Invalid element 'to', unable to convert string representation " - "of addr_from: '%s' to binary.", l_json_item_addr_str); - json_object *l_jobj_err = json_object_new_string(l_str_err); - DAP_DELETE(l_str_err); - json_object_array_add(l_jobj_errors, l_jobj_err); - // Go to the next item - l_list = dap_list_next(l_list); - continue; - } - } - else { - log_it(L_WARNING, "Invalid 'in' item, incorrect addr_from: '%s'", l_json_item_addr_str ? l_json_item_addr_str : "[null]"); - char *l_str_err = dap_strdup_printf("Invalid 'in' item, incorrect addr_from: '%s'", - l_json_item_addr_str ? l_json_item_addr_str : "[null]"); - json_object *l_jobj_err = json_object_new_string(l_str_err); - DAP_DELETE(l_str_err); - json_object_array_add(l_jobj_errors, l_jobj_err); - // Go to the next item - l_list = dap_list_next(l_list); - continue; - } - if(!l_json_item_token) { - log_it(L_WARNING, "Invalid 'in' item, not found token name"); - json_object *l_jobj_err = json_object_new_string("Invalid 'in' item, not found token name"); - json_object_array_add(l_jobj_errors, l_jobj_err); - // Go to the next item - l_list = dap_list_next(l_list); - continue; - } - if(IS_ZERO_256(l_value_need)) { - log_it(L_WARNING, "Invalid 'in' item, not found value in out items"); - json_object *l_jobj_err = json_object_new_string("Invalid 'in' item, not found value in out items"); - json_object_array_add(l_jobj_errors, l_jobj_err); - // Go to the next item - l_list = dap_list_next(l_list); - continue; - } - if(l_addr_from) - { - // find the transactions from which to take away coins - dap_list_t *l_list_used_out = NULL; - dap_list_t *l_list_used_out_fee = NULL; - uint256_t l_value_transfer = { }; // how many coins to transfer - uint256_t l_value_transfer_fee = { }; // how many coins to transfer - //SUM_256_256(a_value, a_value_fee, &l_value_need); - uint256_t l_value_need_check = {}; - if (!dap_strcmp(l_native_token, l_main_token)) { - SUM_256_256(l_value_need_check, l_value_need, &l_value_need_check); - SUM_256_256(l_value_need_check, l_value_need_fee, &l_value_need_check); - l_list_used_out = dap_chain_wallet_get_list_tx_outs_with_val(a_net->pub.ledger, l_json_item_token, - l_addr_from, l_value_need_check, &l_value_transfer); - if(!l_list_used_out) { - log_it(L_WARNING, "Not enough funds in previous tx to transfer"); - json_object *l_jobj_err = json_object_new_string("Can't create in transaction. Not enough funds in previous tx " - "to transfer"); - json_object_array_add(l_jobj_errors, l_jobj_err); - // Go to the next item - l_list = dap_list_next(l_list); - continue; - } - } else { - //CHECK value need - l_list_used_out = dap_chain_wallet_get_list_tx_outs_with_val(a_net->pub.ledger, l_json_item_token, - l_addr_from, l_value_need, &l_value_transfer); - if(!l_list_used_out) { - log_it(L_WARNING, "Not enough funds in previous tx to transfer"); - json_object *l_jobj_err = json_object_new_string("Can't create in transaction. Not enough funds " - "in previous tx to transfer"); - json_object_array_add(l_jobj_errors, l_jobj_err); - // Go to the next item - l_list = dap_list_next(l_list); - continue; - } - //CHECK value fee - l_list_used_out_fee = dap_chain_wallet_get_list_tx_outs_with_val(a_net->pub.ledger, l_native_token, - l_addr_from, l_value_need_fee, &l_value_transfer_fee); - if(!l_list_used_out_fee) { - log_it(L_WARNING, "Not enough funds in previous tx to transfer"); - json_object *l_jobj_err = json_object_new_string("Can't create in transaction. Not enough funds " - "in previous tx to transfer"); - json_object_array_add(l_jobj_errors, l_jobj_err); - // Go to the next item - l_list = dap_list_next(l_list); - continue; - } - } - // add 'in' items - uint256_t l_value_got = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out); - assert(EQUAL_256(l_value_got, l_value_transfer)); - if (l_list_used_out_fee) { - uint256_t l_value_got_fee = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out_fee); - assert(EQUAL_256(l_value_got_fee, l_value_transfer_fee)); - dap_list_free_full(l_list_used_out_fee, free); - // add 'out' item for coin fee back - uint256_t l_value_back; - SUBTRACT_256_256(l_value_got_fee, l_value_need_fee, &l_value_back); - if (!IS_ZERO_256(l_value_back)) { - dap_chain_datum_tx_add_out_ext_item(&l_tx, l_addr_from, l_value_back, l_native_token); - } - } else { - SUM_256_256(l_value_need, l_value_need_fee, &l_value_need); - } - dap_list_free_full(l_list_used_out, free); - if(!IS_ZERO_256(l_value_got)) { - l_items_ready++; - - // add 'out' item for coin back - uint256_t l_value_back; - SUBTRACT_256_256(l_value_got, l_value_need, &l_value_back); - if(!IS_ZERO_256(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); - } - } - } - // Go to the next 'in' item - l_list = dap_list_next(l_list); - } - dap_list_free(l_in_list); - - // Add signs - l_list = l_sign_list; - while(l_list) { - - struct json_object *l_json_item_obj = (struct json_object*) l_list->data; - - dap_enc_key_t * l_enc_key = NULL; - - //get wallet or cert - dap_chain_wallet_t *l_wallet = s_json_get_wallet(l_json_item_obj, "wallet"); - const dap_cert_t *l_cert = s_json_get_cert(l_json_item_obj, "cert"); - - //wallet goes first - if (l_wallet) { - l_enc_key = dap_chain_wallet_get_key(l_wallet, 0); - - } else if (l_cert && l_cert->enc_key) { - l_enc_key = l_cert->enc_key; - } - else{ - json_object *l_jobj_err = json_object_new_string("Can't create sign for transactions."); - json_object_array_add(l_jobj_errors, l_jobj_err); - log_it(L_ERROR, "Json TX: Item sign has no wallet or cert of they are invalid "); - l_list = dap_list_next(l_list); - continue; - } - - if(l_enc_key && dap_chain_datum_tx_add_sign_item(&l_tx, l_enc_key) > 0) { - l_items_ready++; - } else { - log_it(L_ERROR, "Json TX: Item sign has invalid enc_key."); - l_list = dap_list_next(l_list); - continue; - } - - if (l_wallet) { - dap_chain_wallet_close(l_wallet); - dap_enc_key_delete(l_enc_key); - } - - - l_list = dap_list_next(l_list); - } - - dap_list_free(l_sign_list); - - *a_out_items_ready = l_items_ready; - - if(l_items_ready<l_items_count) { - *a_out_jobj_error = l_jobj_errors; - DAP_DELETE(l_tx); - return DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_INVALID_ITEMS; - } - json_object_put(l_jobj_errors); - *a_out_tx = l_tx; - - return 0; -} - /** * @breif Create transaction from json rpc request * @param a_param @@ -2168,7 +1384,7 @@ void json_rpc_tx_create(json_object *a_param, json_object *a_reply){ l_net_name = json_object_get_string(l_json_net); } if(!l_net_name) { - dap_json_rpc_error_add(a_reply, DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_REQUIRE_PARAMETER_NET, + dap_json_rpc_error_add(a_reply, DAP_CHAIN_NET_TX_CREATE_JSON_REQUIRE_PARAMETER_NET, "Command requires parameter '-net' or set net in the json-rpc request"); return; } @@ -2179,7 +1395,7 @@ void json_rpc_tx_create(json_object *a_param, json_object *a_reply){ dap_chain_net_t *l_net = dap_chain_net_by_name(l_net_name); if (!l_net) { dap_json_rpc_error_add(a_reply, - DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_NOT_FOUNT_NET_BY_NAME, + DAP_CHAIN_NET_TX_CREATE_JSON_NOT_FOUNT_NET_BY_NAME, "Not found net by name '%s'", l_net_name); return; } @@ -2189,25 +1405,25 @@ void json_rpc_tx_create(json_object *a_param, json_object *a_reply){ } else { l_chain = dap_chain_net_get_chain_by_name(l_net, l_chain_name); if (!l_chain){ - dap_json_rpc_error_add(a_reply, DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_NOT_FOUNT_CHAIN_BY_NAME, + dap_json_rpc_error_add(a_reply, DAP_CHAIN_NET_TX_CREATE_JSON_NOT_FOUNT_CHAIN_BY_NAME, "Chain name '%s' not found, try use parameter '-chain' or set chain in the json-rpc request", l_chain_name); return; } } + + json_object *l_jobj_errors = json_object_new_array(); + size_t l_items_ready = 0, l_items_count = 0; dap_chain_datum_tx_t *l_tx = NULL; - json_object *l_jobj_items = json_object_object_get(a_param, "items"); - if (!l_jobj_items) { - dap_json_rpc_error_add(a_reply, DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_NOT_FOUNT_ARRAY_ITEMS, - "Items with description transaction not found in JSON."); - return ; + int l_ret = 0; + if((l_ret = dap_chain_net_tx_create_by_json(a_param, l_net, l_jobj_errors, &l_tx, &l_items_count, &l_items_ready)) != DAP_CHAIN_NET_TX_CREATE_JSON_OK) { + dap_json_rpc_error_add(a_reply, l_ret, + "Can't create transaction from json file"); + return; } - size_t l_items_ready = 0; - json_object *l_jobj_errors = NULL; - int res = s_json_rpc_tx_parse_json(l_net, l_chain, l_jobj_items, &l_tx, &l_items_ready, &l_jobj_errors); - if (res) { + if (l_items_ready < l_items_count) { json_object *l_jobj_tx_create = json_object_new_boolean(false); json_object *l_jobj_items_ready = json_object_new_uint64(l_items_ready); - json_object *l_jobj_total_items = json_object_new_uint64(json_object_array_length(l_jobj_items)); + json_object *l_jobj_total_items = json_object_new_uint64(l_items_count); json_object_object_add(a_reply, "tx_create", l_jobj_tx_create); json_object_object_add(a_reply, "ready_items", l_jobj_items_ready); json_object_object_add(a_reply, "total_items", l_jobj_total_items); @@ -2229,7 +1445,7 @@ void json_rpc_tx_create(json_object *a_param, json_object *a_reply){ if ((rc = dap_ledger_tx_add_check(l_net->pub.ledger, (dap_chain_datum_tx_t*)l_datum_tx->data, l_tx_size, &l_hf_tx))) { json_object *l_jobj_tx_create = json_object_new_boolean(false); json_object *l_jobj_hash = json_object_new_string(l_tx_hash_str); - json_object *l_jobj_total_items = json_object_new_uint64(json_object_array_length(l_jobj_items)); + json_object *l_jobj_total_items = json_object_new_uint64(l_items_count); json_object *l_jobj_ledger_ret_code = json_object_new_object(); json_object_object_add(l_jobj_ledger_ret_code, "code", json_object_new_int(rc)); json_object_object_add(l_jobj_ledger_ret_code, "message", @@ -2248,14 +1464,14 @@ void json_rpc_tx_create(json_object *a_param, json_object *a_reply){ DAP_DELETE(l_datum_tx); DAP_DELETE(l_gdb_group_mempool_base_tx); if(!l_placed) { - dap_json_rpc_error_add(a_reply, DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_CAN_NOT_ADD_TRANSACTION_TO_MEMPOOL, + dap_json_rpc_error_add(a_reply, DAP_CHAIN_NET_TX_CREATE_JSON_CAN_NOT_ADD_TRANSACTION_TO_MEMPOOL, "Can't add transaction to mempool"); return ; } // Completed successfully json_object *l_jobj_tx_create = json_object_new_boolean(true); json_object *l_jobj_hash = json_object_new_string(l_tx_hash_str); - json_object *l_jobj_total_items = json_object_new_uint64(json_object_array_length(l_jobj_items)); + json_object *l_jobj_total_items = json_object_new_uint64(l_items_count); json_object_object_add(a_reply, "tx_create", l_jobj_tx_create); json_object_object_add(a_reply, "hash", l_jobj_hash); json_object_object_add(a_reply, "total_items", l_jobj_total_items); @@ -2283,21 +1499,21 @@ int com_tx_create_json(int a_argc, char ** a_argv, void **reply) dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-json", &l_json_file_path); if(!l_json_file_path) { - dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_REQUIRE_PARAMETER_JSON, + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NET_TX_CREATE_JSON_REQUIRE_PARAMETER_JSON, "Command requires one of parameters '-json <json file path>'"); - return DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_REQUIRE_PARAMETER_JSON; + return DAP_CHAIN_NET_TX_CREATE_JSON_REQUIRE_PARAMETER_JSON; } // Open json file struct json_object *l_json = json_object_from_file(l_json_file_path); if(!l_json) { - dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_CAN_NOT_OPEN_JSON_FILE, + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NET_TX_CREATE_JSON_CAN_NOT_OPEN_JSON_FILE, "Can't open json file: %s", json_util_get_last_err()); - return DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_CAN_NOT_OPEN_JSON_FILE; + return DAP_CHAIN_NET_TX_CREATE_JSON_CAN_NOT_OPEN_JSON_FILE; } if(!json_object_is_type(l_json, json_type_object)) { - dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_WRONG_JSON_FORMAT, "Wrong json format"); + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NET_TX_CREATE_JSON_WRONG_JSON_FORMAT, "Wrong json format"); json_object_put(l_json); - return DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_WRONG_JSON_FORMAT; + return DAP_CHAIN_NET_TX_CREATE_JSON_WRONG_JSON_FORMAT; } @@ -2308,19 +1524,19 @@ int com_tx_create_json(int a_argc, char ** a_argv, void **reply) l_net_name = json_object_get_string(l_json_net); } if(!l_net_name) { - dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_REQUIRE_PARAMETER_NET, + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NET_TX_CREATE_JSON_REQUIRE_PARAMETER_NET, "Command requires parameter '-net' or set net in the json file"); json_object_put(l_json); - return DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_REQUIRE_PARAMETER_NET; + return DAP_CHAIN_NET_TX_CREATE_JSON_REQUIRE_PARAMETER_NET; } } dap_chain_net_t *l_net = dap_chain_net_by_name(l_net_name); if(!l_net) { dap_json_rpc_error_add(*a_json_arr_reply, - DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_NOT_FOUNT_NET_BY_NAME, + DAP_CHAIN_NET_TX_CREATE_JSON_NOT_FOUNT_NET_BY_NAME, "Not found net by name '%s'", l_net_name); json_object_put(l_json); - return DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_NOT_FOUNT_NET_BY_NAME; + return DAP_CHAIN_NET_TX_CREATE_JSON_NOT_FOUNT_NET_BY_NAME; } // Read chain from json file @@ -2336,37 +1552,39 @@ int com_tx_create_json(int a_argc, char ** a_argv, void **reply) } if(!l_chain) { dap_json_rpc_error_add(*a_json_arr_reply, - DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_NOT_FOUNT_CHAIN_BY_NAME, + DAP_CHAIN_NET_TX_CREATE_JSON_NOT_FOUNT_CHAIN_BY_NAME, "Chain name '%s' not found, try use parameter '-chain' or set chain in the json file", l_chain_name); json_object_put(l_json); - return DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_NOT_FOUNT_CHAIN_BY_NAME; + return DAP_CHAIN_NET_TX_CREATE_JSON_NOT_FOUNT_CHAIN_BY_NAME; } // Read items from json file - struct json_object *l_json_items = json_object_object_get(l_json, "items"); + json_object *l_jobj_errors = json_object_new_array(); + size_t l_items_ready = 0, l_items_count = 0; dap_chain_datum_tx_t *l_tx = NULL; - - size_t l_items_ready = 0; - json_object *l_jobj_errors = NULL; - - int res = s_json_rpc_tx_parse_json(l_net, l_chain, l_json_items, &l_tx, &l_items_ready, &l_jobj_errors); + int l_ret = 0; + if((l_ret = dap_chain_net_tx_create_by_json(l_json, l_net, l_jobj_errors, &l_tx, &l_items_count, &l_items_ready)) != DAP_CHAIN_NET_TX_CREATE_JSON_OK) { + dap_json_rpc_error_add(*a_json_arr_reply, l_ret, + "Can't create transaction from json file"); + json_object_put(l_json); + return l_ret; + } json_object *l_jobj_ret = json_object_new_object(); - if (res) { - json_object *l_jobj_tx_create = json_object_new_boolean(false); - json_object *l_jobj_items_ready = json_object_new_uint64(l_items_ready); - json_object *l_jobj_total_items = json_object_new_uint64(json_object_array_length(l_json_items)); - json_object_object_add(l_jobj_ret, "tx_create", l_jobj_tx_create); - json_object_object_add(l_jobj_ret, "ready_items", l_jobj_items_ready); + if(l_items_ready < l_items_count) { + json_object *l_tx_create = json_object_new_boolean(false); + json_object *l_jobj_valid_items = json_object_new_uint64(l_items_ready); + json_object *l_jobj_total_items = json_object_new_uint64(l_items_count); + json_object_object_add(l_jobj_ret, "tx_create", l_tx_create); + json_object_object_add(l_jobj_ret, "valid_items", l_jobj_valid_items); json_object_object_add(l_jobj_ret, "total_items", l_jobj_total_items); json_object_object_add(l_jobj_ret, "errors", l_jobj_errors); - json_object_array_add(*reply, l_jobj_ret); json_object_array_add(*a_json_arr_reply, l_jobj_ret); DAP_DELETE(l_tx); - return DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_INVALID_ITEMS; + return DAP_CHAIN_NET_TX_CREATE_JSON_INVALID_ITEMS; } - + json_object_put(l_jobj_errors); // Pack transaction into the datum size_t l_tx_size = dap_chain_datum_tx_get_size(l_tx); @@ -2382,7 +1600,7 @@ int com_tx_create_json(int a_argc, char ** a_argv, void **reply) if ((rc = dap_ledger_tx_add_check(l_net->pub.ledger, (dap_chain_datum_tx_t*)l_datum_tx->data, l_tx_size, &l_hf_tx))) { json_object *l_jobj_tx_create = json_object_new_boolean(false); json_object *l_jobj_hash = json_object_new_string(l_tx_hash_str); - json_object *l_jobj_total_items = json_object_new_uint64(json_object_array_length(l_json_items)); + json_object *l_jobj_total_items = json_object_new_uint64(l_items_count); json_object *l_jobj_ledger_ret_code = json_object_new_object(); json_object_object_add(l_jobj_ledger_ret_code, "code", json_object_new_int(rc)); json_object_object_add(l_jobj_ledger_ret_code, "message", @@ -2393,7 +1611,7 @@ int com_tx_create_json(int a_argc, char ** a_argv, void **reply) json_object_object_add(l_jobj_ret, "total_items", l_jobj_total_items); json_object_array_add(*a_json_arr_reply, l_jobj_ret); DAP_DEL_Z(l_datum_tx); - return DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_CAN_CHECK_TX_ADD_LEDGER; + return DAP_CHAIN_NODE_CLI_COM_TX_CREATE_CAN_NOT_CREATE_TRANSACTION; } char *l_gdb_group_mempool_base_tx = dap_chain_mempool_group_new(l_chain);// get group name for mempool @@ -2402,19 +1620,19 @@ int com_tx_create_json(int a_argc, char ** a_argv, void **reply) DAP_DEL_Z(l_datum_tx); DAP_DELETE(l_gdb_group_mempool_base_tx); if(!l_placed) { - dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_CAN_NOT_ADD_TRANSACTION_TO_MEMPOOL, + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NET_TX_CREATE_JSON_CAN_NOT_ADD_TRANSACTION_TO_MEMPOOL, "Can't add transaction to mempool"); - return DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_CAN_NOT_ADD_TRANSACTION_TO_MEMPOOL; + return DAP_CHAIN_NET_TX_CREATE_JSON_CAN_NOT_ADD_TRANSACTION_TO_MEMPOOL; } // Completed successfully json_object *l_jobj_tx_create = json_object_new_boolean(true); json_object *l_jobj_hash = json_object_new_string(l_tx_hash_str); - json_object *l_jobj_total_items = json_object_new_uint64(json_object_array_length(l_json_items)); + json_object *l_jobj_total_items = json_object_new_uint64(l_items_count); json_object_object_add(l_jobj_ret, "tx_create", l_jobj_tx_create); json_object_object_add(l_jobj_ret, "hash", l_jobj_hash); json_object_object_add(l_jobj_ret, "total_items", l_jobj_total_items); json_object_array_add(*a_json_arr_reply, l_jobj_ret); - return DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_OK; + return DAP_CHAIN_NET_TX_CREATE_JSON_OK; } /** diff --git a/modules/node-cli/include/dap_chain_node_cli_cmd.h b/modules/node-cli/include/dap_chain_node_cli_cmd.h index 35f2908dfc..91f2e9d9c3 100644 --- a/modules/node-cli/include/dap_chain_node_cli_cmd.h +++ b/modules/node-cli/include/dap_chain_node_cli_cmd.h @@ -204,19 +204,6 @@ typedef enum s_com_tx_create_err{ DAP_CHAIN_NODE_CLI_COM_TX_CREATE_EQ_SOURCE_DESTINATION_ADDRESS }s_com_tx_create_err_t; int com_tx_create(int a_argc, char **a_argv, void **a_str_reply); -typedef enum s_com_tx_create_json_err { - DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_OK = 0, - DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_REQUIRE_PARAMETER_JSON = DAP_JSON_RPC_ERR_CODE_METHOD_ERR_START, - DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_CAN_NOT_OPEN_JSON_FILE, - DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_WRONG_JSON_FORMAT, - DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_REQUIRE_PARAMETER_NET, - DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_NOT_FOUNT_NET_BY_NAME, - DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_NOT_FOUNT_CHAIN_BY_NAME, - DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_NOT_FOUNT_ARRAY_ITEMS, - DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_INVALID_ITEMS, - DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_CAN_NOT_ADD_TRANSACTION_TO_MEMPOOL, - DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_CAN_CHECK_TX_ADD_LEDGER, -}s_com_tx_create_json_err_t; void json_rpc_tx_create(json_object *a_param, json_object *a_reply); int com_tx_create_json(int a_argc, char **a_argv, void **reply); typedef enum s_com_tx_cond_create{ -- GitLab From 6097b2006573f4a6453e65e80cc37184bc485b7b Mon Sep 17 00:00:00 2001 From: "daniil.frolov" <daniil.frolov@demlabs.net> Date: Fri, 17 Jan 2025 11:42:56 +0300 Subject: [PATCH 2/8] port some fixes --- modules/net/dap_chain_net_tx.c | 133 ++++++++++++++++++++++++++++++++- 1 file changed, 132 insertions(+), 1 deletion(-) diff --git a/modules/net/dap_chain_net_tx.c b/modules/net/dap_chain_net_tx.c index 1abe11db08..ceeab57715 100644 --- a/modules/net/dap_chain_net_tx.c +++ b/modules/net/dap_chain_net_tx.c @@ -722,8 +722,57 @@ int dap_chain_net_tx_create_by_json(json_object *a_tx_json, dap_chain_net_t *a_n if (l_json_item_token && dap_strcmp(l_json_item_token, l_native_token)){ l_multichanel = true; l_main_token = l_json_item_token; + break; + } + const char *l_prev_hash_str = s_json_get_text(l_json_item_obj, "prev_hash"); + int64_t l_out_prev_idx; + bool l_is_out_prev_idx = s_json_get_int64(l_json_item_obj, "out_prev_idx", &l_out_prev_idx); + // If prev_hash and out_prev_idx were read + if(l_prev_hash_str && l_is_out_prev_idx){ + dap_chain_hash_fast_t l_tx_prev_hash = {}; + if(!dap_chain_hash_fast_from_str(l_prev_hash_str, &l_tx_prev_hash)) { + //check out token + dap_chain_datum_tx_t *l_prev_tx = dap_ledger_tx_find_by_hash(a_net->pub.ledger, &l_tx_prev_hash); + byte_t *l_prev_item = l_prev_tx ? dap_chain_datum_tx_item_get_nth(l_prev_tx, TX_ITEM_TYPE_OUT_ALL, l_out_prev_idx) : NULL; + if (l_prev_item){ + const char* l_token = NULL; + if (*l_prev_item == TX_ITEM_TYPE_OUT){ + l_token = dap_ledger_tx_get_token_ticker_by_hash(a_net->pub.ledger, &l_tx_prev_hash); + } else if(*l_prev_item == TX_ITEM_TYPE_OUT_EXT){ + l_token = ((dap_chain_tx_out_ext_t*)l_prev_item)->token; + } else { + log_it(L_WARNING, "Invalid 'in' item, wrong type of item with index %"DAP_UINT64_FORMAT_U" in previous tx %s", l_out_prev_idx, l_prev_hash_str); + char *l_str_err = dap_strdup_printf("Unable to create in for transaction. Invalid 'in' item, " + "wrong type of item with index %"DAP_UINT64_FORMAT_U" in previous tx %s", l_out_prev_idx, l_prev_hash_str); + json_object *l_jobj_err = json_object_new_string(l_str_err); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + break; + } + if (dap_strcmp(l_token, l_native_token)){ + l_multichanel = true; + l_main_token = l_json_item_token; + break; + } + + } else { + log_it(L_WARNING, "Invalid 'in' item, can't find item with index %"DAP_UINT64_FORMAT_U" in previous tx %s", l_out_prev_idx, l_prev_hash_str); + char *l_str_err = dap_strdup_printf("Unable to create in for transaction. Invalid 'in' item, " + "can't find item with index %"DAP_UINT64_FORMAT_U" in previous tx %s", l_out_prev_idx, l_prev_hash_str); + json_object *l_jobj_err = json_object_new_string(l_str_err); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + } + } else { + log_it(L_WARNING, "Invalid 'in' item, bad prev_hash %s", l_prev_hash_str); + char *l_str_err = dap_strdup_printf("Unable to create in for transaction. Invalid 'in' item, " + "bad prev_hash %s", l_prev_hash_str); + json_object *l_jobj_err = json_object_new_string(l_str_err); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + } } }break; + case TX_ITEM_TYPE_IN_COND: + case TX_ITEM_TYPE_IN_EMS: + case TX_ITEM_TYPE_IN_REWARD: default: continue; } if(l_multichanel) @@ -948,7 +997,6 @@ int dap_chain_net_tx_create_by_json(json_object *a_tx_json, dap_chain_net_t *a_n } else { l_srv_uid.uint64 = l_srv_id_ui64; } - dap_chain_net_t *l_net = dap_chain_net_by_name(s_json_get_text(l_json_item_obj, "net")); if(!l_net) { log_it(L_ERROR, "Json TX: bad net in OUT_COND_SUBTYPE_SRV_XCHANGE"); @@ -980,6 +1028,89 @@ int dap_chain_net_tx_create_by_json(json_object *a_tx_json, dap_chain_net_t *a_n } } break; + case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK:{ + dap_chain_srv_uid_t l_srv_uid; + uint64_t l_uid_ui64 = 0; + if(!s_json_get_srv_uid(l_json_item_obj, "service_id", "service", &l_uid_ui64)){ + // Default service DAP_CHAIN_NET_SRV_VPN_ID + l_srv_uid.uint64 = 0x12; + } else { + l_srv_uid.uint64 = l_uid_ui64; + } + uint256_t l_value = { }; + if(!s_json_get_uint256(l_json_item_obj, "value", &l_value) || IS_ZERO_256(l_value)) { + log_it(L_ERROR, "Json TX: bad value in DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK"); + break; + } + const char* l_time_staking_str = NULL; + if((l_time_staking_str = s_json_get_text(l_json_item_obj, "time_staking")) == NULL || dap_strlen(l_time_staking_str) != 6) { + log_it(L_ERROR, "Json TX: bad time staking in DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK"); + break; + } + + char l_time_staking_month_str[3] = {l_time_staking_str[2], l_time_staking_str[3], 0}; + int l_time_staking_month = atoi(l_time_staking_month_str); + if (l_time_staking_month < 1 || l_time_staking_month > 12){ + log_it(L_ERROR, "Json TX: bad time staking in DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK"); + break; + } + + + char l_time_staking_day_str[3] = {l_time_staking_str[4], l_time_staking_str[5], 0}; + int l_time_staking_day = atoi(l_time_staking_day_str); + if (l_time_staking_day < 1 || l_time_staking_day > 31){ + log_it(L_ERROR, "Json TX: bad time staking in DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK"); + break; + } + + dap_time_t l_time_staking = 0; + l_time_staking = dap_time_from_str_simplified(l_time_staking_str); + if (0 == l_time_staking){ + log_it(L_ERROR, "Json TX: bad time staking in DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK"); + break; + } + dap_time_t l_time_now = dap_time_now(); + if (l_time_staking < l_time_now){ + log_it(L_ERROR, "Json TX: bad time staking in DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK"); + break; + } + l_time_staking -= l_time_now; + + uint256_t l_reinvest_percent = uint256_0; + const char* l_reinvest_percent_str = NULL; + if((l_reinvest_percent_str = s_json_get_text(l_json_item_obj, "reinvest_percent"))!=NULL) { + l_reinvest_percent = dap_uint256_scan_decimal(l_reinvest_percent_str); + if (compare256(l_reinvest_percent, dap_uint256_scan_decimal("100.0")) == 1){ + log_it(L_ERROR, "Json TX: bad reinvest percent in DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK"); + break; + } + if (IS_ZERO_256(l_reinvest_percent)) { + int l_reinvest_percent_int = atoi(l_reinvest_percent_str); + if (l_reinvest_percent_int < 0 || l_reinvest_percent_int > 100){ + log_it(L_ERROR, "Json TX: bad reinvest percent in DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK"); + break; + } + l_reinvest_percent = dap_chain_uint256_from(l_reinvest_percent_int); + MULT_256_256(l_reinvest_percent, GET_256_FROM_64(1000000000000000000ULL), &l_reinvest_percent); + } + } + + dap_chain_tx_out_cond_t *l_out_cond_item = dap_chain_datum_tx_item_out_cond_create_srv_stake_lock(l_srv_uid, l_value, l_time_staking, l_reinvest_percent, + DAP_CHAIN_NET_SRV_STAKE_LOCK_FLAG_BY_TIME | + DAP_CHAIN_NET_SRV_STAKE_LOCK_FLAG_EMIT); + l_item = (const uint8_t*) l_out_cond_item; + // Save value for using in In item + if(l_item) { + SUM_256_256(l_value_need, l_value, &l_value_need); + } else { + char *l_str_err = dap_strdup_printf("Unable to create conditional out for transaction " + "can of type %s described in item %zu.", l_subtype_str, i); + json_object *l_jobj_err = json_object_new_string(l_str_err); + DAP_DELETE(l_str_err); + if (l_jobj_errors) json_object_array_add(l_jobj_errors, l_jobj_err); + } + } + break; case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE:{ dap_chain_srv_uid_t l_srv_uid; uint64_t l_srv_id_ui64 = 0; -- GitLab From a1ad2a1843a7e2e92ae800fb5aae55ccba55997c Mon Sep 17 00:00:00 2001 From: "daniil.frolov" <daniil.frolov@demlabs.net> Date: Wed, 15 Jan 2025 10:02:26 +0300 Subject: [PATCH 3/8] .. --- dap-sdk | 2 +- modules/net/dap_chain_net_tx.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dap-sdk b/dap-sdk index bf5454cead..ecdce84bf4 160000 --- a/dap-sdk +++ b/dap-sdk @@ -1 +1 @@ -Subproject commit bf5454ceadc27aa0d053fb593833f960c008485b +Subproject commit ecdce84bf4c1ec1df1b555f7bd97d900fe33131b diff --git a/modules/net/dap_chain_net_tx.c b/modules/net/dap_chain_net_tx.c index 33032a2b09..969b105ecf 100644 --- a/modules/net/dap_chain_net_tx.c +++ b/modules/net/dap_chain_net_tx.c @@ -536,4 +536,4 @@ bool dap_chain_net_tx_set_fee(dap_chain_net_id_t a_net_id, uint256_t a_value, da l_net->pub.fee_addr = a_addr; return true; -} + } -- GitLab From 317a980b8f92ee45f031d3199c24373579dc75b7 Mon Sep 17 00:00:00 2001 From: "daniil.frolov" <daniil.frolov@demlabs.net> Date: Thu, 16 Jan 2025 15:45:54 +0300 Subject: [PATCH 4/8] [*] Fix block dump command --- modules/type/blocks/dap_chain_cs_blocks.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/type/blocks/dap_chain_cs_blocks.c b/modules/type/blocks/dap_chain_cs_blocks.c index 5637c55154..921deb25a7 100644 --- a/modules/type/blocks/dap_chain_cs_blocks.c +++ b/modules/type/blocks/dap_chain_cs_blocks.c @@ -732,6 +732,7 @@ static int s_cli_blocks(int a_argc, char ** a_argv, void **a_str_reply) case SUBCMD_DUMP:{ const char *l_hash_out_type = NULL; + const char *l_hash_str = NULL; dap_chain_hash_fast_t l_block_hash={0}; dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-H", &l_hash_out_type); if(!l_hash_out_type) @@ -739,15 +740,17 @@ static int s_cli_blocks(int a_argc, char ** a_argv, void **a_str_reply) if(dap_strcmp(l_hash_out_type,"hex") && dap_strcmp(l_hash_out_type,"base58")) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_BLOCK_PARAM_ERR, "invalid parameter -H, valid values: -H <hex | base58>"); return DAP_CHAIN_NODE_CLI_COM_BLOCK_PARAM_ERR; - } - if (!l_subcmd_str_arg) { + } + dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-hash", &l_hash_str); + if (!l_hash_str) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_BLOCK_HASH_ERR, "Enter block hash "); return DAP_CHAIN_NODE_CLI_COM_BLOCK_HASH_ERR; } - dap_chain_hash_fast_from_str(l_subcmd_str_arg, &l_block_hash); // Convert argument to hash + + dap_chain_hash_fast_from_str(l_hash_str, &l_block_hash); dap_chain_block_cache_t *l_block_cache = dap_chain_block_cache_get_by_hash(l_blocks, &l_block_hash); if (!l_block_cache) { - dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_BLOCK_FIND_ERR, "Can't find block %s ", l_subcmd_str_arg); + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_BLOCK_FIND_ERR, "Can't find block %s ", l_hash_str); return DAP_CHAIN_NODE_CLI_COM_BLOCK_FIND_ERR; } dap_chain_block_t *l_block = l_block_cache->block; -- GitLab From 276d192dd7418caaae84ad3bd3ea6794d7472ed0 Mon Sep 17 00:00:00 2001 From: "daniil.frolov" <daniil.frolov@demlabs.net> Date: Thu, 16 Jan 2025 16:47:06 +0300 Subject: [PATCH 5/8] .. --- modules/type/blocks/dap_chain_cs_blocks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/type/blocks/dap_chain_cs_blocks.c b/modules/type/blocks/dap_chain_cs_blocks.c index 921deb25a7..69ab7de1c5 100644 --- a/modules/type/blocks/dap_chain_cs_blocks.c +++ b/modules/type/blocks/dap_chain_cs_blocks.c @@ -209,7 +209,7 @@ int dap_chain_cs_blocks_init() "\t\tComplete the current new round, verify it and if everything is ok - publish new blocks in chain\n\n" "Blockchain explorer:\n" - "block -net <net_name> [-chain <chain_name>] dump <block_hash>\n" + "block -net <net_name> [-chain <chain_name>] dump -hash <block_hash>\n" "\t\tDump block info\n\n" "block -net <net_name> [-chain <chain_name>] list [{signed | first_signed}] [-limit] [-offset] [-head]" -- GitLab From f90b9482fd0e43a8120008970fbde582b757923e Mon Sep 17 00:00:00 2001 From: "daniil.frolov" <daniil.frolov@demlabs.net> Date: Fri, 17 Jan 2025 15:13:40 +0300 Subject: [PATCH 6/8] .. --- modules/node-cli/dap_chain_node_cli.c | 1 - modules/node-cli/dap_chain_node_cli_cmd_tx.c | 132 +++---------------- 2 files changed, 19 insertions(+), 114 deletions(-) diff --git a/modules/node-cli/dap_chain_node_cli.c b/modules/node-cli/dap_chain_node_cli.c index ae766b5d5f..dbccf955b4 100644 --- a/modules/node-cli/dap_chain_node_cli.c +++ b/modules/node-cli/dap_chain_node_cli.c @@ -324,7 +324,6 @@ int dap_chain_node_cli_init(dap_config_t * g_config) "\texample datoshi amount syntax (only integer) 1 20 0.4321e+4\n\n"); dap_cli_server_cmd_add ("tx_create_json", com_tx_create_json, "Make transaction", "tx_create_json -net <net_name> [-chain <chain_name>] -json <json_file_path>\n" ); - dap_json_rpc_cli_handler_add("j_tx_create", json_rpc_tx_create); dap_cli_server_cmd_add ("tx_cond_create", com_tx_cond_create, "Make cond transaction", "tx_cond_create -net <net_name> -token <token_ticker> -w <wallet_name>" " -cert <pub_cert_name> -value <value> -fee <value> -unit {B | SEC} -srv_uid <numeric_uid>\n\n" 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 9fb620150b..1f0fddcfa0 100644 --- a/modules/node-cli/dap_chain_node_cli_cmd_tx.c +++ b/modules/node-cli/dap_chain_node_cli_cmd_tx.c @@ -2190,112 +2190,6 @@ int s_json_rpc_tx_parse_json(dap_chain_net_t *a_net, dap_chain_t *a_chain, json_ return 0; } -/** - * @breif Create transaction from json rpc request - * @param a_param - * @param a_reply - */ -void json_rpc_tx_create(json_object *a_param, json_object *a_reply){ - const char *l_net_name = NULL; - const char *l_chain_name = NULL; - json_object *l_json_net = json_object_object_get(a_param, "net"); - if(l_json_net && json_object_is_type(l_json_net, json_type_string)) { - l_net_name = json_object_get_string(l_json_net); - } - if(!l_net_name) { - dap_json_rpc_error_add(a_reply, DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_REQUIRE_PARAMETER_NET, - "Command requires parameter '-net' or set net in the json-rpc request"); - return; - } - json_object *l_json_chain = json_object_object_get(a_param, "chain"); - if(l_json_chain && json_object_is_type(l_json_chain, json_type_string)) { - l_chain_name = json_object_get_string(l_json_chain); - } - dap_chain_net_t *l_net = dap_chain_net_by_name(l_net_name); - if (!l_net) { - dap_json_rpc_error_add(a_reply, - DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_NOT_FOUNT_NET_BY_NAME, - "Not found net by name '%s'", l_net_name); - return; - } - dap_chain_t *l_chain = NULL; - if (!l_chain_name) { - l_chain = dap_chain_net_get_chain_by_chain_type(l_net, CHAIN_TYPE_TX); - } else { - l_chain = dap_chain_net_get_chain_by_name(l_net, l_chain_name); - if (!l_chain){ - dap_json_rpc_error_add(a_reply, DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_NOT_FOUNT_CHAIN_BY_NAME, - "Chain name '%s' not found, try use parameter '-chain' or set chain in the json-rpc request", l_chain_name); - return; - } - } - dap_chain_datum_tx_t *l_tx = NULL; - json_object *l_jobj_items = json_object_object_get(a_param, "items"); - if (!l_jobj_items) { - dap_json_rpc_error_add(a_reply, DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_NOT_FOUNT_ARRAY_ITEMS, - "Items with description transaction not found in JSON."); - return ; - } - size_t l_items_ready = 0; - json_object *l_jobj_errors = NULL; - int res = s_json_rpc_tx_parse_json(l_net, l_chain, l_jobj_items, &l_tx, &l_items_ready, &l_jobj_errors); - if (res) { - json_object *l_jobj_tx_create = json_object_new_boolean(false); - json_object *l_jobj_items_ready = json_object_new_uint64(l_items_ready); - json_object *l_jobj_total_items = json_object_new_uint64(json_object_array_length(l_jobj_items)); - json_object_object_add(a_reply, "tx_create", l_jobj_tx_create); - json_object_object_add(a_reply, "ready_items", l_jobj_items_ready); - json_object_object_add(a_reply, "total_items", l_jobj_total_items); - json_object_object_add(a_reply, "errors", l_jobj_errors); - return; - } - - // Pack transaction into the datum - size_t l_tx_size = dap_chain_datum_tx_get_size(l_tx); - dap_chain_datum_t *l_datum_tx = dap_chain_datum_create(DAP_CHAIN_DATUM_TX, l_tx, l_tx_size); - size_t l_datum_tx_size = dap_chain_datum_size(l_datum_tx); - DAP_DELETE(l_tx); - - // Add transaction to mempool - char *l_tx_hash_str = dap_get_data_hash_str(l_datum_tx->data, l_datum_tx->header.data_size).s; - dap_chain_hash_fast_t l_hf_tx = {0}; - dap_chain_hash_fast_from_str(l_tx_hash_str, &l_hf_tx); - int rc = -1; - if ((rc = dap_ledger_tx_add_check(l_net->pub.ledger, (dap_chain_datum_tx_t*)l_datum_tx->data, l_tx_size, &l_hf_tx))) { - json_object *l_jobj_tx_create = json_object_new_boolean(false); - json_object *l_jobj_hash = json_object_new_string(l_tx_hash_str); - json_object *l_jobj_total_items = json_object_new_uint64(json_object_array_length(l_jobj_items)); - json_object *l_jobj_ledger_ret_code = json_object_new_object(); - json_object_object_add(l_jobj_ledger_ret_code, "code", json_object_new_int(rc)); - json_object_object_add(l_jobj_ledger_ret_code, "message", - json_object_new_string(dap_chain_net_verify_datum_err_code_to_str(l_datum_tx, rc))); - json_object_object_add(a_reply, "tx_create", l_jobj_tx_create); - json_object_object_add(a_reply, "hash", l_jobj_hash); - json_object_object_add(a_reply, "ledger_code", l_jobj_ledger_ret_code); - json_object_object_add(a_reply, "total_items", l_jobj_total_items); - DAP_DELETE(l_datum_tx); - return ; - } - - char *l_gdb_group_mempool_base_tx = dap_chain_mempool_group_new(l_chain);// get group name for mempool - bool l_placed = !dap_global_db_set(l_gdb_group_mempool_base_tx, l_tx_hash_str, l_datum_tx, l_datum_tx_size, false, NULL, NULL); - - DAP_DELETE(l_datum_tx); - DAP_DELETE(l_gdb_group_mempool_base_tx); - if(!l_placed) { - dap_json_rpc_error_add(a_reply, DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_CAN_NOT_ADD_TRANSACTION_TO_MEMPOOL, - "Can't add transaction to mempool"); - return ; - } - // Completed successfully - json_object *l_jobj_tx_create = json_object_new_boolean(true); - json_object *l_jobj_hash = json_object_new_string(l_tx_hash_str); - json_object *l_jobj_total_items = json_object_new_uint64(json_object_array_length(l_jobj_items)); - json_object_object_add(a_reply, "tx_create", l_jobj_tx_create); - json_object_object_add(a_reply, "hash", l_jobj_hash); - json_object_object_add(a_reply, "total_items", l_jobj_total_items); -} - /** * @brief Create transaction from json file * com_tx_create command @@ -2312,22 +2206,34 @@ int com_tx_create_json(int a_argc, char ** a_argv, void **reply) const char *l_net_name = NULL; // optional parameter const char *l_chain_name = NULL; // optional parameter const char *l_json_file_path = NULL; + const char *l_json_str = NULL; dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-net", &l_net_name); // optional parameter dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-chain", &l_chain_name); // optional parameter dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-json", &l_json_file_path); + dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-tx_obj", &l_json_str); - if(!l_json_file_path) { + if(!l_json_file_path && !l_json_str) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_REQUIRE_PARAMETER_JSON, - "Command requires one of parameters '-json <json file path>'"); + "Command requires one of parameters '-json <json file path>' or -tx_obj <string>'"); return DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_REQUIRE_PARAMETER_JSON; } // Open json file - struct json_object *l_json = json_object_from_file(l_json_file_path); - if(!l_json) { - dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_CAN_NOT_OPEN_JSON_FILE, - "Can't open json file: %s", json_util_get_last_err()); - return DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_CAN_NOT_OPEN_JSON_FILE; + struct json_object *l_json = NULL; + if (l_json_file_path){ + l_json = json_object_from_file(l_json_file_path); + if(!l_json) { + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NET_TX_CREATE_JSON_CAN_NOT_OPEN_JSON_FILE, + "Can't open json file: %s", json_util_get_last_err()); + return DAP_CHAIN_NET_TX_CREATE_JSON_CAN_NOT_OPEN_JSON_FILE; + } + } else if (l_json_str) { + l_json = json_tokener_parse(l_json_str); + if(!l_json) { + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NET_TX_CREATE_JSON_CAN_NOT_OPEN_JSON_FILE, + "Can't parse input JSON-string", json_util_get_last_err()); + return DAP_CHAIN_NET_TX_CREATE_JSON_CAN_NOT_OPEN_JSON_FILE; + } } if(!json_object_is_type(l_json, json_type_object)) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_WRONG_JSON_FORMAT, "Wrong json format"); -- GitLab From 9bc1ad7a8753a67d85024a676abe7290660a32c2 Mon Sep 17 00:00:00 2001 From: "daniil.frolov" <daniil.frolov@demlabs.net> Date: Fri, 17 Jan 2025 15:20:59 +0300 Subject: [PATCH 7/8] ... --- dap-sdk | 2 +- modules/net/dap_chain_net_tx.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/dap-sdk b/dap-sdk index ecdce84bf4..1bfb4cbd9b 160000 --- a/dap-sdk +++ b/dap-sdk @@ -1 +1 @@ -Subproject commit ecdce84bf4c1ec1df1b555f7bd97d900fe33131b +Subproject commit 1bfb4cbd9bf3bb437f93d91a82a1fa0303bd61db diff --git a/modules/net/dap_chain_net_tx.c b/modules/net/dap_chain_net_tx.c index 41869c3f63..b153d5b86a 100644 --- a/modules/net/dap_chain_net_tx.c +++ b/modules/net/dap_chain_net_tx.c @@ -568,6 +568,11 @@ static bool s_json_get_int64(struct json_object *a_json, const char *a_key, int6 // Read number *a_out = json_object_get_int64(l_json); return true; + } else if (json_object_is_type(l_json, json_type_string)){ + // Read number + const char* l_value_text = json_object_get_string(l_json); + *a_out = atol(l_value_text); + return true; } } return false; -- GitLab From 39c932f38ab07d8953c0cf8506afd81d84353ef3 Mon Sep 17 00:00:00 2001 From: "daniil.frolov" <daniil.frolov@demlabs.net> Date: Fri, 17 Jan 2025 15:40:41 +0300 Subject: [PATCH 8/8] up dap-sdk --- dap-sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dap-sdk b/dap-sdk index 1bfb4cbd9b..4224a8e860 160000 --- a/dap-sdk +++ b/dap-sdk @@ -1 +1 @@ -Subproject commit 1bfb4cbd9bf3bb437f93d91a82a1fa0303bd61db +Subproject commit 4224a8e8602239f6ae71ccf1e709a5fc8df43b8e -- GitLab