From 6ad25067bb0e0b97a09159547cccac07cc8e0460 Mon Sep 17 00:00:00 2001 From: Alexander Lysikov <alexander.lysikov@demlabs.net> Date: Tue, 21 Jun 2022 21:49:27 +0500 Subject: [PATCH] features-6376 Add tx_create_json command to create a custom transaction --- dap-sdk/crypto/include/dap_hash.h | 2 +- modules/common/dap_chain_datum_tx_items.c | 50 ++ modules/common/include/dap_chain_common.h | 3 +- .../common/include/dap_chain_datum_tx_items.h | 22 +- .../include/dap_chain_datum_tx_out_cond.h | 1 + modules/net/dap_chain_node_cli.c | 2 + modules/net/dap_chain_node_cli_cmd.c | 519 +++++++++++++++++- modules/net/include/dap_chain_node_cli_cmd.h | 1 + modules/net/srv/dap_chain_net_srv.c | 22 + modules/net/srv/include/dap_chain_net_srv.h | 19 + .../service/stake/dap_chain_net_srv_stake.c | 32 +- 11 files changed, 658 insertions(+), 15 deletions(-) diff --git a/dap-sdk/crypto/include/dap_hash.h b/dap-sdk/crypto/include/dap_hash.h index 76d507087c..4565a72262 100755 --- a/dap-sdk/crypto/include/dap_hash.h +++ b/dap-sdk/crypto/include/dap_hash.h @@ -129,7 +129,7 @@ DAP_STATIC_INLINE int dap_chain_hash_fast_to_str( dap_hash_fast_t *a_hash, char DAP_STATIC_INLINE char *dap_chain_hash_fast_to_str_new(dap_hash_fast_t * a_hash) { const size_t c_hash_str_size = DAP_CHAIN_HASH_FAST_STR_SIZE; - char * ret = DAP_NEW_Z_SIZE(char, c_hash_str_size); + char * ret = DAP_NEW_Z_SIZE(char, c_hash_str_size + 1); if(dap_chain_hash_fast_to_str( a_hash, ret, c_hash_str_size ) < 0 ) DAP_DEL_Z(ret); return ret; diff --git a/modules/common/dap_chain_datum_tx_items.c b/modules/common/dap_chain_datum_tx_items.c index c3f16a8ba9..10456b810b 100644 --- a/modules/common/dap_chain_datum_tx_items.c +++ b/modules/common/dap_chain_datum_tx_items.c @@ -104,6 +104,56 @@ static size_t dap_chain_datum_tx_receipt_get_size(const dap_chain_datum_tx_recei return size; } +/** + * Get item type by item name + * + * return type, or TX_ITEM_TYPE_UNKNOWN + */ +dap_chain_tx_item_type_t dap_chain_datum_tx_item_str_to_type(const char *a_datum_name) { + if(!a_datum_name) + return TX_ITEM_TYPE_UNKNOWN; + if(!dap_strcmp(a_datum_name, "in")) + return TX_ITEM_TYPE_IN; + else if(!dap_strcmp(a_datum_name, "out")) + return TX_ITEM_TYPE_OUT; + else if(!dap_strcmp(a_datum_name, "out_ext")) + return TX_ITEM_TYPE_OUT_EXT; + else if(!dap_strcmp(a_datum_name, "pkey")) + return TX_ITEM_TYPE_PKEY; + else if(!dap_strcmp(a_datum_name, "sign")) + return TX_ITEM_TYPE_SIG; + else if(!dap_strcmp(a_datum_name, "token")) + return TX_ITEM_TYPE_TOKEN; + else if(!dap_strcmp(a_datum_name, "in_cond")) + return TX_ITEM_TYPE_IN_COND; + else if(!dap_strcmp(a_datum_name, "out_cond")) + return TX_ITEM_TYPE_OUT_COND; + else if(!dap_strcmp(a_datum_name, "receipt")) + return TX_ITEM_TYPE_RECEIPT; + return TX_ITEM_TYPE_UNKNOWN; +} + +/** + * Get dap_chain_tx_out_cond_subtype_t by name + * + * return subtype, or DAP_CHAIN_TX_OUT_COND_SUBTYPE_UNDEFINED + */ +dap_chain_tx_out_cond_subtype_t dap_chain_tx_out_cond_subtype_from_str(const char *a_subtype_str) { + if(!a_subtype_str) + return DAP_CHAIN_TX_OUT_COND_SUBTYPE_UNDEFINED; + if(!dap_strcmp(a_subtype_str, "srv_pay")) + return DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY; + else if(!dap_strcmp(a_subtype_str, "srv_xchange")) + return DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE; + else if(!dap_strcmp(a_subtype_str, "srv_stake")) + return DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE; + else if(!dap_strcmp(a_subtype_str, "srv_stake_update")) + return DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_UPDATE; + else if(!dap_strcmp(a_subtype_str, "fee")) + return DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE; + return DAP_CHAIN_TX_OUT_COND_SUBTYPE_UNDEFINED; +} + /** * Get item type * diff --git a/modules/common/include/dap_chain_common.h b/modules/common/include/dap_chain_common.h index d226b56cc7..50061ba043 100644 --- a/modules/common/include/dap_chain_common.h +++ b/modules/common/include/dap_chain_common.h @@ -209,7 +209,8 @@ enum dap_chain_tx_item_type { TX_ITEM_TYPE_RECEIPT = 0x70, TX_ITEM_TYPE_OUT_ALL = 0xfe, - TX_ITEM_TYPE_ANY = 0xff + TX_ITEM_TYPE_ANY = 0xff, + TX_ITEM_TYPE_UNKNOWN = 0xff }; typedef byte_t dap_chain_tx_item_type_t; diff --git a/modules/common/include/dap_chain_datum_tx_items.h b/modules/common/include/dap_chain_datum_tx_items.h index f71d6f514b..8aa908cc30 100644 --- a/modules/common/include/dap_chain_datum_tx_items.h +++ b/modules/common/include/dap_chain_datum_tx_items.h @@ -25,9 +25,9 @@ #include <stdint.h> #include <string.h> -//#include <glib.h> #include "dap_common.h" +#include "dap_strfuncs.h" #include "dap_list.h" #include "dap_chain_common.h" #include "dap_sign.h" @@ -48,6 +48,12 @@ * return type, or TX_ITEM_TYPE_ANY if error */ dap_chain_tx_item_type_t dap_chain_datum_tx_item_get_type(const uint8_t *a_item); + +/** + * Get item name by item type + * + * return name, or UNDEFINED + */ DAP_STATIC_INLINE const char * dap_chain_datum_tx_item_type_to_str(dap_chain_tx_item_type_t a_item_type) { switch(a_item_type){ @@ -68,6 +74,20 @@ DAP_STATIC_INLINE const char * dap_chain_datum_tx_item_type_to_str(dap_chain_tx_ } } +/** + * Get item type by item name + * + * return type, or TX_ITEM_TYPE_UNKNOWN + */ +dap_chain_tx_item_type_t dap_chain_datum_tx_item_str_to_type(const char *a_datum_name); + +/** + * Get dap_chain_tx_out_cond_subtype_t by name + * + * return subtype, or DAP_CHAIN_TX_OUT_COND_SUBTYPE_UNDEFINED + */ +dap_chain_tx_out_cond_subtype_t dap_chain_tx_out_cond_subtype_from_str(const char *a_subtype_str); + /** * Get item size * diff --git a/modules/common/include/dap_chain_datum_tx_out_cond.h b/modules/common/include/dap_chain_datum_tx_out_cond.h index 552841785a..004b1e0be3 100644 --- a/modules/common/include/dap_chain_datum_tx_out_cond.h +++ b/modules/common/include/dap_chain_datum_tx_out_cond.h @@ -34,6 +34,7 @@ #define MAX_FEE_STAKE GET_256_FROM_64(1000) enum dap_chain_tx_out_cond_subtype { + DAP_CHAIN_TX_OUT_COND_SUBTYPE_UNDEFINED = 0x0, DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY = 0x01, DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE = 0x02, DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE = 0x3, diff --git a/modules/net/dap_chain_node_cli.c b/modules/net/dap_chain_node_cli.c index b000278be9..40569b6c0f 100644 --- a/modules/net/dap_chain_node_cli.c +++ b/modules/net/dap_chain_node_cli.c @@ -1079,6 +1079,8 @@ int dap_chain_node_cli_init(dap_config_t * g_config) // Transaction commands dap_chain_node_cli_cmd_item_create ("tx_create", com_tx_create, "Make transaction", "tx_create -net <net name> -chain <chain name> {-from_wallet <name> -token <token ticker> -value <value> -to_addr <addr> | -from_emission <emission_hash>}[-fee <addr> -value_fee <val>]\n" ); + dap_chain_node_cli_cmd_item_create ("tx_create_json", com_tx_create_json, "Make transaction", + "tx_create_json -net <net name> -chain <chain name> -json <json file path>\n" ); dap_chain_node_cli_cmd_item_create ("tx_cond_create", com_tx_cond_create, "Make cond transaction", "tx_cond_create -net <net name> -token <token ticker> -wallet <from wallet> -cert <public cert> -value <value datoshi> -unit {mb | kb | b | sec | day} -srv_uid <numeric uid>\n" ); diff --git a/modules/net/dap_chain_node_cli_cmd.c b/modules/net/dap_chain_node_cli_cmd.c index d1e0dc7de4..2f052563ae 100644 --- a/modules/net/dap_chain_node_cli_cmd.c +++ b/modules/net/dap_chain_node_cli_cmd.c @@ -3685,17 +3685,7 @@ int com_tx_cond_create(int a_argc, char ** a_argv, char **a_str_reply) return -8; } - dap_chain_net_srv_price_unit_uid_t l_price_unit = { .enm = SERV_UNIT_UNDEFINED }; - if(!dap_strcmp(l_unit_str, "mb")) - l_price_unit.enm = SERV_UNIT_MB; - else if(!dap_strcmp(l_unit_str, "sec")) - l_price_unit.enm = SERV_UNIT_SEC; - else if(!dap_strcmp(l_unit_str, "day")) - l_price_unit.enm = SERV_UNIT_DAY; - else if(!dap_strcmp(l_unit_str, "kb")) - l_price_unit.enm = SERV_UNIT_KB; - else if(!dap_strcmp(l_unit_str, "b")) - l_price_unit.enm = SERV_UNIT_B; + dap_chain_net_srv_price_unit_uid_t l_price_unit = dap_chain_net_srv_price_unit_uid_from_str(l_unit_str); if(l_price_unit.enm == SERV_UNIT_UNDEFINED) { dap_chain_node_cli_set_reply_text(a_str_reply, "Can't recognize unit '%s'. Unit must look like {mb | kb | b | sec | day}", @@ -3960,6 +3950,513 @@ int com_chain_ca_pub( int a_argc, char ** a_argv, char ** a_str_reply) } } +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) { + dap_chain_net_srv_t *l_srv = dap_chain_net_srv_get_by_name(l_service); + if(!l_srv) + return false; + *a_out = l_srv->uid.uint64; + return true; + } + } + return false; +} + +static dap_chain_wallet_t* s_json_get_wallet(struct json_object *a_json, const char *a_key) +{ + dap_enc_key_t *l_enc_key = NULL; + // From wallet + const char *l_wallet_str = s_json_get_text(a_json, a_key); + if(l_wallet_str) { + dap_chain_wallet_t *l_wallet = dap_chain_wallet_open(l_wallet_str, dap_config_get_item_str_default(g_config, "resources", "wallets_path", NULL)); + return l_wallet; + } + return NULL; +} + +static const dap_cert_t* s_json_get_cert(struct json_object *a_json, const char *a_key) +{ + const char *l_cert_name = s_json_get_text(a_json, a_key); + if(l_cert_name) { + dap_cert_t *l_cert = dap_cert_find_by_name(l_cert_name); + return l_cert; + } + return NULL; +} + +// 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; +} + + +/** + * @brief Create transaction from json file + * com_tx_create command + * @param argc + * @param argv + * @param arg_func + * @param str_reply + * @return int + */ +int com_tx_create_json(int a_argc, char ** a_argv, char **a_str_reply) +{ + int l_arg_index = 1; + int l_err_code = 0; + const char *l_net_name = NULL; // optional parameter + const char *l_chain_name = NULL; // optional parameter + const char *l_json_file_path = NULL; + + dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-net", &l_net_name); // optional parameter + dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-chain", &l_chain_name); // optional parameter + dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-json", &l_json_file_path); + + if(!l_json_file_path) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Command requires one of parameters '-json <json file path>'"); + return -1; + } + // Open json file + struct json_object *l_json = json_object_from_file(l_json_file_path); + if(!l_json) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Can't open json file: %s", json_util_get_last_err()); + return -2; + } + if(!json_object_is_type(l_json, json_type_object)) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Wrong json format"); + json_object_put(l_json); + return -3; + } + + // Read network from json file + if(!l_net_name) { + struct json_object *l_json_net = json_object_object_get(l_json, "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_chain_node_cli_set_reply_text(a_str_reply, "Command requires parameter '-net' or set net in the json file"); + json_object_put(l_json); + return -11; + } + } + dap_chain_net_t *l_net = dap_chain_net_by_name(l_net_name); + if(!l_net) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Not found net by name '%s'", l_net_name); + json_object_put(l_json); + return -12; + } + + // Read chain from json file + if(!l_chain_name) { + struct json_object *l_json_chain = json_object_object_get(l_json, "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_t *l_chain = dap_chain_net_get_chain_by_name(l_net, l_chain_name); + if(!l_chain) { + l_chain = dap_chain_net_get_chain_by_chain_type(l_net, CHAIN_TYPE_TX); + } + if(!l_chain) { + dap_chain_node_cli_set_reply_text(a_str_reply, "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 -13; + } + + + // Read items from json file + struct json_object *l_json_items = json_object_object_get(l_json, "items"); + size_t l_items_count = json_object_array_length(l_json_items); + bool a = (l_items_count = json_object_array_length(l_json_items)); + if(!l_json_items || !json_object_is_type(l_json_items, json_type_array) || !(l_items_count = json_object_array_length(l_json_items))) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Wrong json format: not found array 'items' or array is empty"); + json_object_put(l_json); + return -15; + } + // Create transaction + dap_chain_datum_tx_t *l_tx = DAP_NEW_Z_SIZE(dap_chain_datum_tx_t, sizeof(dap_chain_datum_tx_t)); + l_tx->header.ts_created = time(NULL); + size_t l_items_ready = 0; + dap_list_t *l_sign_list = NULL; + // 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; + } + // Create an item depending on its type + const uint8_t *l_item = NULL; + switch (l_item_type) { + case TX_ITEM_TYPE_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); + l_item = (const uint8_t*) l_in_item; + } + else { + log_it(L_WARNING, "Invalid 'in' item %zu", i); + continue; + } + } + } + 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); + if(l_is_value && l_json_item_addr_str) { + dap_chain_addr_t *l_addr = dap_chain_addr_from_str(l_json_item_addr_str); + if(l_addr && !IS_ZERO_256(l_value)) { + if(l_item_type == TX_ITEM_TYPE_OUT) { + // Create OUT item + dap_chain_tx_out_t *l_out_item = dap_chain_datum_tx_item_out_create(l_addr, l_value); + l_item = (const uint8_t*) l_out_item; + } + else if(l_item_type == TX_ITEM_TYPE_OUT_EXT) { + // Read address and value + const char *l_token = s_json_get_text(l_json_item_obj, "token"); + if(l_token) { + // Create OUT_EXT item + dap_chain_tx_out_ext_t *l_out_ext_item = dap_chain_datum_tx_item_out_ext_create(l_addr, l_value, l_token); + l_item = (const uint8_t*) l_out_ext_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); + } + continue; + } + } + } + break; + case TX_ITEM_TYPE_IN_COND: { + // 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; + int64_t l_receipt_idx; + bool l_is_out_prev_idx = s_json_get_int64(l_json_item_obj, "out_prev_idx", &l_out_prev_idx); + bool l_is_receipt_idx = s_json_get_int64(l_json_item_obj, "receipt_idx", &l_receipt_idx); + + if(l_prev_hash_str && l_is_out_prev_idx && l_is_receipt_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_COND item + dap_chain_tx_in_cond_t *l_in_cond_item = dap_chain_datum_tx_item_in_cond_create(&l_tx_prev_hash, (uint32_t) l_out_prev_idx, (uint32_t) l_receipt_idx); + l_item = (const uint8_t*) l_in_cond_item; + } + } + } + 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)) { + 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)) { + 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)) { + break; + } + dap_chain_net_srv_uid_t l_srv_uid; + if(!s_json_get_srv_uid(l_json_item_obj, "service_id", "service", &l_srv_uid.uint64)){ + // Default service DAP_CHAIN_NET_SRV_VPN_ID + l_srv_uid.uint64 = 0x0000000000000001; + } + + // From "wallet" or "cert" + dap_pkey_t *l_pkey = s_json_get_pkey(l_json_item_obj); + if(!l_pkey) { + 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; + DAP_DELETE(l_pkey); + } + break; + case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE: { + + dap_chain_net_srv_uid_t l_srv_uid; + if(!s_json_get_srv_uid(l_json_item_obj, "service_id", "service", &l_srv_uid.uint64)) { + // Default service DAP_CHAIN_NET_SRV_XCHANGE_ID + l_srv_uid.uint64 = 0x2; + } + dap_chain_net_t *l_net = dap_chain_net_by_name(s_json_get_text(l_json_item_obj, "net")); + if(!l_net) { + break; + } + const char *l_token = s_json_get_text(l_json_item_obj, "token"); + if(!l_token) { + break; + } + uint256_t l_value = { }; + if(!s_json_get_uint256(l_json_item_obj, "value", &l_value) || IS_ZERO_256(l_value)) { + 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_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; + } + break; + case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE:{ + dap_chain_net_srv_uid_t l_srv_uid; + if(!s_json_get_srv_uid(l_json_item_obj, "service_id", "service", &l_srv_uid.uint64)) { + // Default service DAP_CHAIN_NET_SRV_STAKE_ID + l_srv_uid.uint64 = 0x13; + } + uint256_t l_value = { }; + if(!s_json_get_uint256(l_json_item_obj, "value", &l_value) || IS_ZERO_256(l_value)) { + 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_fee_addr_str = s_json_get_text(l_json_item_obj, "fee_addr"); + const char *l_hldr_addr_str = s_json_get_text(l_json_item_obj, "hldr_addr"); + const char *l_signing_addr_str = s_json_get_text(l_json_item_obj, "signing_addr"); + dap_chain_addr_t *l_fee_addr = dap_chain_addr_from_str(l_fee_addr_str); + dap_chain_addr_t *l_hldr_addr = dap_chain_addr_from_str(l_hldr_addr_str); + dap_chain_addr_t *l_signing_addr = dap_chain_addr_from_str(l_signing_addr_str); + if(!l_fee_addr || !l_hldr_addr || !l_signing_addr) { + 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)) { + break; + } + 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_fee_value, + l_fee_addr, l_hldr_addr, l_signing_addr, &l_signer_node_addr); + l_item = (const uint8_t*) l_out_cond_item; + } + break; + case DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE: { + uint256_t l_value = { }; + bool l_is_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; + } + } + 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); + break; + } + } + + break; + case TX_ITEM_TYPE_SIG:{ + // Save item obj for sign + l_sign_list = dap_list_append(l_sign_list,l_json_item_obj); + } + break; + //case TX_ITEM_TYPE_PKEY: + //break; + //case TX_ITEM_TYPE_TOKEN: + //break; + //case TX_ITEM_TYPE_TOKEN_EXT: + //break; + //case TX_ITEM_TYPE_RECEIPT: + //break; + } + // Add item to transaction + if(l_item) { + dap_chain_datum_tx_add_item(&l_tx, (const uint8_t*) l_item); + l_items_ready++; + } + } + + // Add signs + dap_list_t *l_list = l_sign_list; + while(l_list){ + bool is_add = false; + struct json_object *l_json_item_obj = (struct json_object*) l_list->data; + // From wallet + dap_chain_wallet_t *l_wallet = s_json_get_wallet(l_json_item_obj, "wallet"); + if(l_wallet) { + dap_enc_key_t *l_enc_key = dap_chain_wallet_get_key(l_wallet, 0); + // sign all previous items in transaction + if(dap_chain_datum_tx_add_sign_item(&l_tx, l_enc_key)>0){ + is_add = true; + l_items_ready++; + } + dap_chain_wallet_close(l_wallet); + } + // If wallet not found + if(!is_add) { + // From cert + const dap_cert_t *l_cert = s_json_get_cert(l_json_item_obj, "cert"); + if(l_cert && l_cert->enc_key) { + // sign all previous items in transaction + if(dap_chain_datum_tx_add_sign_item(&l_tx, l_cert->enc_key) > 0) { + is_add = true; + l_items_ready++; + } + } + } + l_list = dap_list_next(l_list); + } + dap_list_free(l_sign_list); + json_object_put(l_json); + + if(l_items_ready<l_items_count) { + if(!l_items_ready) + dap_chain_node_cli_set_reply_text(a_str_reply, "No valid items found to create a transaction"); + else + dap_chain_node_cli_set_reply_text(a_str_reply, "Can't create transaction, because only %zu items out of %zu are valid",l_items_ready,l_items_count); + DAP_DELETE(l_tx); + return -30; + } + + // Pack transaction into the datum + dap_chain_datum_t *l_datum_tx = dap_chain_datum_create(DAP_CHAIN_DATUM_TX, l_tx, dap_chain_datum_tx_get_size(l_tx)); + size_t l_datum_tx_size = dap_chain_datum_size(l_datum_tx); + DAP_DELETE(l_tx); + + // Add transaction to mempool + char *l_gdb_group_mempool_base_tx = dap_chain_net_get_gdb_group_mempool(l_chain);// get group name for mempool + dap_chain_hash_fast_t *l_datum_tx_hash = DAP_NEW(dap_hash_fast_t); + dap_hash_fast(l_datum_tx, l_datum_tx_size, l_datum_tx_hash);// Calculate datum hash + char *l_tx_hash_str = dap_chain_hash_fast_to_str_new(l_datum_tx_hash); + bool l_placed = dap_chain_global_db_gr_set(l_tx_hash_str, l_datum_tx, l_datum_tx_size, l_gdb_group_mempool_base_tx); + + DAP_DELETE(l_tx_hash_str); + DAP_DELETE(l_datum_tx); + DAP_DELETE(l_gdb_group_mempool_base_tx); + if(!l_placed) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Can't add transaction to mempool"); + return -90; + } + // Completed successfully + dap_chain_node_cli_set_reply_text(a_str_reply, "Transaction with %d items created and added to mempool successfully", l_items_ready); + return l_err_code; +} /** * @brief Create transaction diff --git a/modules/net/include/dap_chain_node_cli_cmd.h b/modules/net/include/dap_chain_node_cli_cmd.h index ea22439219..36b75456e6 100644 --- a/modules/net/include/dap_chain_node_cli_cmd.h +++ b/modules/net/include/dap_chain_node_cli_cmd.h @@ -110,6 +110,7 @@ int com_tx_wallet(int argc, char ** argv, char **str_reply); * Create transaction */ int com_tx_create(int argc, char ** argv, char **str_reply); +int com_tx_create_json(int argc, char ** argv, char **str_reply); int com_tx_cond_create(int argc, char ** argv, char **str_reply); /** diff --git a/modules/net/srv/dap_chain_net_srv.c b/modules/net/srv/dap_chain_net_srv.c index 56114f311e..2cf389562e 100644 --- a/modules/net/srv/dap_chain_net_srv.c +++ b/modules/net/srv/dap_chain_net_srv.c @@ -76,6 +76,7 @@ static dap_chain_net_srv_uid_t * m_uid; typedef struct service_list { dap_chain_net_srv_uid_t uid; dap_chain_net_srv_t * srv; + char name[32]; UT_hash_handle hh; } service_list_t; @@ -732,6 +733,7 @@ dap_chain_net_srv_t* dap_chain_net_srv_add(dap_chain_net_srv_uid_t a_uid, pthread_mutex_init(&l_srv->banlist_mutex, NULL); l_sdata = DAP_NEW_Z(service_list_t); memcpy(&l_sdata->uid, &l_uid, sizeof(l_uid)); + strncpy(l_sdata->name, a_config_section, sizeof(l_sdata->name)); l_sdata->srv = l_srv; dap_chain_net_srv_parse_pricelist(l_srv, a_config_section); HASH_ADD(hh, s_srv_list, uid, sizeof(l_srv->uid), l_sdata); @@ -877,6 +879,26 @@ dap_chain_net_srv_t * dap_chain_net_srv_get(dap_chain_net_srv_uid_t a_uid) return (l_sdata) ? l_sdata->srv : NULL; } +/** + * @brief dap_chain_net_srv_get_by_name + * @param a_client + */ +dap_chain_net_srv_t* dap_chain_net_srv_get_by_name(const char *a_name) +{ + if(!a_name) + return NULL; + dap_chain_net_srv_t *l_srv = NULL; + service_list_t *l_sdata, *l_sdata_tmp; + pthread_mutex_lock(&s_srv_list_mutex); + HASH_ITER(hh, s_srv_list , l_sdata, l_sdata_tmp) + { + if(!dap_strcmp(l_sdata->name, a_name)) + l_srv = l_sdata->srv; + } + pthread_mutex_unlock(&s_srv_list_mutex); + return l_srv; +} + /** * @brief dap_chain_net_srv_count * @return diff --git a/modules/net/srv/include/dap_chain_net_srv.h b/modules/net/srv/include/dap_chain_net_srv.h index 741cde9068..faadfcdbbd 100755 --- a/modules/net/srv/include/dap_chain_net_srv.h +++ b/modules/net/srv/include/dap_chain_net_srv.h @@ -279,6 +279,7 @@ void dap_chain_net_srv_call_closed_all(dap_stream_ch_t * a_client); void dap_chain_net_srv_call_opened_all(dap_stream_ch_t * a_client); dap_chain_net_srv_t * dap_chain_net_srv_get(dap_chain_net_srv_uid_t a_uid); +dap_chain_net_srv_t* dap_chain_net_srv_get_by_name(const char *a_name); size_t dap_chain_net_srv_count(void); const dap_chain_net_srv_uid_t * dap_chain_net_srv_list(void); dap_chain_datum_tx_receipt_t * dap_chain_net_srv_issue_receipt(dap_chain_net_srv_t *a_srv, @@ -300,6 +301,24 @@ DAP_STATIC_INLINE const char * dap_chain_net_srv_price_unit_uid_to_str( dap_chai } } +DAP_STATIC_INLINE dap_chain_net_srv_price_unit_uid_t dap_chain_net_srv_price_unit_uid_from_str( const char *a_unit_str ) +{ + dap_chain_net_srv_price_unit_uid_t l_price_unit = { .enm = SERV_UNIT_UNDEFINED }; + if(!dap_strcmp(a_unit_str, "mb")) + l_price_unit.enm = SERV_UNIT_MB; + else if(!dap_strcmp(a_unit_str, "sec")) + l_price_unit.enm = SERV_UNIT_SEC; + else if(!dap_strcmp(a_unit_str, "day")) + l_price_unit.enm = SERV_UNIT_DAY; + else if(!dap_strcmp(a_unit_str, "kb")) + l_price_unit.enm = SERV_UNIT_KB; + else if(!dap_strcmp(a_unit_str, "b") || !dap_strcmp(a_unit_str, "bytes")) + l_price_unit.enm = SERV_UNIT_B; + else if(!dap_strcmp(a_unit_str, "pcs") || !dap_strcmp(a_unit_str, "pieces")) + l_price_unit.enm = SERV_UNIT_PCS; + return l_price_unit; +} + DAP_STATIC_INLINE bool dap_chain_net_srv_uid_compare(dap_chain_net_srv_uid_t a, dap_chain_net_srv_uid_t b) { #if DAP_CHAIN_NET_SRV_UID_SIZE == 8 diff --git a/modules/service/stake/dap_chain_net_srv_stake.c b/modules/service/stake/dap_chain_net_srv_stake.c index 6538a04c0a..fba31770d7 100644 --- a/modules/service/stake/dap_chain_net_srv_stake.c +++ b/modules/service/stake/dap_chain_net_srv_stake.c @@ -37,6 +37,10 @@ #define LOG_TAG "dap_chain_net_srv_stake" static int s_cli_srv_stake(int a_argc, char **a_argv, char **a_str_reply); +static int s_callback_requested(dap_chain_net_srv_t *a_srv, uint32_t a_usage_id, dap_chain_net_srv_client_remote_t *a_srv_client, const void *a_data, size_t a_data_size); +static int s_callback_response_success(dap_chain_net_srv_t *a_srv, uint32_t a_usage_id, dap_chain_net_srv_client_remote_t *a_srv_client, const void *a_data, size_t a_data_size); +static int s_callback_response_error(dap_chain_net_srv_t *a_srv, uint32_t a_usage_id, dap_chain_net_srv_client_remote_t *a_srv_client, const void *a_data, size_t a_data_size); +static int s_callback_receipt_next_success(dap_chain_net_srv_t *a_srv, uint32_t a_usage_id, dap_chain_net_srv_client_remote_t *a_srv_client, const void *a_data, size_t a_data_size); static dap_chain_net_srv_stake_t *s_srv_stake = NULL; @@ -73,7 +77,13 @@ int dap_chain_net_srv_stake_init() ); s_srv_stake = DAP_NEW_Z(dap_chain_net_srv_stake_t); - + + dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_STAKE_ID }; + dap_chain_net_srv_t* l_srv = dap_chain_net_srv_add(l_uid, "srv_stake", s_callback_requested, + s_callback_response_success, s_callback_response_error, + s_callback_receipt_next_success, NULL); + l_srv->_internal = s_srv_stake; + uint16_t l_net_count; dap_chain_net_t **l_net_list = dap_chain_net_list(&l_net_count); for (uint16_t i = 0; i < l_net_count; i++) { @@ -1475,3 +1485,23 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, char **a_str_reply) } return 0; } + +static int s_callback_requested(dap_chain_net_srv_t *a_srv, uint32_t a_usage_id, dap_chain_net_srv_client_remote_t *a_srv_client, const void *a_data, size_t a_data_size) +{ + return 0; +} + +static int s_callback_response_success(dap_chain_net_srv_t *a_srv, uint32_t a_usage_id, dap_chain_net_srv_client_remote_t *a_srv_client, const void *a_data, size_t a_data_size) +{ + return 0; +} + +static int s_callback_response_error(dap_chain_net_srv_t *a_srv, uint32_t a_usage_id, dap_chain_net_srv_client_remote_t *a_srv_client, const void *a_data, size_t a_data_size) +{ + return 0; +} + +static int s_callback_receipt_next_success(dap_chain_net_srv_t *a_srv, uint32_t a_usage_id, dap_chain_net_srv_client_remote_t *a_srv_client, const void *a_data, size_t a_data_size) +{ + return 0; +} -- GitLab