Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • cellframe/cellframe-sdk
  • MIKA83/cellframe-sdk
2 results
Show changes
Commits on Source (17)
Showing
with 440 additions and 252 deletions
......@@ -1672,15 +1672,17 @@ const dap_chain_datum_tx_t* dap_chain_ledger_tx_find_by_pkey(dap_ledger_t *a_led
* a_addr[in] wallet address, whose owner can use the service
*/
dap_chain_datum_tx_t* dap_chain_ledger_tx_cache_find_out_cond(dap_ledger_t *a_ledger,
dap_chain_addr_t *a_addr, dap_chain_hash_fast_t *a_tx_first_hash)
dap_chain_hash_fast_t *a_tx_first_hash, dap_chain_tx_out_cond_t **a_out_cond, int *a_out_cond_idx)
{
if(!a_addr || !a_tx_first_hash)
if (!a_tx_first_hash)
return NULL;
dap_ledger_private_t *l_ledger_priv = PVT(a_ledger);
dap_chain_datum_tx_t *l_cur_tx = NULL;
bool is_null_hash = dap_hash_fast_is_blank(a_tx_first_hash);
bool is_search_enable = is_null_hash;
dap_chain_ledger_tx_item_t *l_iter_current, *l_item_tmp;
dap_chain_tx_out_cond_t *l_tx_out_cond;
int l_tx_out_cond_idx;
pthread_rwlock_wrlock(&l_ledger_priv->ledger_rwlock);
HASH_ITER(hh, l_ledger_priv->ledger_items, l_iter_current, l_item_tmp)
{
......@@ -1692,10 +1694,8 @@ dap_chain_datum_tx_t* dap_chain_ledger_tx_cache_find_out_cond(dap_ledger_t *a_le
is_search_enable = true;
continue;
}
// Get sign item from transaction
int l_tx_out_cond_size = 0;
dap_chain_tx_out_cond_t *l_tx_out_cond = (dap_chain_tx_out_cond_t*) dap_chain_datum_tx_item_get(
l_tx_tmp, NULL, TX_ITEM_TYPE_OUT_COND, &l_tx_out_cond_size);
// Get out_cond item from transaction
l_tx_out_cond = dap_chain_datum_tx_out_cond_get(l_tx_tmp, &l_tx_out_cond_idx);
if(l_tx_out_cond) {
l_cur_tx = l_tx_tmp;
......@@ -1704,6 +1704,12 @@ dap_chain_datum_tx_t* dap_chain_ledger_tx_cache_find_out_cond(dap_ledger_t *a_le
}
}
pthread_rwlock_unlock(&l_ledger_priv->ledger_rwlock);
if (a_out_cond) {
*a_out_cond = l_tx_out_cond;
}
if (a_out_cond_idx) {
*a_out_cond_idx = l_tx_out_cond_idx;
}
return l_cur_tx;
}
......@@ -1727,22 +1733,21 @@ uint64_t dap_chain_ledger_tx_cache_get_out_cond_value(dap_ledger_t *a_ledger, da
//memcpy(&l_tx_first_hash, 0, sizeof(dap_chain_hash_fast_t));
/* size_t l_pub_key_size = a_key_from->pub_key_data_size;
uint8_t *l_pub_key = dap_enc_key_serealize_pub_key(a_key_from, &l_pub_key_size);*/
dap_chain_tx_out_cond_t *l_tx_out_cond;
// Find all transactions
do {
l_tx_tmp = dap_chain_ledger_tx_cache_find_out_cond(a_ledger, a_addr, &l_tx_first_hash);
l_tx_tmp = dap_chain_ledger_tx_cache_find_out_cond(a_ledger, &l_tx_first_hash, &l_tx_out_cond, NULL);
// Get out_cond item from transaction
if(l_tx_tmp) {
dap_chain_tx_out_cond_t *l_tx_out_cond =(dap_chain_tx_out_cond_t *)dap_chain_datum_tx_item_get(
l_tx_tmp, NULL, TX_ITEM_TYPE_OUT_COND, NULL);
UNUSED(a_addr);
// TODO check relations a_addr with cond_data and public key
if(l_tx_out_cond) {
l_ret_value += l_tx_out_cond->header.value;
if(tx_out_cond)
*tx_out_cond = (dap_chain_tx_out_cond_t*) l_tx_out_cond;
*tx_out_cond = l_tx_out_cond;
}
}
} while(l_tx_tmp);
......
......@@ -175,8 +175,7 @@ const dap_chain_datum_tx_t* dap_chain_ledger_tx_find_by_pkey(dap_ledger_t *a_led
char *a_public_key, size_t a_public_key_size, dap_chain_hash_fast_t *a_tx_first_hash);
// Get the transaction in the cache with the out_cond item
dap_chain_datum_tx_t* dap_chain_ledger_tx_cache_find_out_cond(dap_ledger_t *a_ledger,
dap_chain_addr_t *a_addr, dap_chain_hash_fast_t *a_tx_first_hash);
dap_chain_datum_tx_t* dap_chain_ledger_tx_cache_find_out_cond(dap_ledger_t *a_ledger, dap_chain_hash_fast_t *a_tx_first_hash, dap_chain_tx_out_cond_t **a_out_cond, int *a_out_cond_idx);
// Get the value from all transactions in the cache with out_cond item
uint64_t dap_chain_ledger_tx_cache_get_out_cond_value(dap_ledger_t *a_ledger, dap_chain_addr_t *a_addr,
......
......@@ -432,27 +432,27 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch , void* a_arg)
char * l_tx_in_hash_str = dap_chain_hash_fast_to_str_new(l_tx_in_hash);
log_it(L_NOTICE, "Formed tx %s for input with active receipt", l_tx_in_hash_str);
// We could put transaction directly to chains
/* We could put transaction directly to chains
if ( dap_chain_net_get_role( l_usage->net ).enums == NODE_ROLE_MASTER ||
dap_chain_net_get_role( l_usage->net ).enums == NODE_ROLE_CELL_MASTER ||
dap_chain_net_get_role( l_usage->net ).enums == NODE_ROLE_ROOT ||
dap_chain_net_get_role( l_usage->net ).enums == NODE_ROLE_ROOT_MASTER ){
dap_chain_net_proc_mempool( l_usage->net);
}
}*/
DAP_DELETE(l_tx_in_hash_str);
}else
log_it(L_ERROR, "Can't create input tx cond transaction!");
if (l_tx_in_hash)
DAP_DELETE(l_tx_in_hash);
size_t l_success_size = sizeof (dap_stream_ch_chain_net_srv_pkt_success_hdr_t );
size_t l_success_size = sizeof(dap_stream_ch_chain_net_srv_pkt_success_hdr_t) + sizeof(dap_chain_hash_fast_t);
dap_stream_ch_chain_net_srv_pkt_success_t *l_success = DAP_NEW_Z_SIZE(dap_stream_ch_chain_net_srv_pkt_success_t,
l_success_size);
l_success->hdr.usage_id = l_usage->id;
l_success->hdr.net_id.uint64 = l_usage->net->pub.id.uint64;
l_success->hdr.srv_uid.uint64 = l_usage->service->uid.uint64;
if (l_tx_in_hash) {
memcpy(l_success->custom_data, l_tx_in_hash, sizeof(dap_chain_hash_fast_t));
DAP_DELETE(l_tx_in_hash);
}
dap_stream_ch_pkt_write( a_ch, DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_RESPONSE_SUCCESS ,
l_success, l_success_size);
DAP_DELETE(l_success);
......
......@@ -19,7 +19,6 @@ SOURCES += $$PWD/dap_chain_common.c \
$$PWD/dap_chain_datum_token.c \
$$PWD/dap_chain_datum_tx.c \
$$PWD/dap_chain_datum_tx_items.c \
$$PWD/dap_chain_datum_tx_out_cond.c \
$$PWD/dap_chain_datum_tx_receipt.c \
$$PWD/dap_chain_datum_tx_token.c
......
......@@ -361,23 +361,64 @@ dap_chain_hash_fast_t* dap_chain_mempool_tx_create_cond_input(dap_chain_net_t *
uint16_t pos=0;
dap_chain_datum_tx_add_item(&l_tx, (byte_t*) l_receipt);
pos++;
uint64_t l_value_send = l_receipt->receipt_info.value_datoshi;
// add 'in_cond' items
dap_chain_datum_tx_t *l_cond_tx = dap_chain_ledger_tx_find_by_hash(l_ledger, a_tx_prev_hash);
dap_chain_datum_tx_t *l_tx_cond = dap_chain_ledger_tx_find_by_hash(l_ledger, a_tx_prev_hash);
int l_prev_cond_idx;
dap_chain_datum_tx_out_cond_get(l_cond_tx, &l_prev_cond_idx);
dap_chain_tx_out_cond_t *l_out_cond = dap_chain_datum_tx_out_cond_get(l_tx_cond, &l_prev_cond_idx);
if (dap_chain_ledger_tx_hash_is_used_out_item(l_ledger, a_tx_prev_hash, l_prev_cond_idx)) {
dap_chain_datum_tx_t *l_tx_tmp;
dap_chain_hash_fast_t l_tx_cur_hash = { 0 }; // start hash
dap_chain_tx_out_cond_t *l_tmp_cond;
uint64_t l_value_cond = 0;
int l_tmp_cond_idx;
// Find all transactions
while (l_value_cond < l_value_send) {
l_tx_tmp = dap_chain_ledger_tx_cache_find_out_cond(l_ledger, &l_tx_cur_hash, &l_tmp_cond, &l_tmp_cond_idx);
if (!l_tx_tmp) {
break;
}
if (dap_chain_ledger_tx_hash_is_used_out_item(l_ledger, &l_tx_cur_hash, l_tmp_cond_idx))
continue;
if (l_tmp_cond->header.subtype != l_out_cond->header.subtype)
continue;
if (l_tmp_cond->subtype.srv_pay.srv_uid.uint64 != l_out_cond->subtype.srv_pay.srv_uid.uint64)
continue;
if (l_tmp_cond->subtype.srv_pay.unit.uint32 != l_out_cond->subtype.srv_pay.unit.uint32)
continue;
if (l_tmp_cond->subtype.srv_pay.unit_price_max_datoshi != l_out_cond->subtype.srv_pay.unit_price_max_datoshi)
continue;
if (memcmp(&l_tmp_cond->subtype.srv_pay.pkey_hash, &l_out_cond->subtype.srv_pay.pkey_hash, sizeof(dap_chain_hash_fast_t)))
continue;
l_value_cond = l_tmp_cond->header.value;
}
if (l_value_cond < l_value_send) {
log_it(L_WARNING, "Requested conditional transaction is already used out");
return NULL;
}
}
if (dap_chain_datum_tx_add_in_cond_item(&l_tx, a_tx_prev_hash, l_prev_cond_idx, pos-1) != 0 ){
dap_chain_datum_tx_delete(l_tx);
log_it( L_ERROR, "Cant add tx cond input");
return NULL;
}
if(dap_chain_datum_tx_add_out_item(&l_tx, a_addr_to, l_receipt->receipt_info.value_datoshi) != 1) {
// add 'out' item
if (dap_chain_datum_tx_add_out_item(&l_tx, a_addr_to, l_value_send) != 1) {
dap_chain_datum_tx_delete(l_tx);
log_it( L_ERROR, "Cant add tx output");
return NULL;
}
//add 'out_cond' item
size_t l_size = dap_chain_datum_item_tx_get_size((uint8_t *)l_out_cond);
dap_chain_tx_out_cond_t *l_out_cond_new = DAP_NEW_Z_SIZE(dap_chain_tx_out_cond_t, l_size);
memcpy(l_out_cond_new, l_out_cond, l_size);
l_out_cond_new->header.value -= l_value_send;
dap_chain_datum_tx_add_item(&l_tx, (const uint8_t *)l_out_cond_new);
DAP_DELETE(l_out_cond_new);
// add 'sign' items
if (l_key_tx_sign){
if(dap_chain_datum_tx_add_sign_item(&l_tx, l_key_tx_sign) != 1) {
......@@ -394,7 +435,13 @@ dap_chain_hash_fast_t* dap_chain_mempool_tx_create_cond_input(dap_chain_net_t *
DAP_DELETE( l_tx );
char * l_key_str = dap_chain_hash_fast_to_str_new( l_key_hash );
char * l_gdb_group = dap_chain_net_get_gdb_group_mempool_by_chain_type( a_net ,CHAIN_TYPE_TX);
char * l_gdb_group;
if(a_net->pub.default_chain)
l_gdb_group = dap_chain_net_get_gdb_group_mempool(a_net->pub.default_chain);
else
l_gdb_group = dap_chain_net_get_gdb_group_mempool_by_chain_type( a_net ,CHAIN_TYPE_TX);
if( dap_chain_global_db_gr_set( dap_strdup(l_key_str), (uint8_t *) l_datum, dap_chain_datum_size(l_datum)
, l_gdb_group ) ) {
log_it(L_NOTICE, "Transaction %s placed in mempool", l_key_str);
......@@ -874,7 +921,6 @@ void chain_mempool_proc(struct dap_http_simple *cl_st, void * arg)
{
dap_datum_mempool_free(datum_mempool);
char *a_key = calc_datum_hash(request_str, (size_t) request_size);
char *a_value;
switch (action)
{
case DAP_DATUM_MEMPOOL_ADD: // add datum in base
......
......@@ -78,6 +78,7 @@ static int bugreport_write_to_file(byte_t *a_request_byte, size_t a_request_size
static void bugreport_http_proc(struct dap_http_simple *a_http_simple, void * a_arg)
{
// data:text/html,<form action=http://192.168.100.92:8079/bugreport/ method=post><input name=a></form>
// data:text/html,<form action=http://cdb.klvn.io/bugreport/ method=post><input name=a></form>
log_it(L_DEBUG, "bugreport_http_proc request");
http_status_code_t * return_code = (http_status_code_t*) a_arg;
//if(dap_strcmp(cl_st->http->url_path, BUGREPORT_URL) == 0 )
......
......@@ -957,11 +957,11 @@ int dap_chain_node_cli_init(dap_config_t * g_config)
dap_chain_node_cli_cmd_item_create ("exit", com_exit, NULL, "Stop application and exit",
"exit\n" );
#ifndef _WIN32
//#ifndef _WIN32
// News
//dap_chain_node_cli_cmd_item_create("news", com_news, NULL, "Add News for VPN clients. Language code is a text code like \"en\", \"ru\", \"fr\"",
// "news [-text <news text> | -file <filename with news>] -lang <language code> \n");
#endif
dap_chain_node_cli_cmd_item_create("news", com_news, NULL, "Add News for VPN clients. Language code is a text code like \"en\", \"ru\", \"fr\"",
"news [-text <news text> | -file <filename with news>] -lang <language code> \n");
//#endif
// create thread for waiting of clients
pthread_t l_thread_id;
......
......@@ -133,10 +133,10 @@ int com_stats(int argc, char ** argv, void *arg_func, char **str_reply);
int com_exit(int argc, char ** argv, void *arg_func, char **str_reply);
#ifndef _WIN32
//#ifndef _WIN32
// Add News for VPN clients
//int com_news(int a_argc, char ** a_argv, void *a_arg_func, char **a_str_reply);
#endif
int com_news(int a_argc, char ** a_argv, void *a_arg_func, char **a_str_reply);
//#endif
// vpn_client command
int com_vpn_client(int a_argc, char ** a_argv, void *arg_func, char **a_str_reply);
......
......@@ -37,6 +37,9 @@
#define LOG_TAG "chain_net_srv_geoip"
#define LOCALE_DEFAULT "en"
static char *s_geoip_db_file_path = NULL; // share/geoip/GeoLite2-City.mmdb
/**
* @brief m_request_response
* @param a_response
......@@ -168,18 +171,18 @@ geoip_info_t *chain_net_geoip_get_ip_info_by_local_db(const char *a_ip_str, cons
{
// https://geoip.maxmind.com/geoip/v2.1/city/178.7.88.55
// https://maxmind.github.io/libmaxminddb/
char *l_file_db_name = dap_strdup_printf("%s/share/geoip/GeoLite2-City.mmdb", g_sys_dir_path);
if(!dap_file_test(l_file_db_name)) {
DAP_DELETE(l_file_db_name);
//char *l_file_db_name = dap_strdup_printf("%s/share/geoip/GeoLite2-City.mmdb", g_sys_dir_path);
if(!dap_file_test(s_geoip_db_file_path)) {
//DAP_DELETE(l_file_db_name);
return NULL ;
}
MMDB_s mmdb;
int l_status = MMDB_open(l_file_db_name, MMDB_MODE_MMAP, &mmdb);
int l_status = MMDB_open(s_geoip_db_file_path, MMDB_MODE_MMAP, &mmdb);
if(MMDB_SUCCESS != l_status) {
log_it(L_WARNING, "geoip file %s opened with errcode=%d", l_file_db_name, l_status);
log_it(L_WARNING, "geoip file %s opened with errcode=%d", s_geoip_db_file_path, l_status);
return NULL ;
}
DAP_DELETE(l_file_db_name);
//DAP_DELETE(l_file_db_name);
geoip_info_t *l_ret = DAP_NEW_Z(geoip_info_t);
......@@ -252,3 +255,25 @@ geoip_info_t *chain_net_geoip_get_ip_info(const char *a_ip_str)
return chain_net_geoip_get_ip_info_by_local_db(a_ip_str, "en");
//return chain_net_geoip_get_ip_info_by_web(a_ip_str);
}
int chain_net_geoip_init(dap_config_t *a_config)
{
s_geoip_db_file_path = dap_strdup_printf("%s/%s", g_sys_dir_path,
dap_config_get_item_str(g_config, "resources", "geoip_db_path"));
if(!dap_file_test(s_geoip_db_file_path)) {
log_it(L_ERROR, "No exists geoip db file %s", s_geoip_db_file_path);
DAP_DELETE(s_geoip_db_file_path);
s_geoip_db_file_path = NULL;
return -1;
}
MMDB_s mmdb;
int l_status = MMDB_open(s_geoip_db_file_path, MMDB_MODE_MMAP, &mmdb);
if(MMDB_SUCCESS != l_status) {
log_it(L_WARNING, "geoip file %s opened with errcode=%d", s_geoip_db_file_path, l_status);
DAP_DELETE(s_geoip_db_file_path);
s_geoip_db_file_path = NULL;
return -2;
}
return 0;
}
......@@ -22,6 +22,8 @@
along with any DAP based project. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dap_config.h"
typedef struct geoip_info {
char ip_str[20];
......@@ -35,3 +37,5 @@ typedef struct geoip_info {
} geoip_info_t;
geoip_info_t *chain_net_geoip_get_ip_info(const char *a_ip_str);
int chain_net_geoip_init(dap_config_t *a_config);
......@@ -156,7 +156,7 @@ static void s_http_simple_proc(dap_http_simple_t *a_http_simple, void *a_arg)
dap_string_t *l_reply_str = dap_string_new("[\n");
char *l_client_ip = a_http_simple->http->client->s_ip;//"77.222.110.44"
char *l_client_ip = a_http_simple->http->client->s_ip;//"64.225.61.216"
geoip_info_t *l_geoip_info = chain_net_geoip_get_ip_info(l_client_ip);
log_it(L_DEBUG, "Have %zd chain networks for cdb lists", s_cdb_net_count );
......@@ -232,28 +232,39 @@ static void s_http_simple_proc(dap_http_simple_t *a_http_simple, void *a_arg)
int8_t l_client_continent = l_geoip_info ? dap_chain_net_srv_order_continent_to_num(l_geoip_info->continent) : 0;
// random node on client's continent
if (l_client_continent) {
int l_count = 0;
while (l_orders_num > 0) {
size_t k = rand() % l_continents_numbers[l_client_continent];
dap_chain_net_srv_order_t *l_order = l_orders_pos[k];
const char *country_code = dap_chain_net_srv_order_get_country_code(l_order);
if (country_code) {
// only for other countries
if (dap_strcmp(l_geoip_info->country_code, country_code)){
if (!order_info_print(l_reply_str, l_net, l_order, "Auto", -1)) {
dap_string_append_printf(l_reply_str, ",\n");
break;
}
}
}
if (l_count > 20)
break;
l_count++;
}
if(l_client_continent > 0 && l_continents_numbers[l_client_continent] > 1) {
int l_count = 0;
while(l_orders_num > 0) {
size_t k = rand() % l_continents_numbers[l_client_continent];
size_t l_node_pos = -1;
for(size_t j2 = 0; j2 <= l_orders_num; j2++) {
if(k == l_node_numbering[l_client_continent][j2]) {
l_node_pos = j2;
break;
}
}
if(l_node_pos == -1) {
// random node for the whole world
l_node_pos = rand() % l_orders_num;
}
dap_chain_net_srv_order_t *l_order = l_orders_pos[l_node_pos];
const char *country_code = dap_chain_net_srv_order_get_country_code(l_order);
if(country_code) {
// only for other countries
if(dap_strcmp(l_geoip_info->country_code, country_code)) {
if(!order_info_print(l_reply_str, l_net, l_order, "Auto", -1)) {
dap_string_append_printf(l_reply_str, ",\n");
break;
}
}
}
if(l_count > 20)
break;
l_count++;
}
}
// random node for the whole world
}
// random node for the whole world
else {
int l_count = 0;
while(l_orders_num > 0) {
......
......@@ -22,6 +22,7 @@
along with any DAP based project. If not, see <http://www.gnu.org/licenses/>.
*/
#include <math.h>
#include "dap_string.h"
#include "dap_chain_common.h"
#include "dap_chain_node_cli.h"
......@@ -36,6 +37,7 @@ static int s_callback_requested(dap_chain_net_srv_t *a_srv, uint32_t a_usage_id,
static int s_callback_response_success(dap_chain_net_srv_t *a_srv, uint32_t a_usage_id, dap_chain_net_srv_client_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_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_t *a_srv_client, const void *a_data, size_t a_data_size);
static dap_chain_net_srv_xchange_price_t *s_xchange_db_load(char *a_key, uint8_t *a_item);
static dap_chain_net_srv_xchange_t *s_srv_xchange;
......@@ -49,19 +51,19 @@ int dap_chain_net_srv_xchange_init()
{
dap_chain_node_cli_cmd_item_create("srv_xchange", s_cli_srv_xchange, NULL, "eXchange service commands",
"srv_xchange price create -net_sell <net name> -token_sell <token ticker> -net_buy <net_name> -token_buy <token ticker>"
"-wallet <name> -datoshi_sell <value> -datoshi_buy <value>\n"
"\tCreate a new price with specified amounts of datoshi to exchange\n"
"-wallet <name> -coins <value> -rate <value>\n"
"\tCreate a new price with specified amount of datoshi to exchange with specified rate (sell : buy)\n"
"srv_xchange price remove -net_sell <net name> -token_sell <token ticker> -net_buy <net_name> -token_buy <token ticker>\n"
"\tRemove price with specified tickers within specified net names\n"
"srv_xchange price list\n"
"\tList all active prices\n"
"srv_xchange price update -net_sell <net name> -token_sell <token ticker> -net_buy <net_name> -token_buy <token ticker>"
"{-datoshi_sell <value> | datoshi_buy <value> | -wallet <name>}\n"
"{-coins <value> | rate <value> | -wallet <name>}\n"
"\tUpdate price with specified tickers within specified net names\n"
"srv_xchange orders -net <net name>\n"
"\tGet the exchange orders list within specified net name\n"
"srv_xchange purchase -order <order hash> -net <net name> -wallet <wallet_name>\n"
"\tExchange tokens with specified order within specified net name\n"
"srv_xchange purchase -order <order hash> -net <net name> -wallet <wallet_name> -coins <value>\n"
"\tExchange tokens with specified order within specified net name. Specify how datoshies to buy\n"
"srv_xchange enable\n"
"\tEnable eXchange service\n"
"srv_xchange disable\n"
......@@ -70,9 +72,16 @@ int dap_chain_net_srv_xchange_init()
dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_XCHANGE_ID };
dap_chain_net_srv_t* l_srv = dap_chain_net_srv_add(l_uid, s_callback_requested, s_callback_response_success,
s_callback_response_error, s_callback_receipt_next_success);
s_srv_xchange = DAP_NEW_Z(dap_chain_net_srv_xchange_t);
s_srv_xchange = DAP_NEW_Z(dap_chain_net_srv_xchange_t);
l_srv->_inhertor = s_srv_xchange;
s_srv_xchange->enabled = false;
size_t l_prices_count = 0;
dap_global_db_obj_t *l_prices = dap_chain_global_db_gr_load(GROUP_LOCAL_XCHANGE, &l_prices_count);
for (size_t i = 0; i < l_prices_count; i++) {
dap_chain_net_srv_xchange_price_t *l_price = s_xchange_db_load(l_prices[i].key, l_prices[i].value);
HASH_ADD_KEYPTR(hh, s_srv_xchange->pricelist, l_price->key_ptr, strlen(l_price->key_ptr), l_price);
}
dap_chain_global_db_objs_delete(l_prices, l_prices_count);
return 0;
}
......@@ -104,18 +113,27 @@ bool dap_chain_net_srv_xchange_verificator(dap_chain_tx_out_cond_t *a_cond, dap_
return true;
} else {
dap_list_t *l_list_out = dap_chain_datum_tx_items_get(a_tx, TX_ITEM_TYPE_OUT_EXT, NULL);
uint64_t l_out_val = 0;
for (dap_list_t *l_list_tmp = l_list_out;l_list_tmp; l_list_tmp = l_list_tmp->next) {
long double l_seller_rate = (long double)a_cond->header.value / a_cond->subtype.srv_xchange.value;
uint64_t l_out_val = 0, l_back_val = 0;
char *l_ticker_ctrl = NULL;
for (dap_list_t *l_list_tmp = l_list_out; l_list_tmp; l_list_tmp = l_list_tmp->next) {
dap_chain_tx_out_ext_t *l_tx_out = (dap_chain_tx_out_ext_t *)l_list_tmp->data;
if (memcmp(&l_tx_out->addr, &a_cond->params, sizeof(dap_chain_addr_t)) ||
strcmp(l_tx_out->token, a_cond->subtype.srv_xchange.token)) {
if (memcmp(&l_tx_out->addr, &a_cond->params, sizeof(dap_chain_addr_t))) {
continue;
}
l_out_val += l_tx_out->header.value;
if (strcmp(l_tx_out->token, a_cond->subtype.srv_xchange.token)) {
if (l_ticker_ctrl && strcmp(l_ticker_ctrl, l_tx_out->token)) {
return false; // too many tokens
}
l_ticker_ctrl = l_tx_out->token;
l_back_val += l_tx_out->header.value;
} else { // buying token
l_out_val += l_tx_out->header.value;
}
}
if (l_out_val != a_cond->subtype.srv_xchange.value) {
return false;
long double l_buyer_rate = (a_cond->header.value - l_back_val) / (long double)l_out_val;
if (l_seller_rate < l_buyer_rate) {
return false; // wrong changing rate
}
}
return true;
......@@ -125,11 +143,12 @@ static dap_chain_datum_tx_receipt_t *s_xchage_receipt_create(dap_chain_net_srv_x
{
uint32_t l_ext_size = sizeof(uint64_t) + DAP_CHAIN_TICKER_SIZE_MAX;
uint8_t *l_ext = DAP_NEW_SIZE(uint8_t, l_ext_size);
dap_lendian_put64(l_ext, a_price->datoshi_sell);
strcpy((char *)&l_ext[sizeof(uint64_t)], a_price->token_sell);
uint64_t l_datoshi_buy = (long double)a_price->datoshi_sell / a_price->rate;
dap_lendian_put64(l_ext, l_datoshi_buy);
strcpy((char *)&l_ext[sizeof(uint64_t)], a_price->token_buy);
dap_chain_net_srv_price_unit_uid_t l_unit = { .uint32 = SERV_UNIT_UNDEFINED};
dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_XCHANGE_ID };
dap_chain_datum_tx_receipt_t *l_receipt = dap_chain_datum_tx_receipt_create(l_uid, l_unit, 0, a_price->datoshi_buy,
dap_chain_datum_tx_receipt_t *l_receipt = dap_chain_datum_tx_receipt_create(l_uid, l_unit, 0, a_price->datoshi_sell,
l_ext, l_ext_size);
return l_receipt;
}
......@@ -213,13 +232,14 @@ static dap_chain_datum_tx_t *s_xchange_tx_create_exchange(dap_chain_net_srv_xcha
// create empty transaction
dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create();
dap_ledger_t *l_ledger = dap_chain_ledger_by_net_name(a_price->net_sell->pub.name);
dap_chain_addr_t *l_seller_addr = (dap_chain_addr_t *) dap_chain_wallet_get_addr(a_wallet, a_price->net_sell->pub.id);
dap_ledger_t *l_ledger = dap_chain_ledger_by_net_name(a_price->net_buy->pub.name);
dap_chain_addr_t *l_seller_addr = (dap_chain_addr_t *)dap_chain_wallet_get_addr(a_wallet, a_price->net_buy->pub.id);
dap_enc_key_t *l_seller_key = dap_chain_wallet_get_key(a_wallet, 0);
uint64_t l_value_sell = 0; // how many coins to transfer
uint64_t l_value_buy = 0; // how many coins to transfer
// list of transaction with 'out' items to sell
dap_list_t *l_list_used_out = dap_chain_ledger_get_list_tx_outs_with_val(l_ledger, a_price->token_sell,
l_seller_addr, a_price->datoshi_sell, &l_value_sell);
uint64_t l_datoshi_buy = ceill(a_price->datoshi_sell / a_price->rate);
dap_list_t *l_list_used_out = dap_chain_ledger_get_list_tx_outs_with_val(l_ledger, a_price->token_buy,
l_seller_addr, l_datoshi_buy, &l_value_buy);
if(!l_list_used_out) {
dap_chain_datum_tx_delete(l_tx);
log_it(L_WARNING, "Nothing to change (not enough funds)");
......@@ -233,7 +253,7 @@ static dap_chain_datum_tx_t *s_xchange_tx_create_exchange(dap_chain_net_srv_xcha
// add 'in' items to sell
uint64_t l_value_to_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out);
dap_list_free_full(l_list_used_out, free);
if (l_value_to_items != l_value_sell) {
if (l_value_to_items != l_value_buy) {
dap_chain_datum_tx_delete(l_tx);
DAP_DELETE(l_seller_addr);
log_it(L_ERROR, "Can't compose the transaction input");
......@@ -247,21 +267,25 @@ static dap_chain_datum_tx_t *s_xchange_tx_create_exchange(dap_chain_net_srv_xcha
}
int l_prev_cond_idx;
dap_chain_tx_out_cond_t *l_tx_out_cond = dap_chain_datum_tx_out_cond_get(l_cond_tx, &l_prev_cond_idx);
if (dap_chain_ledger_tx_hash_is_used_out_item(l_ledger, &a_price->tx_hash, l_prev_cond_idx)) {
log_it(L_WARNING, "Requested conditional transaction is already used out");
return false;
}
dap_chain_datum_tx_add_in_cond_item(&l_tx, a_tx_cond_hash, l_prev_cond_idx, 0);
// add 'out' items
{
// transfer selling coins
const dap_chain_addr_t *l_buyer_addr = (dap_chain_addr_t *)l_tx_out_cond->params;
if (dap_chain_datum_tx_add_out_ext_item(&l_tx, l_buyer_addr, a_price->datoshi_sell, a_price->token_sell) == -1) {
if (dap_chain_datum_tx_add_out_ext_item(&l_tx, l_buyer_addr, l_datoshi_buy, a_price->token_buy) == -1) {
dap_chain_datum_tx_delete(l_tx);
DAP_DELETE(l_seller_addr);
log_it(L_ERROR, "Can't add selling coins output");
return NULL;
}
// coin back
uint64_t l_value_back = l_value_sell - a_price->datoshi_sell;
uint64_t l_value_back = l_value_buy - l_datoshi_buy;
if (l_value_back) {
if (dap_chain_datum_tx_add_out_ext_item(&l_tx, l_seller_addr, l_value_back, a_price->token_sell) == -1) {
if (dap_chain_datum_tx_add_out_ext_item(&l_tx, l_seller_addr, l_value_back, a_price->token_buy) == -1) {
dap_chain_datum_tx_delete(l_tx);
DAP_DELETE(l_seller_addr);
log_it(L_ERROR, "Can't add selling coins back output");
......@@ -269,21 +293,21 @@ static dap_chain_datum_tx_t *s_xchange_tx_create_exchange(dap_chain_net_srv_xcha
}
}
//transfer buying coins
if (dap_chain_datum_tx_add_out_ext_item(&l_tx, l_seller_addr, a_price->datoshi_buy, a_price->token_buy) == -1) {
if (dap_chain_datum_tx_add_out_ext_item(&l_tx, l_seller_addr, a_price->datoshi_sell, a_price->token_sell) == -1) {
dap_chain_datum_tx_delete(l_tx);
DAP_DELETE(l_seller_addr);
log_it(L_ERROR, "Cant add buying coins output");
log_it(L_ERROR, "Can't add buying coins output");
return NULL;
}
DAP_DELETE(l_seller_addr);
//transfer unbuying coins (partial exchange)
uint64_t l_buying_value = l_tx_out_cond->header.value;
l_value_back = l_buying_value - a_price->datoshi_buy;
l_value_back = l_buying_value - a_price->datoshi_sell;
if (l_value_back) {
//if (dap_chain_datum_tx_add_out_ext_item(&l_tx, l_buyer_addr, l_value_back, a_price->token_buy) == -1) {
log_it(L_WARNING, "Partial exchange not allowed");
if (dap_chain_datum_tx_add_out_ext_item(&l_tx, l_buyer_addr, l_value_back, a_price->token_sell) == -1) {
log_it(L_WARNING, "Can't add buying coins back output (cashback)");
return NULL;
//}
}
}
}
......@@ -309,19 +333,9 @@ static bool s_xchange_tx_put(dap_chain_datum_tx_t *a_tx, dap_chain_net_t *a_net)
if (!l_chain) {
return false;
}
dap_chain_node_role_t l_role = dap_chain_net_get_role(a_net);
size_t l_datums_number;
switch (l_role.enums) {
case NODE_ROLE_ROOT:
case NODE_ROLE_MASTER:
case NODE_ROLE_ROOT_MASTER:
case NODE_ROLE_CELL_MASTER:
l_datums_number = l_chain->callback_datums_pool_proc(l_chain, &l_datum, 1);
break;
default:
l_datums_number = dap_chain_mempool_datum_add(l_datum, l_chain);
}
if(!l_datums_number) {
// Processing will be made according to autoprocess policy
size_t l_datums_number = dap_chain_mempool_datum_add(l_datum, l_chain);
if (!l_datums_number) {
DAP_DELETE(l_datum);
return false;
}
......@@ -333,8 +347,8 @@ static bool s_xchage_tx_invalidate(dap_chain_net_srv_xchange_price_t *a_price, d
// create empty transaction
dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create();
dap_ledger_t *l_ledger = dap_chain_ledger_by_net_name(a_price->net_sell->pub.name);
dap_chain_addr_t *l_seller_addr = (dap_chain_addr_t *) dap_chain_wallet_get_addr(a_wallet, a_price->net_sell->pub.id);
dap_ledger_t *l_ledger = dap_chain_ledger_by_net_name(a_price->net_buy->pub.name);
dap_chain_addr_t *l_seller_addr = (dap_chain_addr_t *)dap_chain_wallet_get_addr(a_wallet, a_price->net_buy->pub.id);
dap_enc_key_t *l_seller_key = dap_chain_wallet_get_key(a_wallet, 0);
// create and add reciept
......@@ -376,7 +390,7 @@ static bool s_xchage_tx_invalidate(dap_chain_net_srv_xchange_price_t *a_price, d
log_it( L_ERROR, "Can't add sign output");
return false;
}
if (!s_xchange_tx_put(l_tx, a_price->net_sell)) {
if (!s_xchange_tx_put(l_tx, a_price->net_buy)) {
return false;
}
return true;
......@@ -395,19 +409,60 @@ char *s_xchange_order_create(dap_chain_net_srv_xchange_price_t *a_price, dap_cha
dap_chain_node_addr_t *l_node_addr = dap_chain_net_get_cur_addr(a_price->net_sell);
dap_chain_net_srv_price_unit_uid_t l_unit = { .uint32 = SERV_UNIT_UNDEFINED};
dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_XCHANGE_ID };
uint64_t l_datoshi_buy = ceill(a_price->datoshi_sell / a_price->rate);
char *l_order_hash_str = dap_chain_net_srv_order_create(a_price->net_buy, SERV_DIR_SELL, l_uid, *l_node_addr,
l_tx_hash, a_price->datoshi_buy, l_unit, a_price->token_buy, 0,
l_tx_hash, l_datoshi_buy, l_unit, a_price->token_buy, 0,
(uint8_t *)&l_ext, l_ext_size, NULL, 0);
return l_order_hash_str;
}
void s_xchange_price_print(dap_string_t *a_reply_str, dap_chain_net_srv_xchange_price_t *a_price, char *a_str)
dap_chain_net_srv_xchange_price_t *s_xchange_price_from_order(dap_chain_net_t *a_net, dap_chain_net_srv_order_t *a_order)
{
dap_chain_net_srv_xchange_price_t *l_price = DAP_NEW_Z(dap_chain_net_srv_xchange_price_t);
dap_srv_xchange_order_ext_t *l_ext = (dap_srv_xchange_order_ext_t *)a_order->ext;
dap_chain_net_id_t l_net_buy_id = { .uint64 = dap_lendian_get64((uint8_t *)&l_ext->net_sell_id) };
l_price->net_sell = dap_chain_net_by_id(l_net_buy_id);
l_price->datoshi_sell = dap_lendian_get64((uint8_t *)&l_ext->datoshi_sell);
strcpy(l_price->token_sell, l_ext->token_sell);
l_price->net_buy = a_net;
strcpy(l_price->token_buy, a_order->price_ticker);
l_price->rate = (long double)l_price->datoshi_sell / a_order->price;
return l_price;
}
static bool s_xchange_db_add(dap_chain_net_srv_xchange_price_t *a_price)
{
size_t l_size = sizeof(dap_chain_net_srv_xchange_db_item_t) + strlen(a_price->wallet_str) + 1;
dap_chain_net_srv_xchange_db_item_t *l_item = DAP_NEW_Z_SIZE(dap_chain_net_srv_xchange_db_item_t, l_size);
strcpy(l_item->token_sell, a_price->token_sell);
strcpy(l_item->token_buy, a_price->token_buy);
l_item->net_sell_id = a_price->net_sell->pub.id.uint64;
l_item->net_buy_id = a_price->net_buy->pub.id.uint64;
l_item->datoshi_sell = a_price->datoshi_sell;
l_item->rate = a_price->rate;
memcpy(&l_item->tx_hash, &a_price->tx_hash, sizeof(dap_chain_hash_fast_t));
memcpy(&l_item->order_hash, &a_price->order_hash, sizeof(dap_chain_hash_fast_t));
strcpy(l_item->wallet_str, a_price->wallet_str);
return dap_chain_global_db_gr_set(dap_strdup(a_price->key_ptr), (uint8_t *)l_item, l_size, GROUP_LOCAL_XCHANGE);
}
static dap_chain_net_srv_xchange_price_t *s_xchange_db_load(char *a_key, uint8_t *a_item)
{
char *l_order_hash_str = dap_chain_hash_fast_to_str_new(&a_price->order_hash);
dap_string_append_printf(a_reply_str, "%s\t%s\t%s\t%s\t%s\t%lu\t%lu\t%s\n", l_order_hash_str, a_price->token_sell, a_price->net_sell->pub.name,
a_price->token_buy, a_price->net_buy->pub.name, a_price->datoshi_sell,
a_price->datoshi_buy, a_str);
DAP_DELETE(l_order_hash_str);
dap_chain_net_srv_xchange_db_item_t *l_item = (dap_chain_net_srv_xchange_db_item_t *)a_item;
dap_chain_net_srv_xchange_price_t *l_price = DAP_NEW_Z(dap_chain_net_srv_xchange_price_t);
strcpy(l_price->key_ptr, a_key);
strcpy(l_price->token_sell, l_item->token_sell);
strcpy(l_price->token_buy, l_item->token_buy);
dap_chain_net_id_t l_id = { .uint64 = l_item->net_sell_id};
l_price->net_sell = dap_chain_net_by_id(l_id);
l_id.uint64 = l_item->net_buy_id;
l_price->net_buy = dap_chain_net_by_id(l_id);
l_price->datoshi_sell = l_item->datoshi_sell;
l_price->rate = l_item->rate;
memcpy(&l_price->tx_hash, &l_item->tx_hash, sizeof(dap_chain_hash_fast_t));
memcpy(&l_price->order_hash, &l_item->order_hash, sizeof(dap_chain_hash_fast_t));
strcpy(l_price->wallet_str, l_item->wallet_str);
return l_price;
}
static int s_cli_srv_xchange_price(int a_argc, char **a_argv, int a_arg_index, char **a_str_reply)
......@@ -491,25 +546,25 @@ static int s_cli_srv_xchange_price(int a_argc, char **a_argv, int a_arg_index, c
dap_chain_node_cli_set_reply_text(a_str_reply, "Price with provided pair of token ticker + net name already exist");
return -7;
}
const char *l_val_sell_str = NULL, *l_val_buy_str = NULL, *l_wallet_str = NULL;
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-datoshi_sell", &l_val_sell_str);
const char *l_val_sell_str = NULL, *l_val_rate_str = NULL, *l_wallet_str = NULL;
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-coins", &l_val_sell_str);
if (!l_val_sell_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'price create' required parameter -datoshi_sell");
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'price create' required parameter -coins");
return -8;
}
uint64_t l_datoshi_sell = strtoull(l_val_sell_str, NULL, 10);
if (!l_datoshi_sell) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Format -datoshi_sell <unsigned long long>");
dap_chain_node_cli_set_reply_text(a_str_reply, "Format -coins <unsigned long long>");
return -9;
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-datoshi_buy", &l_val_buy_str);
if (!l_val_buy_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'price create' required parameter -datoshi_buy");
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-rate", &l_val_rate_str);
if (!l_val_rate_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'price create' required parameter -rate");
return -8;
}
uint64_t l_datoshi_buy = strtoull(l_val_buy_str, NULL, 10);
if (!l_datoshi_buy) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Format -datoshi_buy <unsigned long long>");
long double l_rate = strtold(l_val_rate_str, NULL);
if (!l_rate) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Format -rate <long double> = sell / buy");
return -9;
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-wallet", &l_wallet_str);
......@@ -536,24 +591,30 @@ static int s_cli_srv_xchange_price(int a_argc, char **a_argv, int a_arg_index, c
l_price->net_buy = l_net_buy;
l_price->key_ptr = l_strkey;
l_price->datoshi_sell = l_datoshi_sell;
l_price->datoshi_buy = l_datoshi_buy;
l_price->rate = l_rate;
// Create conditional transaction
dap_chain_datum_tx_t *l_tx = s_xchange_tx_create_request(l_price, l_wallet);
dap_chain_wallet_close(l_wallet);
if (!l_tx) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Can't compose the conditional transaction");
DAP_DELETE(l_price);
break;
return -14;
}
// Create the order & put it to GDB
char *l_order_hash_str = s_xchange_order_create(l_price, l_tx);
if (l_order_hash_str) {
dap_chain_str_to_hash_fast(l_order_hash_str, &l_price->order_hash);
if(!s_xchange_tx_put(l_tx, l_net_buy)) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Can't put transaction to mempool/chains");
dap_chain_node_cli_set_reply_text(a_str_reply, "Can't put transaction to mempool");
dap_chain_net_srv_order_delete_by_hash_str(l_net_buy, l_order_hash_str);
DAP_DELETE(l_order_hash_str);
break;
return -15;
}
if (!s_xchange_db_add(l_price)) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Can't save price in database");
dap_chain_net_srv_order_delete_by_hash_str(l_net_buy, l_order_hash_str);
DAP_DELETE(l_order_hash_str);
return -16;
}
dap_chain_node_cli_set_reply_text(a_str_reply, "Successfully created order %s", l_order_hash_str);
DAP_DELETE(l_order_hash_str);
......@@ -569,127 +630,138 @@ static int s_cli_srv_xchange_price(int a_argc, char **a_argv, int a_arg_index, c
case CMD_UPDATE: {
dap_chain_net_srv_xchange_price_t *l_price = NULL;
HASH_FIND_STR(s_srv_xchange->pricelist, l_strkey, l_price);
if (l_price) {
if (l_cmd_num == CMD_REMOVE) {
dap_string_t *l_str_reply = dap_string_new("");
HASH_DEL(s_srv_xchange->pricelist, l_price);
dap_chain_wallet_t *l_wallet = dap_chain_wallet_open(l_price->wallet_str, dap_chain_wallet_get_path(g_config));
bool l_ret = s_xchage_tx_invalidate(l_price, l_wallet);
dap_chain_wallet_close(l_wallet);
if (!l_ret) {
char *l_tx_hash_str = dap_chain_hash_fast_to_str_new(&l_price->tx_hash);
dap_string_append_printf(l_str_reply, "Can't invalidate transaction %s\n", l_tx_hash_str);
DAP_DELETE(l_tx_hash_str);
}
char *l_order_hash_str = dap_chain_hash_fast_to_str_new(&l_price->order_hash);
if (dap_chain_net_srv_order_delete_by_hash_str(l_price->net_buy, l_order_hash_str)) {
dap_string_append_printf(l_str_reply, "Can't remove order %s\n", l_order_hash_str);
}
DAP_DELETE(l_order_hash_str);
DAP_DELETE(l_price->key_ptr);
DAP_DELETE(l_price->wallet_str);
DAP_DELETE(l_price);
if (!l_str_reply->len) {
dap_string_append(l_str_reply, "Price successfully removed");
}
*a_str_reply = dap_string_free(l_str_reply, false);
} else { // CMD_UPDATE
const char *l_val_sell_str = NULL, *l_val_buy_str = NULL, *l_wallet_str = NULL, *l_new_wallet_str = NULL;
uint64_t l_datoshi_sell = 0, l_datoshi_buy = 0;
dap_chain_wallet_t *l_wallet = NULL;
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-datoshi_sell", &l_val_sell_str);
if (l_val_sell_str) {
l_datoshi_sell = strtoull(l_val_sell_str, NULL, 10);
if (!l_datoshi_sell) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Format -datoshi_sell <unsigned long long>");
return -9;
}
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-datoshi_buy", &l_val_buy_str);
if (l_val_buy_str) {
l_datoshi_buy = strtoull(l_val_buy_str, NULL, 10);
if (!l_datoshi_buy) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Format -datoshi_buy <unsigned long long>");
return -9;
}
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-wallet", &l_new_wallet_str);
l_wallet_str = l_new_wallet_str ? l_new_wallet_str : l_price->wallet_str;
l_wallet = dap_chain_wallet_open(l_wallet_str, dap_chain_wallet_get_path(g_config));
if (!l_wallet) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Specified wallet not found");
return -11;
}
if (!l_val_sell_str && !l_val_buy_str && !l_wallet_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "At least one of updating parameters is mandatory");
return -13;
}
if (l_datoshi_sell && dap_chain_wallet_get_balance(l_wallet, l_net_sell->pub.id, l_token_sell_str) < l_datoshi_sell) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Not enough cash in specified wallet");
dap_chain_wallet_close(l_wallet);
return -12;
}
if (l_val_sell_str) {
l_price->datoshi_sell = l_datoshi_sell;
if (!l_price) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Price with provided pair of token ticker + net name is not exist");
return -1;
}
if (l_cmd_num == CMD_REMOVE) {
dap_string_t *l_str_reply = dap_string_new("");
HASH_DEL(s_srv_xchange->pricelist, l_price);
dap_chain_global_db_gr_del(l_price->key_ptr, GROUP_LOCAL_XCHANGE);
dap_chain_wallet_t *l_wallet = dap_chain_wallet_open(l_price->wallet_str, dap_chain_wallet_get_path(g_config));
bool l_ret = s_xchage_tx_invalidate(l_price, l_wallet);
dap_chain_wallet_close(l_wallet);
if (!l_ret) {
char *l_tx_hash_str = dap_chain_hash_fast_to_str_new(&l_price->tx_hash);
dap_string_append_printf(l_str_reply, "Can't invalidate transaction %s\n", l_tx_hash_str);
DAP_DELETE(l_tx_hash_str);
}
char *l_order_hash_str = dap_chain_hash_fast_to_str_new(&l_price->order_hash);
if (dap_chain_net_srv_order_delete_by_hash_str(l_price->net_buy, l_order_hash_str)) {
dap_string_append_printf(l_str_reply, "Can't remove order %s\n", l_order_hash_str);
}
DAP_DELETE(l_order_hash_str);
DAP_DELETE(l_price->wallet_str);
DAP_DELETE(l_price);
if (!l_str_reply->len) {
dap_string_append(l_str_reply, "Price successfully removed");
}
*a_str_reply = dap_string_free(l_str_reply, false);
} else { // CMD_UPDATE
const char *l_val_sell_str = NULL, *l_val_rate_str = NULL, *l_wallet_str = NULL, *l_new_wallet_str = NULL;
uint64_t l_datoshi_sell = 0;
long double l_rate = 0;
dap_chain_wallet_t *l_wallet = NULL;
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-coins", &l_val_sell_str);
if (l_val_sell_str) {
l_datoshi_sell = strtoull(l_val_sell_str, NULL, 10);
if (!l_datoshi_sell) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Format -coins <unsigned long long>");
return -9;
}
if (l_val_buy_str) {
l_price->datoshi_buy = l_datoshi_buy;
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-rate", &l_val_rate_str);
if (l_val_rate_str) {
l_rate = strtold(l_val_rate_str, NULL);
if (!l_rate) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Format -rate <long double> = sell / buy");
return -9;
}
// Update the transaction
dap_chain_datum_tx_t *l_tx = s_xchange_tx_create_request(l_price, l_wallet);
if (l_new_wallet_str) {
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-wallet", &l_new_wallet_str);
l_wallet_str = l_new_wallet_str ? l_new_wallet_str : l_price->wallet_str;
l_wallet = dap_chain_wallet_open(l_wallet_str, dap_chain_wallet_get_path(g_config));
if (!l_wallet) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Specified wallet not found");
return -11;
}
if (!l_val_sell_str && !l_val_rate_str && !l_wallet_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "At least one of updating parameters is mandatory");
return -13;
}
if (l_datoshi_sell && dap_chain_wallet_get_balance(l_wallet, l_net_sell->pub.id, l_token_sell_str) < l_datoshi_sell) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Not enough cash in specified wallet");
dap_chain_wallet_close(l_wallet);
l_wallet = dap_chain_wallet_open(l_price->wallet_str, dap_chain_wallet_get_path(g_config));
DAP_DELETE(l_price->wallet_str);
l_price->wallet_str = dap_strdup(l_new_wallet_str);
}
if (!l_tx) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Can't compose the conditional transaction");
break;
}
HASH_DEL(s_srv_xchange->pricelist, l_price);
bool l_ret = s_xchage_tx_invalidate(l_price, l_wallet); // may be changed to old price later
return -12;
}
if (l_val_sell_str) {
l_price->datoshi_sell = l_datoshi_sell;
}
if (l_val_rate_str) {
l_price->rate = l_rate;
}
// Update the transaction
dap_chain_datum_tx_t *l_tx = s_xchange_tx_create_request(l_price, l_wallet);
if (l_new_wallet_str) {
dap_chain_wallet_close(l_wallet);
if (!l_ret) {
char *l_tx_hash_str = dap_chain_hash_fast_to_str_new(&l_price->tx_hash);
dap_chain_node_cli_set_reply_text(a_str_reply, "Can't invalidate transaction %s\n", l_tx_hash_str);
DAP_DELETE(l_tx_hash_str);
break;
l_wallet = dap_chain_wallet_open(l_price->wallet_str, dap_chain_wallet_get_path(g_config));
DAP_DELETE(l_price->wallet_str);
l_price->wallet_str = dap_strdup(l_new_wallet_str);
}
if (!l_tx) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Can't compose the conditional transaction");
return -14;
}
HASH_DEL(s_srv_xchange->pricelist, l_price);
dap_chain_global_db_gr_del(l_price->key_ptr, GROUP_LOCAL_XCHANGE);
bool l_ret = s_xchage_tx_invalidate(l_price, l_wallet); // may be changed to old price later
dap_chain_wallet_close(l_wallet);
if (!l_ret) {
char *l_tx_hash_str = dap_chain_hash_fast_to_str_new(&l_price->tx_hash);
dap_chain_node_cli_set_reply_text(a_str_reply, "Can't invalidate transaction %s\n", l_tx_hash_str);
DAP_DELETE(l_tx_hash_str);
return -17;
}
// Update the order
char *l_order_hash_str = dap_chain_hash_fast_to_str_new(&l_price->order_hash);
dap_chain_net_srv_order_delete_by_hash_str(l_price->net_buy, l_order_hash_str);
DAP_DELETE(l_order_hash_str);
l_order_hash_str = s_xchange_order_create(l_price, l_tx);
if (l_order_hash_str) {
dap_chain_str_to_hash_fast(l_order_hash_str, &l_price->order_hash);
if(!s_xchange_tx_put(l_tx, l_net_buy)) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Can't put transaction to mempool");
dap_chain_net_srv_order_delete_by_hash_str(l_net_buy, l_order_hash_str);
DAP_DELETE(l_order_hash_str);
return -15;
}
// Update the order
char *l_order_hash_str = dap_chain_hash_fast_to_str_new(&l_price->order_hash);
dap_chain_net_srv_order_delete_by_hash_str(l_price->net_buy, l_order_hash_str);
DAP_DELETE(l_order_hash_str);
l_order_hash_str = s_xchange_order_create(l_price, l_tx);
if (l_order_hash_str) {
dap_chain_str_to_hash_fast(l_order_hash_str, &l_price->order_hash);
if(!s_xchange_tx_put(l_tx, l_net_buy)) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Can't put transaction to mempool/chains");
dap_chain_net_srv_order_delete_by_hash_str(l_net_buy, l_order_hash_str);
break;
} else {
dap_chain_node_cli_set_reply_text(a_str_reply, "Successfully created order %s", l_order_hash_str);
}
if (!s_xchange_db_add(l_price)) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Can't save price in database");
dap_chain_net_srv_order_delete_by_hash_str(l_net_buy, l_order_hash_str);
DAP_DELETE(l_order_hash_str);
} else {
dap_chain_node_cli_set_reply_text(a_str_reply, "Can't compose the order");
DAP_DELETE(l_price->key_ptr);
DAP_DELETE(l_price);
break;
return -16;
}
// Update the pricelist
HASH_ADD_KEYPTR(hh, s_srv_xchange->pricelist, l_price->key_ptr, strlen(l_price->key_ptr), l_price);
dap_chain_node_cli_set_reply_text(a_str_reply, "Successfully created order %s", l_order_hash_str);
DAP_DELETE(l_order_hash_str);
} else {
dap_chain_node_cli_set_reply_text(a_str_reply, "Can't compose the order");
DAP_DELETE(l_price->key_ptr);
DAP_DELETE(l_price);
return -18;
}
} else {
dap_chain_node_cli_set_reply_text(a_str_reply, "Price with provided pair of token ticker + net name is not exist");
return -1;
// Update the pricelist
HASH_ADD_KEYPTR(hh, s_srv_xchange->pricelist, l_price->key_ptr, strlen(l_price->key_ptr), l_price);
}
} break;
case CMD_LIST: {
dap_chain_net_srv_xchange_price_t *l_price = NULL, *l_tmp;
dap_string_t *l_reply_str = dap_string_new("");
HASH_ITER(hh, s_srv_xchange->pricelist, l_price, l_tmp) {
s_xcjhange_price_print(l_reply_str, l_price, l_price->wallet_str);
char *l_order_hash_str = dap_chain_hash_fast_to_str_new(&l_price->order_hash);
dap_string_append_printf(l_reply_str, "%s\t%s\t%s\t%s\t%s\t%lu\t%ld\t%s\n", l_order_hash_str, l_price->token_sell,
l_price->net_sell->pub.name, l_price->token_buy, l_price->net_buy->pub.name,
l_price->datoshi_sell, l_price->rate, l_price->wallet_str);
DAP_DELETE(l_order_hash_str);
}
if (!l_reply_str->len) {
dap_string_append(l_reply_str, "Pricelist is empty");
......@@ -746,19 +818,28 @@ static int s_cli_srv_xchange(int a_argc, char **a_argv, void *a_arg_func, char *
char * l_gdb_group_str = dap_chain_net_srv_order_get_gdb_group(l_net);
size_t l_orders_count = 0;
dap_global_db_obj_t * l_orders = dap_chain_global_db_gr_load(l_gdb_group_str, &l_orders_count);
dap_chain_net_srv_xchange_price_t *l_price;
dap_string_t *l_reply_str = dap_string_new("");
for (size_t i = 0; i < l_orders_count; i++) {
dap_chain_net_srv_order_t *l_order = (dap_chain_net_srv_order_t *)l_orders[i].value;
if (l_order->srv_uid.uint64 != DAP_CHAIN_NET_SRV_XCHANGE_ID)
continue;
// TODO add filters to list (tokens, network, etc.)
l_price = s_xchange_price_from_order(l_net, l_order);
dap_string_append_printf(l_reply_str, "%s\t%s\t%s\t%s\t%s\t%lu\t%ld\n", l_orders[i].key, l_price->token_sell,
l_price->net_sell->pub.name, l_price->token_buy, l_price->net_buy->pub.name,
l_price->datoshi_sell, l_price->rate);
DAP_DELETE(l_price);
}
dap_chain_global_db_objs_delete(l_orders, l_orders_count);
DAP_DELETE( l_gdb_group_str);
if (!l_reply_str->len) {
dap_string_append(l_reply_str, "No orders found");
}
*a_str_reply = dap_string_free(l_reply_str, false);
} break;
case CMD_PURCHASE: {
const char *l_net_str = NULL, *l_wallet_str = NULL, *l_order_hash_str = NULL;
const char *l_net_str = NULL, *l_wallet_str = NULL, *l_order_hash_str = NULL, *l_val_sell_str = NULL;
l_arg_index++;
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-net", &l_net_str);
if (!l_net_str) {
......@@ -785,21 +866,24 @@ static int s_cli_srv_xchange(int a_argc, char **a_argv, void *a_arg_func, char *
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'purchase' required parameter -order");
return -12;
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-coins", &l_val_sell_str);
if (!l_val_sell_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'price create' required parameter -coins");
return -8;
}
uint64_t l_datoshi_sell = strtoull(l_val_sell_str, NULL, 10);
if (!l_datoshi_sell) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Format -coins <unsigned long long>");
return -9;
}
dap_chain_net_srv_order_t *l_order = dap_chain_net_srv_order_find_by_hash_str(l_net, l_order_hash_str);
if (l_order) {
dap_chain_net_srv_xchange_price_t *l_price = DAP_NEW(dap_chain_net_srv_xchange_price_t);
l_price->net_sell = l_net;
strcpy(l_price->token_sell, l_order->price_ticker);
l_price->datoshi_sell = l_order->price;
dap_srv_xchange_order_ext_t *l_ext = (dap_srv_xchange_order_ext_t *)l_order->ext;
dap_chain_net_id_t l_net_buy_id = { .uint64 = dap_lendian_get64((uint8_t *)&l_ext->net_sell_id) };
l_price->net_buy = dap_chain_net_by_id(l_net_buy_id);
l_price->datoshi_buy = dap_lendian_get64((uint8_t *)&l_ext->datoshi_sell);
strcpy(l_price->token_buy, l_ext->token_sell);
dap_chain_net_srv_xchange_price_t *l_price = s_xchange_price_from_order(l_net, l_order);
l_price->datoshi_sell = l_datoshi_sell;
// Create conditional transaction
dap_chain_datum_tx_t *l_tx = s_xchange_tx_create_exchange(l_price, &l_order->tx_cond_hash, l_wallet);
if (l_tx && s_xchange_tx_put(l_tx, l_net)) {
// TODO send request to seller to delete order & price
// TODO send request to seller to update / delete order & price
dap_chain_net_srv_order_delete_by_hash_str(l_price->net_buy, l_order_hash_str);
}
DAP_DELETE(l_price);
......
......@@ -28,6 +28,7 @@
#include "dap_chain_net_srv_order.h"
#define DAP_CHAIN_NET_SRV_XCHANGE_ID 0x2
#define GROUP_LOCAL_XCHANGE "local.xchange"
typedef struct dap_chain_net_srv_xchange_price {
char *wallet_str;
......@@ -36,13 +37,26 @@ typedef struct dap_chain_net_srv_xchange_price {
uint64_t datoshi_sell;
dap_chain_net_t *net_buy;
char token_buy[DAP_CHAIN_TICKER_SIZE_MAX];
uint64_t datoshi_buy;
long double rate;
dap_chain_hash_fast_t tx_hash;
dap_chain_hash_fast_t order_hash;
char *key_ptr;
UT_hash_handle hh;
} dap_chain_net_srv_xchange_price_t;
typedef struct dap_chain_net_srv_xchange_db_item {
char token_sell[DAP_CHAIN_TICKER_SIZE_MAX];
char token_buy[DAP_CHAIN_TICKER_SIZE_MAX];
uint8_t padding[4];
uint64_t net_sell_id;
uint64_t net_buy_id;
uint64_t datoshi_sell;
long double rate;
dap_chain_hash_fast_t tx_hash;
dap_chain_hash_fast_t order_hash;
char wallet_str[];
} DAP_ALIGN_PACKED dap_chain_net_srv_xchange_db_item_t;
typedef struct dap_srv_xchange_order_ext {
uint64_t net_sell_id;
uint64_t datoshi_sell;
......