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 (6)
Showing
with 1034 additions and 63 deletions
......@@ -117,6 +117,12 @@ if (CELLFRAME_MODULES MATCHES "srv-xchange")
set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_chain_net_srv_xchange )
endif()
# Enable service of delegated stake
if (CELLFRAME_MODULES MATCHES "srv-stake")
message("[+] Module 'srv-stake'")
set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_chain_net_srv_stake )
endif()
if (WIN32)
set(CELLFRAME_LIBS ${CELLFRAME_LIBS} KERNEL32 USER32 SHELL32 WINMM GDI32 ADVAPI32
Ole32 Version Imm32 OleAut32 ws2_32 ntdll psapi
......
......@@ -81,6 +81,11 @@ if (CELLFRAME_MODULES MATCHES "srv-xchange")
add_subdirectory(service/xchange)
endif()
# Service of delegated stake
if (CELLFRAME_MODULES MATCHES "srv-stake")
add_subdirectory(service/stake)
endif()
# Unit tests
if( BUILD_TESTS)
add_subdirectory(test)
......
......@@ -672,8 +672,10 @@ int dap_chain_ledger_tx_cache_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t
3. hash(tx1) == tx2.dap_chain_datump_tx_in.tx_prev_hash
&&
4. tx1.dap_chain_datum_tx_out.addr.data.key == tx2.dap_chain_datum_tx_sig.pkey for unconditional output
&&
5. tx1.dap_chain_datum_tx_out.condition == verify_svc_type(tx2) for conditional ouput
\\
5a. tx1.dap_chain_datum_tx_sig.pkey == tx1.dap_chain_datum_tx_sig.pkey for conditional owner
\\
5b. tx1.dap_chain_datum_tx_out.condition == verify_svc_type(tx2) for conditional output
&&
6. sum( find (tx2.input.tx_prev_hash).output[tx2.input_tx_prev_idx].value ) == sum (tx2.outputs.value) per token
*/
......@@ -854,19 +856,31 @@ int dap_chain_ledger_tx_cache_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t
l_err_num = -8;
break;
}
dap_chain_tx_out_cond_t * l_tx_prev_out_cond = (dap_chain_tx_out_cond_t *)l_tx_prev_out;
dap_chain_ledger_verificator_t *l_verificator;
int l_tmp = (int)l_tx_prev_out_cond->header.subtype;
HASH_FIND_INT(s_verificators, &l_tmp, l_verificator);
if (!l_verificator || !l_verificator->callback) {
log_it(L_ERROR, "No verificator set for conditional output subtype %d", l_tmp);
l_err_num = -13;
break;
}
// 5. Call verificator for conditional output
if (l_verificator->callback(l_tx_prev_out_cond, a_tx) == false) {
l_err_num = -14;
break;
// 5a. Check for condition owner
dap_chain_tx_sig_t *l_tx_prev_sig = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get(l_tx_prev, NULL, TX_ITEM_TYPE_SIG, NULL);
dap_sign_t *l_prev_sign = dap_chain_datum_tx_item_sign_get_sig((dap_chain_tx_sig_t *)l_tx_prev_sig);
size_t l_prev_pkey_ser_size = 0;
const uint8_t *l_prev_pkey_ser = dap_sign_get_pkey(l_prev_sign, &l_prev_pkey_ser_size);
dap_chain_tx_sig_t *l_tx_sig = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get(a_tx, NULL, TX_ITEM_TYPE_SIG, NULL);
dap_sign_t *l_sign = dap_chain_datum_tx_item_sign_get_sig((dap_chain_tx_sig_t *)l_tx_sig);
size_t l_pkey_ser_size = 0;
const uint8_t *l_pkey_ser = dap_sign_get_pkey(l_sign, &l_pkey_ser_size);
dap_chain_tx_out_cond_t *l_tx_prev_out_cond = (dap_chain_tx_out_cond_t *)l_tx_prev_out;
if (l_pkey_ser_size != l_prev_pkey_ser_size ||
memcmp(l_prev_pkey_ser, l_pkey_ser, l_prev_pkey_ser_size)) {
// 5b. Call verificator for conditional output
dap_chain_ledger_verificator_t *l_verificator;
int l_tmp = (int)l_tx_prev_out_cond->header.subtype;
HASH_FIND_INT(s_verificators, &l_tmp, l_verificator);
if (!l_verificator || !l_verificator->callback) {
log_it(L_ERROR, "No verificator set for conditional output subtype %d", l_tmp);
l_err_num = -13;
break;
}
if (l_verificator->callback(l_tx_prev_out_cond, a_tx) == false) {
l_err_num = -14;
break;
}
}
bound_item->out.tx_prev_out_cond = l_tx_prev_out_cond;
// calculate sum of values from previous transactions
......@@ -1672,7 +1686,7 @@ 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_hash_fast_t *a_tx_first_hash, dap_chain_tx_out_cond_t **a_out_cond, int *a_out_cond_idx)
dap_chain_hash_fast_t *a_tx_first_hash, dap_chain_tx_out_cond_t **a_out_cond, int *a_out_cond_idx, char *a_token_ticker)
{
if (!a_tx_first_hash)
return NULL;
......@@ -1700,6 +1714,9 @@ dap_chain_datum_tx_t* dap_chain_ledger_tx_cache_find_out_cond(dap_ledger_t *a_le
if(l_tx_out_cond) {
l_cur_tx = l_tx_tmp;
memcpy(a_tx_first_hash, l_tx_hash_tmp, sizeof(dap_chain_hash_fast_t));
if (a_token_ticker) {
strcpy(a_token_ticker, l_iter_current->token_tiker);
}
break;
}
}
......@@ -1737,7 +1754,7 @@ uint64_t dap_chain_ledger_tx_cache_get_out_cond_value(dap_ledger_t *a_ledger, da
// Find all transactions
do {
l_tx_tmp = dap_chain_ledger_tx_cache_find_out_cond(a_ledger, &l_tx_first_hash, &l_tx_out_cond, NULL);
l_tx_tmp = dap_chain_ledger_tx_cache_find_out_cond(a_ledger, &l_tx_first_hash, &l_tx_out_cond, NULL, NULL);
// Get out_cond item from transaction
if(l_tx_tmp) {
......@@ -1800,7 +1817,7 @@ dap_list_t *dap_chain_ledger_get_list_tx_outs_with_val(dap_ledger_t *a_ledger, c
l_list_used_out = dap_list_append(l_list_used_out, item);
l_value_transfer += item->value;
// already accumulated the required value, finish the search for 'out' items
if(l_value_transfer >= a_value_need) {
if (l_value_transfer >= a_value_need) {
break;
}
}
......
......@@ -175,7 +175,8 @@ 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_hash_fast_t *a_tx_first_hash, dap_chain_tx_out_cond_t **a_out_cond, int *a_out_cond_idx);
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, char *a_token_ticker);
// 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,
......
......@@ -228,27 +228,14 @@ dap_chain_net_srv_uid_t dap_chain_net_srv_uid_from_str( const char * a_net_srv_u
* @param a_net_id
* @return
*/
void dap_chain_addr_fill(dap_chain_addr_t *a_addr, dap_enc_key_t *a_key, dap_chain_net_id_t *a_net_id)
void dap_chain_addr_fill(dap_chain_addr_t *a_addr, dap_sign_type_t a_type, dap_chain_hash_fast_t *a_pkey_hash, dap_chain_net_id_t *a_net_id)
{
if(!a_addr || !a_key || !a_net_id)
if(!a_addr || !a_pkey_hash || !a_net_id)
return;
a_addr->addr_ver = DAP_CHAIN_ADDR_VERSION_CURRENT;
a_addr->net_id.uint64 = a_net_id->uint64;
a_addr->sig_type.raw = dap_sign_type_from_key_type(a_key->type).raw;
// key -> serialized key
dap_chain_hash_fast_t l_hash_public_key;
size_t l_pub_key_data_size;
uint8_t *l_pub_key_data = dap_enc_key_serealize_pub_key(a_key, &l_pub_key_data_size);
if ( l_pub_key_data == NULL ){
log_it(L_ERROR,"Can't fill address from key, its empty");
return;
}
// serialized key -> key hash
if(dap_hash_fast(l_pub_key_data, l_pub_key_data_size, &l_hash_public_key))
memcpy(a_addr->data.hash, l_hash_public_key.raw, sizeof(l_hash_public_key.raw));
DAP_DELETE(l_pub_key_data);
a_addr->sig_type.raw = a_type.raw;
memcpy(a_addr->data.hash, a_pkey_hash, sizeof(dap_chain_hash_fast_t));
// calc checksum
dap_hash_fast(a_addr, sizeof(dap_chain_addr_t) - sizeof(dap_chain_hash_fast_t), &a_addr->checksum);
}
......
......@@ -256,7 +256,7 @@ dap_chain_tx_out_cond_t* dap_chain_datum_tx_item_out_cond_create_srv_pay(dap_enc
}
dap_chain_tx_out_cond_t* dap_chain_datum_tx_item_out_cond_create_srv_xchange(dap_chain_net_srv_uid_t a_srv_uid, dap_chain_net_id_t a_net_id,
dap_chain_tx_out_cond_t *dap_chain_datum_tx_item_out_cond_create_srv_xchange(dap_chain_net_srv_uid_t a_srv_uid, dap_chain_net_id_t a_net_id,
const char *a_token, uint64_t a_value,
const void *a_params, uint32_t a_params_size)
{
......@@ -277,6 +277,23 @@ dap_chain_tx_out_cond_t* dap_chain_datum_tx_item_out_cond_create_srv_xchange(dap
return l_item;
}
dap_chain_tx_out_cond_t *dap_chain_datum_tx_item_out_cond_create_srv_stake(dap_chain_net_srv_uid_t a_srv_uid, uint64_t a_value, long double a_fee_value,
dap_chain_addr_t *a_fee_addr, const void *a_params, uint32_t a_params_size)
{
dap_chain_tx_out_cond_t *l_item = DAP_NEW_Z_SIZE(dap_chain_tx_out_cond_t, sizeof(dap_chain_tx_out_cond_t) + a_params_size);
l_item->header.item_type = TX_ITEM_TYPE_OUT_COND;
l_item->header.value = a_value;
l_item->header.subtype = DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE;
l_item->subtype.srv_stake.srv_uid = a_srv_uid;
l_item->subtype.srv_stake.fee_value = a_fee_value;
memcpy(&l_item->subtype.srv_stake.fee_addr, a_fee_addr, sizeof(dap_chain_addr_t));
l_item->params_size = a_params_size;
if (a_params_size) {
memcpy(l_item->params, a_params, a_params_size);
}
return l_item;
}
/**
* Create item dap_chain_tx_sig_t
*
......
......@@ -207,7 +207,7 @@ dap_chain_addr_t* dap_chain_addr_from_str(const char *str);
dap_chain_net_id_t dap_chain_net_id_from_str(const char* a_str);
dap_chain_net_srv_uid_t dap_chain_net_srv_uid_from_str(const char* a_str);
void dap_chain_addr_fill(dap_chain_addr_t *a_addr, dap_enc_key_t *a_key, dap_chain_net_id_t *a_net_id);
void dap_chain_addr_fill(dap_chain_addr_t *a_addr, dap_sign_type_t a_type, dap_chain_hash_fast_t *a_pkey_hash, dap_chain_net_id_t *a_net_id);
int dap_chain_addr_check_sum(const dap_chain_addr_t *a_addr);
......
......@@ -96,13 +96,21 @@ dap_chain_tx_out_cond_t* dap_chain_datum_tx_item_out_cond_create_srv_pay(dap_enc
uint64_t a_value, uint64_t a_value_max_per_unit, dap_chain_net_srv_price_unit_uid_t a_unit,
const void *a_cond, size_t a_cond_size);
/**
* Create item dap_chain_tx_out_cond_t fo eXchange service
* Create item dap_chain_tx_out_cond_t for eXchange service
*
* return item, NULL Error
*/
dap_chain_tx_out_cond_t* dap_chain_datum_tx_item_out_cond_create_srv_xchange(dap_chain_net_srv_uid_t a_srv_uid,
dap_chain_net_id_t a_net_id, const char *a_token, uint64_t a_value,
const void *a_params, uint32_t a_params_size);
/**
* Create item dap_chain_tx_out_cond_t for stake service
*
* return item, NULL Error
*/
dap_chain_tx_out_cond_t *dap_chain_datum_tx_item_out_cond_create_srv_stake(dap_chain_net_srv_uid_t a_srv_uid, uint64_t a_value, long double a_fee_value,
dap_chain_addr_t *a_fee_addr, const void *a_params, uint32_t a_params_size);
/**
* Create item dap_chain_tx_sig_t
*
......
......@@ -31,7 +31,8 @@
typedef enum dap_chain_tx_out_cond_subtype {
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_XCHANGE = 0x02,
DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE = 0x03
} dap_chain_tx_out_cond_subtype_t;
/**
......@@ -71,6 +72,14 @@ typedef struct dap_chain_tx_out_cond {
// Total amount of datoshi to change to
uint64_t value;
} srv_xchange;
struct {
// Service uid that only could be used for this outout
dap_chain_net_srv_uid_t srv_uid;
// Fee address
dap_chain_addr_t fee_addr;
// Fee value in percent
long double fee_value;
} srv_stake;
} subtype;
uint32_t params_size; // Condition parameters size
uint8_t params[]; // condition parameters, pkey, hash or smth like this
......
......@@ -11,6 +11,6 @@ endif()
add_library(${PROJECT_NAME} STATIC ${DAP_CHAIN_DAG_CS_POA_SRCS} ${DAP_CHAIN_DAG_CS_POA_HEADERS})
target_link_libraries(dap_chain_cs_dag_poa dap_core dap_crypto dap_chain dap_chain_cs_dag )
target_link_libraries(dap_chain_cs_dag_poa dap_core dap_crypto dap_chain dap_chain_cs_dag dap_chain_net_srv_stake)
target_include_directories(dap_chain_cs_dag_poa INTERFACE .)
target_include_directories(${PROJECT_NAME} PUBLIC include)
......@@ -43,6 +43,7 @@
#include "dap_chain_cs_dag.h"
#include "dap_chain_cs_dag_event.h"
#include "dap_chain_cs_dag_poa.h"
#include "dap_chain_net_srv_stake.h"
#include "dap_cert.h"
......@@ -306,12 +307,33 @@ static int s_callback_event_verify(dap_chain_cs_dag_t * a_dag, dap_chain_cs_dag_
dap_chain_cs_dag_poa_pvt_t * l_poa_pvt = PVT ( DAP_CHAIN_CS_DAG_POA( a_dag ) );
if ( a_dag_event->header.signs_count >= l_poa_pvt->auth_certs_count_verify ){
size_t l_verified = 0;
for ( uint16_t i = 0; i < a_dag_event->header.signs_count; i++ ){
for ( uint16_t j = 0; j < l_poa_pvt->auth_certs_count; j++){
if( dap_cert_compare_with_sign ( l_poa_pvt->auth_certs[j],
dap_chain_cs_dag_event_get_sign(a_dag_event,i) ) == 0 )
for ( uint16_t i = 0; i < a_dag_event->header.signs_count; i++ ) {
dap_sign_t * l_sign = dap_chain_cs_dag_event_get_sign(a_dag_event, 0);
if ( l_sign == NULL){
log_it(L_WARNING, "Event is NOT signed with anything");
return -4;
}
for (uint16_t j = 0; j < l_poa_pvt->auth_certs_count; j++) {
if (dap_cert_compare_with_sign ( l_poa_pvt->auth_certs[j], l_sign) == 0)
l_verified++;
}
if (i == 0) {
dap_chain_hash_fast_t l_pkey_hash;
if (!dap_sign_get_pkey_hash(l_sign, &l_pkey_hash)) {
log_it(L_WARNING, "Event's sign has no any key");
return -5;
}
dap_chain_addr_t l_addr = {};
dap_chain_addr_fill(&l_addr, l_sign->header.type, &l_pkey_hash, &a_dag->chain->net_id);
dap_chain_datum_t *l_datum = (dap_chain_datum_t *)dap_chain_cs_dag_event_get_datum(a_dag_event);
if (l_datum->header.type_id == DAP_CHAIN_DATUM_TX) {
dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t *)l_datum->data;
if (!dap_chain_net_srv_stake_validator(&l_addr, l_tx)) {
return -6;
}
}
}
}
return l_verified >= l_poa_pvt->auth_certs_count_verify ? 0 : -1;
}else
......
......@@ -11,6 +11,6 @@ endif()
add_library(${PROJECT_NAME} STATIC ${DAP_CHAIN_CS_DAG_POS_SRCS} ${DAP_CHAIN_CS_DAG_POS_HEADERS})
target_link_libraries(dap_chain_cs_dag_pos dap_core dap_crypto dap_chain dap_chain_cs_dag )
target_link_libraries(dap_chain_cs_dag_pos dap_core dap_crypto dap_chain dap_chain_cs_dag dap_chain_net_srv_stake)
target_include_directories(dap_chain_cs_dag_pos INTERFACE .)
target_include_directories(${PROJECT_NAME} PUBLIC include)
......@@ -30,7 +30,7 @@
#include "dap_chain_cs.h"
#include "dap_chain_cs_dag.h"
#include "dap_chain_cs_dag_pos.h"
#include "dap_chain_net_srv_stake.h"
#include "dap_chain_ledger.h"
#define LOG_TAG "dap_chain_cs_dag_pos"
......@@ -224,21 +224,28 @@ static int s_callback_event_verify(dap_chain_cs_dag_t * a_dag, dap_chain_cs_dag_
dap_chain_addr_t l_addr = { 0 };
for ( size_t l_sig_pos=0; l_sig_pos < a_dag_event->header.signs_count; l_sig_pos++ ){
dap_sign_t * l_sign = dap_chain_cs_dag_event_get_sign(a_dag_event,0);
dap_sign_t * l_sign = dap_chain_cs_dag_event_get_sign(a_dag_event, 0);
if ( l_sign == NULL){
log_it(L_WARNING, "Event is NOT signed with anything");
return -4;
}
dap_enc_key_t * l_key = dap_sign_to_enc_key( l_sign);
if ( l_key == NULL){
dap_chain_hash_fast_t l_pkey_hash;
if (!dap_sign_get_pkey_hash(l_sign, &l_pkey_hash)) {
log_it(L_WARNING, "Event's sign has no any key");
return -5;
}
dap_chain_addr_fill (&l_addr,l_key,&a_dag->chain->net_id );
dap_enc_key_delete (l_key); // TODO cache all this operations to prevent useless memory copy ops
dap_chain_addr_fill(&l_addr, l_sign->header.type, &l_pkey_hash, &a_dag->chain->net_id);
if (l_sig_pos == 0) {
dap_chain_datum_t *l_datum = (dap_chain_datum_t *)dap_chain_cs_dag_event_get_datum(a_dag_event);
if (l_datum->header.type_id == DAP_CHAIN_DATUM_TX) {
dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t *)l_datum->data;
if (!dap_chain_net_srv_stake_validator(&l_addr, l_tx)) {
return -6;
}
}
}
/*
dap_chain_datum_t *l_datum = dap_chain_cs_dag_event_get_datum(a_dag_event);
// transaction include emission?
......
......@@ -62,6 +62,7 @@
#include "dap_cert.h"
#include "dap_chain_common.h"
#include "dap_chain_net.h"
#include "dap_chain_net_srv.h"
#include "dap_chain_node_client.h"
#include "dap_chain_node_cli.h"
#include "dap_chain_node_cli_cmd.h"
......@@ -249,10 +250,8 @@ static void s_gbd_history_callback_notify (void * a_arg, const char a_op_code, c
const size_t a_value_len)
{
(void) a_op_code;
UNUSED(a_key);
UNUSED(a_value_len);
UNUSED(a_prefix);
UNUSED(a_group);
UNUSED(a_value_len);
if (a_arg) {
dap_chain_net_t * l_net = (dap_chain_net_t *) a_arg;
s_net_set_go_sync(l_net);
......@@ -264,6 +263,31 @@ static void s_gbd_history_callback_notify (void * a_arg, const char a_op_code, c
}
}*/
}
if (!dap_config_get_item_bool_default(g_config, "srv", "order_signed_only", false)) {
return;
}
dap_chain_net_t *l_net = (dap_chain_net_t *)a_arg;
char *l_gdb_group_str = dap_chain_net_srv_order_get_gdb_group(l_net);
if (strcmp(a_group, l_gdb_group_str)) {
dap_chain_net_srv_order_t *l_order = (dap_chain_net_srv_order_t *)a_value;
if (l_order->version == 1) {
dap_chain_global_db_gr_del((char *)a_key, a_group);
} else {
dap_sign_t *l_sign = (dap_sign_t *)&l_order->ext[l_order->ext_size];
dap_chain_hash_fast_t l_pkey_hash;
if (!dap_sign_get_pkey_hash(l_sign, &l_pkey_hash)) {
return;
}
dap_chain_addr_t l_addr = {0};
dap_chain_addr_fill(&l_addr, l_sign->header.type, &l_pkey_hash, &l_net->pub.id);
uint64_t l_solvency = dap_chain_ledger_calc_balance(l_net->pub.ledger, &l_addr, l_order->price_ticker);
if (l_solvency < l_order->price) {
dap_chain_global_db_gr_del((char *)a_key, a_group);
}
// TODO check for delegated key
}
DAP_DELETE(l_gdb_group_str);
}
}
/**
......
......@@ -457,7 +457,7 @@ static int s_cli_net_srv( int argc, char **argv, void *arg_func, char **a_str_re
strncpy(l_price_token, l_price_token_str, DAP_CHAIN_TICKER_SIZE_MAX - 1);
char * l_order_new_hash_str = dap_chain_net_srv_order_create(
l_net,l_direction, l_srv_uid, l_node_addr,l_tx_cond_hash, l_price, l_price_unit,
l_price_token, l_expires, (uint8_t *)l_ext, strlen(l_ext) + 1, l_region_str, l_continent_num);
l_price_token, l_expires, (uint8_t *)l_ext, strlen(l_ext) + 1, l_region_str, l_continent_num, NULL);
if (l_order_new_hash_str)
dap_string_append_printf( l_string_ret, "Created order %s\n", l_order_new_hash_str);
else{
......
......@@ -78,7 +78,12 @@ void dap_chain_net_srv_order_deinit()
size_t dap_chain_net_srv_order_get_size(dap_chain_net_srv_order_t *a_order)
{
return a_order ? sizeof(dap_chain_net_srv_order_t) + a_order->ext_size : 0;
size_t l_sign_size = 0;
if (a_order->version > 1) {
dap_sign_t *l_sign = (dap_sign_t *)&a_order->ext[a_order->ext_size];
l_sign_size = dap_sign_get_size(l_sign);
}
return a_order ? sizeof(dap_chain_net_srv_order_t) + a_order->ext_size + l_sign_size : 0;
}
/**
......@@ -218,7 +223,8 @@ char * dap_chain_net_srv_order_create(
const uint8_t *a_ext,
uint32_t a_ext_size,
const char *a_region,
int8_t a_continent_num
int8_t a_continent_num,
dap_enc_key_t *a_key
)
{
UNUSED(a_expires);
......@@ -235,7 +241,7 @@ char * dap_chain_net_srv_order_create(
}
dap_chain_hash_fast_t l_order_hash;
l_order->version = 1;
l_order->version = a_key ? 2 : 1;
l_order->srv_uid = a_srv_uid;
l_order->direction = a_direction;
l_order->ts_created = (dap_chain_time_t) time(NULL);
......@@ -250,7 +256,16 @@ char * dap_chain_net_srv_order_create(
if ( a_price_ticker)
strncpy(l_order->price_ticker, a_price_ticker,sizeof(l_order->price_ticker)-1);
if (a_key) {
dap_sign_t *l_sign = dap_sign_create(a_key, l_order, sizeof(dap_chain_net_srv_order_t) + l_order->ext_size, 0);
if (!l_sign) {
return NULL;
}
size_t l_sign_size = dap_sign_get_size(l_sign); // sign data
l_order = DAP_REALLOC(l_order, sizeof(dap_chain_net_srv_order_t) + l_order->ext_size + l_sign_size);
memcpy(&l_order->ext[l_order->ext_size], l_sign, l_sign_size);
DAP_DELETE(l_sign);
}
size_t l_order_size = dap_chain_net_srv_order_get_size(l_order);
dap_hash_fast( l_order, l_order_size, &l_order_hash );
char * l_order_hash_str = dap_chain_hash_fast_to_str_new( &l_order_hash );
......
......@@ -104,7 +104,8 @@ char *dap_chain_net_srv_order_create(dap_chain_net_t * a_net,
const uint8_t *a_ext,
uint32_t a_ext_size,
const char *a_region,
int8_t a_continent_num
int8_t a_continent_num,
dap_enc_key_t *a_key
);
int dap_chain_net_srv_order_save(dap_chain_net_t * a_net, dap_chain_net_srv_order_t *a_order);
......
cmake_minimum_required(VERSION 2.8)
project (dap_chain_net_srv_stake)
file(GLOB DAP_SRV_STAKE_SRCS *.c)
file(GLOB DAP_SRV_STAKE_HEADERS include/*.h)
add_library(${PROJECT_NAME} STATIC ${DAP_SRV_STAKE_SRCS} ${DAP_SRV_STAKE_HEADERS})
target_include_directories(dap_chain_crypto INTERFACE .)
target_include_directories(${PROJECT_NAME} PUBLIC include)
target_link_libraries(${PROJECT_NAME} dap_core dap_crypto dap_chain dap_chain_crypto dap_chain_net dap_chain_net_srv)
/*
* Authors:
* Roman Khlopkov <roman.khlopkov@demlabs.net>
* DeM Labs Inc. https://demlabs.net
* DeM Labs Open source community https://gitlab.demlabs.net
* Copyright (c) 2017-2020
* All rights reserved.
This file is part of DAP (Deus Applications Prototypes) the open source project
DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DAP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
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"
#include "dap_chain_mempool.h"
#include "dap_chain_net_srv_common.h"
#include "dap_chain_net_srv_stake.h"
#define LOG_TAG "dap_chain_net_srv_stake"
static int s_cli_srv_stake(int a_argc, char **a_argv, void *a_arg_func, char **a_str_reply);
static dap_chain_net_srv_stake_t *s_srv_stake;
/**
* @brief dap_stream_ch_vpn_init Init actions for VPN stream channel
* @param vpn_addr Zero if only client mode. Address if the node shares its local VPN
* @param vpn_mask Zero if only client mode. Mask if the node shares its local VPN
* @return 0 if everything is okay, lesser then zero if errors
*/
int dap_chain_net_srv_stake_init()
{
dap_chain_node_cli_cmd_item_create("srv_stake", s_cli_srv_stake, NULL, "Delegated stake service commands",
"srv_stake order create -net <net name> -from_addr <addr> -token <ticker> -coins <value> -to_addr <addr> -fee_percent <value>\n"
"\tCreate a new order with specified amount of datoshi to delegate it to the specified address."
"The fee with specified percent with this delagation will be returned to the fee address pointed by delegator\n"
"srv_stake order remove -net <net name> -order <order hash>\n"
"\tRemove order with specified hash\n"
"srv_stake order update -net <net name> -order <order hash> {-from_addr <addr> | -token <ticker> -coins <value> | "
"-to_addr <addr> | -fee_percent <value>}\n"
"\tUpdate order with specified hash\n"
"srv_stake order list -net <net name>\n"
"\tGet the stake orders list within specified net name\n"
"srv_stake delegate -order <order hash> -net <net name> -wallet <wallet_name> -fee_addr <addr>\n"
"\tDelegate tokens with specified order within specified net name. Specify fee address\n"
"srv_stake transactions -net <net name> {-addr <addr from>}"
"\tShow the list of active stake transactions (optional delegated from addr)"
"srv_stake invalidate -net <net name> -tx <transaction hash> -wallet <wallet name>"
"\tInvalidate stake transaction by hash within net name and return stake to specified wallet"
);
s_srv_stake = DAP_NEW_Z(dap_chain_net_srv_stake_t);
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++) {
dap_ledger_t *l_ledger = l_net_list[i]->pub.ledger;
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_out_cond;
int l_out_cond_idx;
char l_token[DAP_CHAIN_TICKER_SIZE_MAX];
// Find all transactions
do {
l_tx_tmp = dap_chain_ledger_tx_cache_find_out_cond(l_ledger, &l_tx_cur_hash, &l_out_cond, &l_out_cond_idx, l_token);
if (!l_tx_tmp) {
break;
}
if (l_out_cond->header.subtype != DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE)
continue;
if (dap_chain_ledger_tx_hash_is_used_out_item(l_ledger, &l_tx_cur_hash, l_out_cond_idx))
continue;
// Create the stake item
dap_chain_net_srv_stake_item_t *l_stake = DAP_NEW_Z(dap_chain_net_srv_stake_item_t);
l_stake->net = l_net_list[i];
dap_stpcpy(l_stake->token, l_token);
l_stake->value = l_out_cond->header.value;
dap_chain_tx_sig_t *l_tx_sig = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get(l_tx_tmp, NULL,
TX_ITEM_TYPE_SIG, NULL);
dap_sign_t *l_sign = dap_chain_datum_tx_item_sign_get_sig((dap_chain_tx_sig_t *)l_tx_sig);
dap_chain_hash_fast_t l_pkey_hash;
if (!dap_sign_get_pkey_hash(l_sign, &l_pkey_hash)) {
continue;
}
dap_chain_addr_fill(&l_stake->addr_from, l_sign->header.type, &l_pkey_hash, &l_net_list[i]->pub.id);
memcpy(&l_stake->addr_to, l_out_cond->params, sizeof(dap_chain_addr_t));
memcpy(&l_stake->addr_fee, &l_out_cond->subtype.srv_stake.fee_addr, sizeof(dap_chain_addr_t));
l_stake->fee_value = l_out_cond->subtype.srv_stake.fee_value;
memcpy(&l_stake->tx_hash, &l_tx_cur_hash, sizeof(dap_chain_hash_fast_t));
HASH_ADD(hh, s_srv_stake->itemlist, tx_hash, sizeof(dap_chain_hash_fast_t), l_stake);
} while (l_tx_tmp);
}
DAP_DELETE(l_net_list);
return 0;
}
void dap_chain_net_srv_stake_deinit()
{
dap_chain_net_srv_stake_item_t *l_stake = NULL, *l_tmp;
HASH_ITER(hh, s_srv_stake->itemlist, l_stake, l_tmp) {
HASH_DEL(s_srv_stake->itemlist, l_stake);
DAP_DELETE(l_stake);
}
DAP_DELETE(s_srv_stake);
}
bool dap_chain_net_srv_stake_verificator(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx)
{
UNUSED(a_cond);
UNUSED(a_tx);
return false;
}
bool dap_chain_net_srv_stake_validator(dap_chain_addr_t *a_addr, dap_chain_datum_tx_t *a_tx)
{
dap_chain_net_srv_stake_item_t *l_stake = NULL;
HASH_FIND(hh, s_srv_stake->itemlist, a_addr, sizeof(dap_chain_addr_t), l_stake);
if (l_stake == NULL) { // public key not delegated for this network
return true;
}
uint64_t l_outs_sum = 0, l_fee_sum = 0;
dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(a_tx, TX_ITEM_TYPE_OUT_ALL, NULL);
uint32_t l_out_idx_tmp = 0; // current index of 'out' item
for (dap_list_t *l_list_tmp = l_list_out_items; l_list_tmp; l_list_tmp = dap_list_next(l_list_tmp), l_out_idx_tmp++) {
dap_chain_tx_item_type_t l_type = *(uint8_t *)l_list_tmp->data;
if (l_type == TX_ITEM_TYPE_OUT_COND) {
dap_chain_tx_out_cond_t *l_out_cond = (dap_chain_tx_out_cond_t *)l_list_tmp->data;
l_outs_sum += l_out_cond->header.value;
}
if (l_type == TX_ITEM_TYPE_OUT) {
dap_chain_tx_out_t *l_out = (dap_chain_tx_out_t *)l_list_tmp->data;
if (memcmp(&l_stake->addr_fee, &l_out->addr, sizeof(dap_chain_addr_t))) {
l_fee_sum += l_out->header.value;
} else {
l_outs_sum += l_out->header.value;
}
} else { // TX_ITEM_TYPE_OUT_EXT
dap_chain_tx_out_ext_t *l_out_ext = (dap_chain_tx_out_ext_t *)l_list_tmp->data;
if (memcmp(&l_stake->addr_fee, &l_out_ext->addr, sizeof(dap_chain_addr_t))) {
l_fee_sum += l_out_ext->header.value;
} else {
l_outs_sum += l_out_ext->header.value;
}
}
}
dap_list_free(l_list_out_items);
if (l_outs_sum * l_stake->fee_value / 100.0 < l_fee_sum) {
return false;
}
return true;
}
static dap_chain_datum_tx_t *s_stake_tx_create(dap_chain_net_srv_stake_item_t *a_stake, dap_chain_wallet_t *a_wallet)
{
if (!a_stake || !a_stake->net || !a_stake->addr_to.addr_ver || !a_stake->addr_from.addr_ver ||
!a_stake->addr_fee.addr_ver || !*a_stake->token || !a_wallet) {
return NULL;
}
// 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_stake->net->pub.name);
dap_chain_addr_t *l_owner_addr = (dap_chain_addr_t *)dap_chain_wallet_get_addr(a_wallet, a_stake->net->pub.id);
if (memcmp(l_owner_addr, &a_stake->addr_from, sizeof(dap_chain_addr_t))) {
log_it(L_WARNING, "Odree and wallet address do not match");
return NULL;
}
dap_enc_key_t *l_owner_key = dap_chain_wallet_get_key(a_wallet, 0);
uint64_t l_value_sell = 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_stake->token, l_owner_addr, a_stake->value, &l_value_sell);
if(!l_list_used_out) {
dap_chain_datum_tx_delete(l_tx);
DAP_DELETE(l_owner_addr);
log_it(L_WARNING, "Nothing to change (not enough funds)");
return NULL;
}
// 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) {
dap_chain_datum_tx_delete(l_tx);
DAP_DELETE(l_owner_addr);
log_it(L_ERROR, "Can't compose the transaction input");
return NULL;
}
// add 'out_cond' & 'out' items
{
dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_STAKE_ID };
dap_chain_tx_out_cond_t *l_tx_out = dap_chain_datum_tx_item_out_cond_create_srv_stake(l_uid, a_stake->value, a_stake->fee_value, &a_stake->addr_fee,
(void *)&a_stake->addr_to, sizeof(dap_chain_addr_t));
if (!l_tx_out) {
dap_chain_datum_tx_delete(l_tx);
DAP_DELETE(l_owner_addr);
log_it(L_ERROR, "Can't compose the transaction conditional output");
return NULL;
}
dap_chain_datum_tx_add_item(&l_tx, (const uint8_t *)l_tx_out);
DAP_DELETE(l_tx_out);
// coin back
uint64_t l_value_back = l_value_sell - a_stake->value;
if (l_value_back) {
if (dap_chain_datum_tx_add_out_item(&l_tx, l_owner_addr, l_value_back) != 1) {
dap_chain_datum_tx_delete(l_tx);
DAP_DELETE(l_owner_addr);
log_it(L_ERROR, "Cant add coin back output");
return NULL;
}
}
}
DAP_DELETE(l_owner_addr);
// add 'sign' item
if(dap_chain_datum_tx_add_sign_item(&l_tx, l_owner_key) != 1) {
dap_chain_datum_tx_delete(l_tx);
log_it(L_ERROR, "Can't add sign output");
return NULL;
}
return l_tx;
}
// Put the transaction to mempool or directly to chains & write transaction's hash to the price
static bool s_stake_tx_put(dap_chain_datum_tx_t *a_tx, dap_chain_net_t *a_net)
{
// Put the transaction to mempool or directly to chains
size_t l_tx_size = dap_chain_datum_tx_get_size(a_tx);
dap_chain_datum_t *l_datum = dap_chain_datum_create(DAP_CHAIN_DATUM_TX, a_tx, l_tx_size);
DAP_DELETE(a_tx);
dap_chain_t *l_chain = dap_chain_net_get_chain_by_chain_type(a_net, CHAIN_TYPE_TX);
if (!l_chain) {
return false;
}
// Processing will be made according to autoprocess policy
if (dap_chain_mempool_datum_add(l_datum, l_chain)) {
DAP_DELETE(l_datum);
return false;
}
return true;
}
static bool s_stake_tx_invalidate(dap_chain_net_srv_stake_item_t *a_stake, dap_chain_wallet_t *a_wallet)
{
// 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_stake->net->pub.name);
dap_chain_addr_t *l_owner_addr = (dap_chain_addr_t *)dap_chain_wallet_get_addr(a_wallet, a_stake->net->pub.id);
dap_enc_key_t *l_owner_key = dap_chain_wallet_get_key(a_wallet, 0);
// create and add reciept
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_STAKE_ID };
dap_chain_datum_tx_receipt_t *l_receipt = dap_chain_datum_tx_receipt_create(l_uid, l_unit, 0, a_stake->value, NULL, 0);
dap_chain_datum_tx_add_item(&l_tx, (byte_t *)l_receipt);
DAP_DELETE(l_receipt);
// add 'in' item to buy from conditional transaction
dap_chain_datum_tx_t *l_cond_tx = dap_chain_ledger_tx_find_by_hash(l_ledger, &a_stake->tx_hash);
if (!l_cond_tx) {
log_it(L_WARNING, "Requested conditional transaction not found");
return false;
}
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_stake->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_stake->tx_hash, l_prev_cond_idx, 0);
// add 'out' item
if (dap_chain_datum_tx_add_out_item(&l_tx, l_owner_addr, l_tx_out_cond->header.value) == -1) {
dap_chain_datum_tx_delete(l_tx);
DAP_DELETE(l_owner_addr);
log_it(L_ERROR, "Cant add returning coins output");
return false;
}
DAP_DELETE(l_owner_addr);
// add 'sign' items
if(dap_chain_datum_tx_add_sign_item(&l_tx, l_owner_key) != 1) {
dap_chain_datum_tx_delete(l_tx);
log_it( L_ERROR, "Can't add sign output");
return false;
}
if (!s_stake_tx_put(l_tx, a_stake->net)) {
return false;
}
return true;
}
char *s_stake_order_create(dap_chain_net_srv_stake_item_t *a_item)
{
dap_chain_hash_fast_t l_tx_hash = {};
dap_srv_stake_order_ext_t l_ext;
memcpy(&l_ext.addr_from, &a_item->addr_from, sizeof(dap_chain_addr_t));
memcpy(&l_ext.addr_to, &a_item->addr_to, sizeof(dap_chain_addr_t));
l_ext.fee_value = a_item->fee_value;
uint32_t l_ext_size = sizeof(dap_srv_stake_order_ext_t);
dap_chain_node_addr_t *l_node_addr = dap_chain_net_get_cur_addr(a_item->net);
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_STAKE_ID };
char *l_order_hash_str = dap_chain_net_srv_order_create(a_item->net, SERV_DIR_BUY, l_uid, *l_node_addr,
l_tx_hash, a_item->value, l_unit, a_item->token, 0,
(uint8_t *)&l_ext, l_ext_size, NULL, 0, NULL);
return l_order_hash_str;
}
dap_chain_net_srv_stake_item_t *s_stake_item_from_order(dap_chain_net_t *a_net, dap_chain_net_srv_order_t *a_order)
{
dap_chain_net_srv_stake_item_t *l_item = DAP_NEW_Z(dap_chain_net_srv_stake_item_t);
dap_srv_stake_order_ext_t *l_ext = (dap_srv_stake_order_ext_t *)a_order->ext;
memcpy(&l_item->addr_from, &l_ext->addr_from, sizeof(dap_chain_addr_t));
memcpy(&l_item->addr_to, &l_ext->addr_to, sizeof(dap_chain_addr_t));
l_item->fee_value = l_ext->fee_value;
l_item->net = a_net;
l_item->value = a_order->price;
strcpy(l_item->token, a_order->price_ticker);
return l_item;
}
static int s_cli_srv_stake_order(int a_argc, char **a_argv, int a_arg_index, char **a_str_reply)
{
enum {
CMD_NONE, CMD_CREATE, CMD_REMOVE, CMD_LIST, CMD_UPDATE
};
int l_cmd_num = CMD_NONE;
if(dap_chain_node_cli_find_option_val(a_argv, a_arg_index, min(a_argc, a_arg_index + 1), "create", NULL)) {
l_cmd_num = CMD_CREATE;
}
else if(dap_chain_node_cli_find_option_val(a_argv, a_arg_index, min(a_argc, a_arg_index + 1), "remove", NULL)) {
l_cmd_num = CMD_REMOVE;
}
else if(dap_chain_node_cli_find_option_val(a_argv, a_arg_index, min(a_argc, a_arg_index + 1), "list", NULL)) {
l_cmd_num = CMD_LIST;
}
else if(dap_chain_node_cli_find_option_val(a_argv, a_arg_index, min(a_argc, a_arg_index + 1), "update", NULL)) {
l_cmd_num = CMD_UPDATE;
}
int l_arg_index = a_arg_index + 1;
switch (l_cmd_num) {
case CMD_CREATE: {
const char *l_net_str = NULL, *l_token_str = NULL, *l_coins_str = NULL;
const char *l_addr_from_str = NULL, *l_addr_to_str = NULL, *l_fee_str = NULL;
dap_chain_net_t *l_net = NULL;
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-net", &l_net_str);
if (!l_net_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order create' required parameter -net");
return -3;
}
l_net = dap_chain_net_by_name(l_net_str);
if (!l_net) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Network %s not found", l_net_str);
return -4;
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-token", &l_token_str);
if (!l_token_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order create' required parameter -token");
return -5;
}
if (!dap_chain_ledger_token_ticker_check(l_net->pub.ledger, l_token_str)) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Token ticker %s not found", l_token_str);
return -6;
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-coins", &l_coins_str);
if (!l_coins_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order create' required parameter -coins");
return -7;
}
uint64_t l_value = strtoull(l_coins_str, NULL, 10);
if (!l_value) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Format -coins <unsigned long long>");
return -8;
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-addr_from", &l_addr_from_str);
if (!l_addr_from_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order create' required parameter -addr_from");
return -9;
}
dap_chain_addr_t *l_addr_from = dap_chain_addr_from_str(l_addr_from_str);
if (!l_addr_from) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Wrong address format");
return -10;
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-addr_to", &l_addr_to_str);
if (!l_addr_to_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order create' required parameter -addr_to");
return -9;
}
dap_chain_addr_t *l_addr_to = dap_chain_addr_from_str(l_addr_to_str);
if (!l_addr_to) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Wrong address format");
return -10;
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-fee_percent", &l_fee_str);
if (!l_fee_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order create' required parameter -fee_percent");
return -11;
}
long double l_fee = strtold(l_fee_str, NULL);
if (!l_fee) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Format -fee_percent <long double> %");
return -12;
}
// Create the stake item
dap_chain_net_srv_stake_item_t *l_stake = DAP_NEW_Z(dap_chain_net_srv_stake_item_t);
l_stake->net = l_net;
dap_stpcpy(l_stake->token, l_token_str);
l_stake->value = l_value;
memcpy(&l_stake->addr_from, l_addr_from, sizeof(dap_chain_addr_t));
memcpy(&l_stake->addr_to, l_addr_to, sizeof(dap_chain_addr_t));
DAP_DELETE(l_addr_from);
DAP_DELETE(l_addr_to);
l_stake->fee_value = l_fee;
// Create the order & put it to GDB
char *l_order_hash_str = s_stake_order_create(l_stake);
if (l_order_hash_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Successfully created order %s", l_order_hash_str);
DAP_DELETE(l_order_hash_str);
DAP_DELETE(l_stake);
} else {
dap_chain_node_cli_set_reply_text(a_str_reply, "Can't compose the order");
DAP_DELETE(l_stake);
return -15;
}
} break;
case CMD_REMOVE: {
const char *l_net_str = NULL, *l_order_hash_str = NULL;
dap_chain_net_t *l_net = NULL;
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-net", &l_net_str);
if (!l_net_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order remove' requires parameter -net");
return -3;
}
l_net = dap_chain_net_by_name(l_net_str);
if (!l_net) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Network %s not found", l_net_str);
return -4;
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-order", &l_order_hash_str);
if (!l_net_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order remove' requires parameter -order");
return -13;
}
if (dap_chain_net_srv_order_delete_by_hash_str(l_net, l_order_hash_str)) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Can't remove order %s\n", l_order_hash_str);
return -14;
}
dap_chain_node_cli_set_reply_text(a_str_reply, "Stake order successfully removed");
} break;
case CMD_UPDATE: {
const char *l_net_str = NULL, *l_token_str = NULL, *l_coins_str = NULL;
const char *l_addr_from_str = NULL, *l_addr_to_str = NULL, *l_fee_str = NULL;
char *l_order_hash_str = NULL;
dap_chain_net_t *l_net = NULL;
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-net", &l_net_str);
if (!l_net_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order update' required parameter -net");
return -3;
}
l_net = dap_chain_net_by_name(l_net_str);
if (!l_net) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Network %s not found", l_net_str);
return -4;
}
if (!l_net_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order update' requires parameter -order");
return -13;
}
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_node_cli_set_reply_text(a_str_reply, "Can't find order %s\n", l_order_hash_str);
return -14;
}
dap_chain_net_srv_stake_item_t *l_stake = s_stake_item_from_order(l_net, l_order);
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-token", &l_token_str);
if (l_token_str) {
if (!dap_chain_ledger_token_ticker_check(l_net->pub.ledger, l_token_str)) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Token ticker %s not found", l_token_str);
return -6;
}
strcpy(l_stake->token, l_token_str);
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-coins", &l_coins_str);
if (l_coins_str) {
uint64_t l_value = strtoull(l_coins_str, NULL, 10);
if (!l_value) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Format -coins <unsigned long long>");
return -8;
}
l_stake->value = l_value;
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-addr_from", &l_addr_from_str);
if (l_addr_from_str) {
dap_chain_addr_t *l_addr_from = dap_chain_addr_from_str(l_addr_from_str);
if (!l_addr_from) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Wrong address format");
return -10;
}
memcpy(&l_stake->addr_from, l_addr_from, sizeof(dap_chain_addr_t));
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-addr_to", &l_addr_to_str);
if (l_addr_to_str) {
dap_chain_addr_t *l_addr_to = dap_chain_addr_from_str(l_addr_to_str);
if (!l_addr_to) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Wrong address format");
return -10;
}
memcpy(&l_stake->addr_to, l_addr_to, sizeof(dap_chain_addr_t));
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-fee_percent", &l_fee_str);
if (l_fee_str) {
long double l_fee = strtold(l_fee_str, NULL);
if (!l_fee) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Format -fee_percent <long double> %");
return -12;
}
}
if (!l_token_str && !l_coins_str && !l_addr_from_str && !l_addr_to_str && !l_fee_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "At least one of updating parameters is mandatory");
return -16;
}
// Create the order & put it to GDB
dap_chain_net_srv_order_delete_by_hash_str(l_net, l_order_hash_str);
l_order_hash_str = s_stake_order_create(l_stake);
if (l_order_hash_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Successfully created order %s", l_order_hash_str);
DAP_DELETE(l_order_hash_str);
DAP_DELETE(l_stake);
} else {
dap_chain_node_cli_set_reply_text(a_str_reply, "Can't compose the order");
DAP_DELETE(l_stake);
return -15;
}
} break;
case CMD_LIST: {
const char *l_net_str = NULL;
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-net", &l_net_str);
if (!l_net_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'order list' required parameter -net");
return -3;
}
dap_chain_net_t *l_net = dap_chain_net_by_name(l_net_str);
if (!l_net) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Network %s not found", l_net_str);
return -4;
}
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_stake_item_t *l_stake;
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_STAKE_ID)
continue;
// TODO add filters to list (token, address, etc.)
l_stake = s_stake_item_from_order(l_net, l_order);
char *l_addr = dap_chain_addr_to_str(&l_stake->addr_to);
dap_string_append_printf(l_reply_str, "%s %lu %s %s %llf\n", l_orders[i].key, l_stake->value, l_stake->token,
l_addr, l_stake->fee_value);
DAP_DELETE(l_addr);
DAP_DELETE(l_stake);
}
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;
default: {
dap_chain_node_cli_set_reply_text(a_str_reply, "Subcommand %s not recognized", a_argv[a_arg_index]);
return -2;
}
}
return 0;
}
static int s_cli_srv_stake(int a_argc, char **a_argv, void *a_arg_func, char **a_str_reply)
{
UNUSED(a_arg_func);
enum {
CMD_NONE, CMD_ORDER, CMD_DELEGATE, CMD_TX, CMD_INVALIDATE
};
int l_arg_index = 1;
int l_cmd_num = CMD_NONE;
if (dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "order", NULL)) {
l_cmd_num = CMD_ORDER;
}
else if (dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "delegate", NULL)) {
l_cmd_num = CMD_DELEGATE;
}
switch (l_cmd_num) {
case CMD_ORDER:
return s_cli_srv_stake_order(a_argc, a_argv, l_arg_index + 1, a_str_reply);
case CMD_DELEGATE: {
const char *l_net_str = NULL, *l_wallet_str = NULL, *l_order_hash_str = NULL, *l_addr_fee_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) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'delegate' required parameter -net");
return -3;
}
dap_chain_net_t *l_net = dap_chain_net_by_name(l_net_str);
if (!l_net) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Network %s not found", l_net_str);
return -4;
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-wallet", &l_wallet_str);
if (!l_wallet_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'delegate' required parameter -wallet");
return -17;
}
dap_chain_wallet_t *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 -18;
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-order", &l_order_hash_str);
if (!l_order_hash_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'delegate' required parameter -order");
return -13;
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-fee_addr", &l_addr_fee_str);
if (!l_addr_fee_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'delegate' required parameter -fee_addr");
return -9;
}
dap_chain_addr_t *l_addr_fee = dap_chain_addr_from_str(l_addr_fee_str);
if (!l_addr_fee) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Wrong address format");
return -10;
}
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_stake_item_t *l_stake = s_stake_item_from_order(l_net, l_order);
memcpy(&l_stake->addr_fee, l_addr_fee, sizeof(dap_chain_addr_t));
DAP_DELETE(l_addr_fee);
// Create conditional transaction
dap_chain_datum_tx_t *l_tx = s_stake_tx_create(l_stake, l_wallet);
dap_chain_wallet_close(l_wallet);
if (l_tx && s_stake_tx_put(l_tx, l_net)) {
// TODO send request to order owner to delete it
dap_chain_net_srv_order_delete_by_hash_str(l_net, l_order_hash_str);
}
DAP_DELETE(l_order);
dap_chain_node_cli_set_reply_text(a_str_reply, l_tx ? "Stake transaction has done" :
"Stake transaction error");
if (!l_tx) {
DAP_DELETE(l_stake);
return -19;
}
HASH_ADD(hh, s_srv_stake->itemlist, addr_to, sizeof(dap_chain_addr_t), l_stake);
} else {
DAP_DELETE(l_addr_fee);
dap_chain_node_cli_set_reply_text(a_str_reply, "Specified order not found");
return -14;
}
} break;
case CMD_TX: {
const char *l_net_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) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'transactions' required parameter -net");
return -3;
}
dap_chain_net_t *l_net = dap_chain_net_by_name(l_net_str);
if (!l_net) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Network %s not found", l_net_str);
return -4;
}
dap_chain_net_srv_stake_item_t *l_stake = NULL, *l_tmp;
dap_string_t *l_reply_str = dap_string_new("");
HASH_ITER(hh, s_srv_stake->itemlist, l_stake, l_tmp) {
if (l_stake->net->pub.id.uint64 != l_net->pub.id.uint64) {
continue;
}
char *l_tx_hash_str = dap_chain_hash_fast_to_str_new(&l_stake->tx_hash);
char *l_addr_from_str = dap_chain_addr_to_str(&l_stake->addr_from);
char *l_addr_to_str = dap_chain_addr_to_str(&l_stake->addr_to);
char *l_addr_fee_str = dap_chain_addr_to_str(&l_stake->addr_fee);
dap_string_append_printf(l_reply_str, "%s %s %lu %s %s %s %llf\n", l_tx_hash_str, l_stake->token,
l_stake->value, l_addr_from_str, l_addr_to_str,
l_addr_fee_str, l_stake->fee_value);
DAP_DELETE(l_tx_hash_str);
DAP_DELETE(l_addr_from_str);
DAP_DELETE(l_addr_to_str);
DAP_DELETE(l_addr_fee_str);
}
if (!l_reply_str->len) {
dap_string_append(l_reply_str, "Pricelist is empty");
}
*a_str_reply = dap_string_free(l_reply_str, false);
} break;
case CMD_INVALIDATE: {
const char *l_net_str = NULL, *l_wallet_str = NULL, *l_tx_hash_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) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'delegate' required parameter -net");
return -3;
}
dap_chain_net_t *l_net = dap_chain_net_by_name(l_net_str);
if (!l_net) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Network %s not found", l_net_str);
return -4;
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-wallet", &l_wallet_str);
if (!l_wallet_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'delegate' required parameter -wallet");
return -17;
}
dap_chain_wallet_t *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 -18;
}
dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-tx", &l_tx_hash_str);
if (!l_tx_hash_str) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'delegate' required parameter -tx");
return -13;
}
dap_chain_hash_fast_t l_tx_hash = {};
dap_chain_str_to_hash_fast(l_tx_hash_str, &l_tx_hash);
dap_chain_net_srv_stake_item_t *l_stake = NULL, *l_tmp;
HASH_ITER(hh, s_srv_stake->itemlist, l_stake, l_tmp) {
if (!memcmp(&l_stake->tx_hash, &l_tx_hash, sizeof(dap_chain_hash_fast_t))) {
break;
}
}
if (!l_stake) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Transaction %s not found", l_tx_hash_str);
dap_chain_wallet_close(l_wallet);
return -20;
}
bool l_success = s_stake_tx_invalidate(l_stake, l_wallet);
dap_chain_wallet_close(l_wallet);
if (l_success) {
dap_chain_node_cli_set_reply_text(a_str_reply, "Stake successfully returned to owner");
HASH_DEL(s_srv_stake->itemlist, l_stake);
} else {
dap_chain_node_cli_set_reply_text(a_str_reply, "Can't invalidate transaction %s", l_tx_hash_str);
return -21;
}
} break;
default: {
dap_chain_node_cli_set_reply_text(a_str_reply, "Command %s not recognized", a_argv[l_arg_index]);
return -1;
}
}
return 0;
}
/*
* Authors:
* Roman Khlopkov <roman.khlopkov@demlabs.net>
* DeM Labs Inc. https://demlabs.net
* DeM Labs Open source community https://gitlab.demlabs.net
* Copyright (c) 2017-2020
* All rights reserved.
This file is part of DAP (Deus Applications Prototypes) the open source project
DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DAP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with any DAP based project. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "dap_chain_net_srv.h"
#include "dap_chain_net_srv_order.h"
#define DAP_CHAIN_NET_SRV_STAKE_ID 0x3
typedef struct dap_chain_net_srv_stake_item {
dap_chain_net_t *net;
char token[DAP_CHAIN_TICKER_SIZE_MAX];
uint64_t value;
dap_chain_addr_t addr_from;
dap_chain_addr_t addr_to;
dap_chain_addr_t addr_fee;
long double fee_value;
dap_chain_hash_fast_t tx_hash;
dap_chain_hash_fast_t order_hash;
UT_hash_handle hh;
} dap_chain_net_srv_stake_item_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_stake_order_ext {
dap_chain_addr_t addr_from;
dap_chain_addr_t addr_to;
long double fee_value;
} dap_srv_stake_order_ext_t;
typedef struct dap_chain_net_srv_stake {
dap_chain_net_srv_stake_item_t *itemlist;
} dap_chain_net_srv_stake_t;
int dap_chain_net_srv_xchange_init();
void dap_chain_net_srv_xchange_deinit();
bool dap_chain_net_srv_xchange_verificator(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx);