diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c27852a5b13bdd71987627c3d0c6c6fe616e580..2b42568ef18b03473a9a18d2e7fa2eb87093f28c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,6 +111,12 @@ if (CELLFRAME_MODULES MATCHES "srv-vpn") set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_chain_net_srv_vpn ) endif() +# Enable service eXchange +if (CELLFRAME_MODULES MATCHES "srv-xchange") + message("[+] Module 'srv-xchange'") + set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_chain_net_srv_xchange ) +endif() + if (WIN32) set(CELLFRAME_LIBS ${CELLFRAME_LIBS} KERNEL32 USER32 SHELL32 WINMM GDI32 ADVAPI32 Ole32 Version Imm32 OleAut32 ws2_32 ntdll psapi diff --git a/dap-sdk/crypto/src/dap_cert.c b/dap-sdk/crypto/src/dap_cert.c index fe22a7a369c3961fe352a1955797d10b2d5d5d26..5f5da67741246f0be603313d4a33cf54a2bc8c82 100755 --- a/dap-sdk/crypto/src/dap_cert.c +++ b/dap-sdk/crypto/src/dap_cert.c @@ -468,7 +468,10 @@ void dap_cert_dump(dap_cert_t * a_cert) printf("%s\t%u\t%u\t%u\n", l_meta_item->key, l_meta_item->type, l_meta_item->length, *(uint32_t *)l_meta_item->value); break; default: - printf("%s\t%u\t%u\t0x%016lX\n", l_meta_item->key, l_meta_item->type, l_meta_item->length, *(uint64_t *)l_meta_item->value); + l_str = l_meta_item->length ? DAP_NEW_Z_SIZE(char, l_meta_item->length * 2 + 1) : NULL; + dap_bin2hex(l_str, l_meta_item->value, l_meta_item->length); + printf("%s\t%u\t%u\t%s\n", l_meta_item->key, l_meta_item->type, l_meta_item->length, l_str); + DAP_DELETE(l_str); break; } l_meta_list_item = l_meta_list_item->next; diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index 19026903b6ce517c04a435c744cb06bc802ccad1..0b39d4eb7bde8e6af5bbeec9aaf22674322845ee 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -76,7 +76,12 @@ if (CELLFRAME_MODULES MATCHES "srv-vpn") add_subdirectory(service/vpn) endif() +# Service eXchange +if (CELLFRAME_MODULES MATCHES "srv-xchange") + add_subdirectory(service/xchange) +endif() + # Unit tests if( BUILD_TESTS) add_subdirectory(test) -endif() \ No newline at end of file +endif() diff --git a/modules/chain/dap_chain_ledger.c b/modules/chain/dap_chain_ledger.c index 7c1a1f4f7139c5b60c903e0c48446e5f2eb093cc..af1201fd203562f82a93ddcfb694c3d8b6a625ef 100644 --- a/modules/chain/dap_chain_ledger.c +++ b/modules/chain/dap_chain_ledger.c @@ -57,6 +57,15 @@ #define LOG_TAG "dap_chain_ledger" +typedef struct dap_chain_ledger_verificator { + int subtype; // hash key + dap_chain_ledger_verificator_callback_t callback; + UT_hash_handle hh; +} dap_chain_ledger_verificator_t; + +static dap_chain_ledger_verificator_t *s_verificators; +static pthread_rwlock_t s_verificators_rwlock; + #define MAX_OUT_ITEMS 10 typedef struct dap_chain_ledger_token_emission_item { dap_chain_hash_fast_t datum_token_emission_hash; @@ -102,8 +111,15 @@ typedef struct dap_chain_ledger_tokenizer { typedef struct dap_chain_ledger_tx_bound { dap_chain_hash_fast_t tx_prev_hash_fast; dap_chain_datum_tx_t *tx_prev; - dap_chain_tx_in_t *tx_cur_in; - dap_chain_tx_out_t *tx_prev_out; + union { + dap_chain_tx_in_t *tx_cur_in; + dap_chain_tx_in_cond_t *tx_cur_in_cond; + } in; + union { + dap_chain_tx_out_t *tx_prev_out; + dap_chain_tx_out_ext_t *tx_prev_out_ext; + dap_chain_tx_out_cond_t *tx_prev_out_cond; + } out; dap_chain_ledger_tx_item_t *item_out; } dap_chain_ledger_tx_bound_t; @@ -159,6 +175,7 @@ static void s_treshold_txs_proc( dap_ledger_t * a_ledger); static size_t s_treshold_emissions_max = 1000; static size_t s_treshold_txs_max = 10000; + /** * Create dap_ledger_t structure */ @@ -228,6 +245,23 @@ int dap_chain_ledger_token_decl_add_check(dap_ledger_t * a_ledger, dap_chain_da return 0; } +/** + * @brief dap_chain_ledger_token_ticker_check + * @param a_ledger + * @param a_token_ticker + * @return + */ +int dap_chain_ledger_token_ticker_check(dap_ledger_t * a_ledger, const char *a_token_ticker) +{ + if ( !a_ledger){ + log_it(L_WARNING, "NULL ledger, can't find token ticker"); + return -2; + } + dap_chain_ledger_token_item_t *l_token_item; + HASH_FIND_STR(PVT(a_ledger)->tokens, a_token_ticker, l_token_item); + return (size_t)l_token_item; +} + /** * @brief dap_chain_ledger_token_add * @param a_token @@ -283,6 +317,7 @@ int dap_chain_ledger_token_add(dap_ledger_t * a_ledger, dap_chain_datum_token_t */ static void s_treshold_emissions_proc( dap_ledger_t * a_ledger) { + UNUSED(a_ledger); // TODO } @@ -292,6 +327,7 @@ static void s_treshold_emissions_proc( dap_ledger_t * a_ledger) */ static void s_treshold_txs_proc( dap_ledger_t * a_ledger) { + UNUSED(a_ledger); // TODO } @@ -635,9 +671,11 @@ 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 + 4. tx1.dap_chain_datum_tx_out.addr.data.key == tx2.dap_chain_datum_tx_sig.pkey for unconditional output && - 5. sum( find (tx2.input.tx_prev_hash).output[tx2.input_tx_prev_idx].value ) == sum (tx2.outputs.value) per token + 5. tx1.dap_chain_datum_tx_out.condition == verify_svc_type(tx2) for conditional ouput + && + 6. sum( find (tx2.input.tx_prev_hash).output[tx2.input_tx_prev_idx].value ) == sum (tx2.outputs.value) per token */ dap_ledger_private_t *l_ledger_priv = PVT(a_ledger); @@ -669,17 +707,35 @@ int dap_chain_ledger_tx_cache_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t // ---------------------------------------------------------------- // find all 'in' items in current transaction dap_list_t *l_list_in = dap_chain_datum_tx_items_get((dap_chain_datum_tx_t*) a_tx, TX_ITEM_TYPE_IN, - &l_prev_tx_count); - //log_it(L_DEBUG,"Tx check: %d inputs",l_prev_tx_count); + &l_prev_tx_count); + // find all conditional 'in' items in current transaction + dap_list_t *l_list_tmp = dap_chain_datum_tx_items_get((dap_chain_datum_tx_t *)a_tx, TX_ITEM_TYPE_IN_COND, + &l_prev_tx_count); + if (l_list_tmp) { + // add conditional input to common list + l_list_in = dap_list_append(l_list_in, l_list_tmp->data); + dap_list_free(l_list_tmp); + } + l_list_tmp = l_list_in; dap_chain_ledger_tx_bound_t *bound_item; int l_list_tmp_num = 0; // find all previous transactions for (dap_list_t *l_list_tmp = l_list_in; l_list_tmp; l_list_tmp = dap_list_next(l_list_tmp), l_list_tmp_num++) { bound_item = DAP_NEW_Z(dap_chain_ledger_tx_bound_t); - dap_chain_tx_in_t *l_tx_in = (dap_chain_tx_in_t*) l_list_tmp->data; + dap_chain_tx_in_t *l_tx_in; + dap_chain_tx_in_cond_t *l_tx_in_cond; + dap_chain_hash_fast_t l_tx_prev_hash; + uint8_t l_cond_type = *(uint8_t *)l_list_tmp->data; // one of the previous transaction - dap_chain_hash_fast_t l_tx_prev_hash = l_tx_in->header.tx_prev_hash; - bound_item->tx_cur_in = l_tx_in; + if (l_cond_type == TX_ITEM_TYPE_IN) { + l_tx_in = (dap_chain_tx_in_t *)l_list_tmp->data; + l_tx_prev_hash = l_tx_in->header.tx_prev_hash; + bound_item->in.tx_cur_in = l_tx_in; + } else { // TX_ITEM_TYPE_IN_COND + l_tx_in_cond = (dap_chain_tx_in_cond_t *)l_list_tmp->data; + l_tx_prev_hash = l_tx_in_cond->header.tx_prev_hash; + bound_item->in.tx_cur_in_cond = l_tx_in_cond; + } memcpy(&bound_item->tx_prev_hash_fast, &l_tx_prev_hash, sizeof(dap_chain_hash_fast_t)); bool l_is_blank = dap_hash_fast_is_blank(&l_tx_prev_hash); @@ -722,14 +778,14 @@ int dap_chain_ledger_tx_cache_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t break; } //log_it(L_INFO,"Previous transaction was found for hash %s",l_tx_prev_hash_str); - bound_item->tx_prev = l_tx_prev; // 1. Verify signature in previous transaction int l_res_sign = dap_chain_datum_tx_verify_sign(l_tx_prev); // 2. Check if out in previous transaction has spent - if (dap_chain_ledger_item_is_used_out(l_item_out, l_tx_in->header.tx_out_prev_idx)) { + int l_idx = (l_cond_type == TX_ITEM_TYPE_IN) ? l_tx_in->header.tx_out_prev_idx : l_tx_in_cond->header.tx_out_prev_idx; + if (dap_chain_ledger_item_is_used_out(l_item_out, l_idx)) { l_err_num = -6; break; } @@ -744,41 +800,87 @@ int dap_chain_ledger_tx_cache_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t break; } DAP_DELETE(l_hash_prev); - + uint64_t l_value; // Get list of all 'out' items from previous transaction - dap_list_t *l_list_prev_out = dap_chain_datum_tx_items_get(l_tx_prev, TX_ITEM_TYPE_OUT, NULL); + dap_list_t *l_list_prev_out = dap_chain_datum_tx_items_get(l_tx_prev, TX_ITEM_TYPE_OUT_ALL, NULL); // Get one 'out' item in previous transaction bound with current 'in' item - dap_chain_tx_out_t *l_tx_prev_out = dap_list_nth_data(l_list_prev_out, l_tx_in->header.tx_out_prev_idx); + void *l_tx_prev_out = dap_list_nth_data(l_list_prev_out, l_idx); + dap_list_free(l_list_prev_out); if(!l_tx_prev_out) { l_err_num = -8; break; } - dap_list_free(l_list_prev_out); - bound_item->tx_prev_out = l_tx_prev_out; - - // calculate hash of public key in current transaction - dap_chain_hash_fast_t l_hash_pkey; - { - // Get sign item - 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); - // Get sign from sign item - dap_sign_t *l_sign = dap_chain_datum_tx_item_sign_get_sig((dap_chain_tx_sig_t*) l_tx_sig); - // Get public key from sign - size_t l_pkey_ser_size = 0; - const uint8_t *l_pkey_ser = dap_sign_get_pkey(l_sign, &l_pkey_ser_size); - // calculate hash from public key - dap_hash_fast(l_pkey_ser, l_pkey_ser_size, &l_hash_pkey); + if (l_cond_type == TX_ITEM_TYPE_IN) { + dap_chain_tx_item_type_t l_type = *(uint8_t *)l_tx_prev_out; + if (l_type == TX_ITEM_TYPE_OUT) { + bound_item->out.tx_prev_out = l_tx_prev_out; + } else if (l_type == TX_ITEM_TYPE_OUT_EXT) { + bound_item->out.tx_prev_out_ext = l_tx_prev_out; + } else { + l_err_num = -8; + break; + } + // calculate hash of public key in current transaction + dap_chain_hash_fast_t l_hash_pkey; + { + // Get sign item + 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); + // Get sign from sign item + dap_sign_t *l_sign = dap_chain_datum_tx_item_sign_get_sig((dap_chain_tx_sig_t*) l_tx_sig); + // Get public key from sign + size_t l_pkey_ser_size = 0; + const uint8_t *l_pkey_ser = dap_sign_get_pkey(l_sign, &l_pkey_ser_size); + // calculate hash from public key + dap_hash_fast(l_pkey_ser, l_pkey_ser_size, &l_hash_pkey); + // hash of public key in 'out' item of previous transaction + uint8_t *l_prev_out_addr_key = (l_type == TX_ITEM_TYPE_OUT) ? + bound_item->out.tx_prev_out->addr.data.key : + bound_item->out.tx_prev_out_ext->addr.data.key; + // 4. compare public key hashes in the signature of the current transaction and in the 'out' item of the previous transaction + if(memcmp(&l_hash_pkey, l_prev_out_addr_key, sizeof(dap_chain_hash_fast_t))) { + l_err_num = -9; + break; + } + } + if (l_type == TX_ITEM_TYPE_OUT) { + l_value = bound_item->out.tx_prev_out->header.value; + } else { + l_value = bound_item->out.tx_prev_out_ext->header.value; + l_token = bound_item->out.tx_prev_out_ext->token; + } + } else { // TX_ITEM_TYPE_IN_COND + if(*(uint8_t *)l_tx_prev_out != TX_ITEM_TYPE_OUT_COND) { + 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; + } + bound_item->out.tx_prev_out_cond = l_tx_prev_out_cond; + // calculate sum of values from previous transactions + l_value = l_tx_prev_out_cond->header.value; + l_token = NULL; } - // hash of public key in 'out' item of previous transaction - uint8_t *l_prev_out_addr_key = l_tx_prev_out->addr.data.key; - - // 4. compare public key hashes in the signature of the current transaction and in the 'out' item of the previous transaction - if(memcmp(&l_hash_pkey, l_prev_out_addr_key, sizeof(dap_chain_hash_fast_t))) { - l_err_num = -9; + if (!l_token || !*l_token) { + l_token = l_item_out->token_tiker; + } + if (!*l_token) { + log_it(L_WARNING, "No token ticker found in previous transaction"); + l_err_num = -15; break; } - l_token = l_item_out->token_tiker; HASH_FIND_STR(l_values_from_prev_tx, l_token, l_value_cur); if (!l_value_cur) { l_value_cur = DAP_NEW_Z(dap_chain_ledger_tokenizer_t); @@ -786,9 +888,7 @@ int dap_chain_ledger_tx_cache_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t HASH_ADD_STR(l_values_from_prev_tx, token_ticker, l_value_cur); } // calculate sum of values from previous transactions per each token - l_value_cur->sum += l_tx_prev_out->header.value; - l_value_cur = NULL; - + l_value_cur->sum += l_value; l_list_bound_items = dap_list_append(l_list_bound_items, bound_item); } if (l_list_in) @@ -804,46 +904,76 @@ int dap_chain_ledger_tx_cache_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t return l_err_num; } - // 5. Compare sum of values in 'out' items in the current transaction and in the previous transactions + // 6. Compare sum of values in 'out' items in the current transaction and in the previous transactions // Calculate the sum of values in 'out' items from the current transaction - l_value_cur = DAP_NEW_Z(dap_chain_ledger_tokenizer_t); - strcpy(l_value_cur->token_ticker, l_token); - HASH_ADD_STR(l_values_from_cur_tx, token_ticker, l_value_cur); + bool l_multichannel = false; + if (HASH_COUNT(l_values_from_prev_tx) > 1) { + l_multichannel = true; + } else { + l_value_cur = DAP_NEW_Z(dap_chain_ledger_tokenizer_t); + strcpy(l_value_cur->token_ticker, l_token); + HASH_ADD_STR(l_values_from_cur_tx, token_ticker, l_value_cur); + } dap_list_t *l_list_tx_out = NULL; bool emission_flag = !l_is_first_transaction || (l_is_first_transaction && l_ledger_priv->check_token_emission); // find 'out' items - dap_list_t *l_list_out = dap_chain_datum_tx_items_get((dap_chain_datum_tx_t*) a_tx, TX_ITEM_TYPE_OUT, NULL); - dap_list_t *l_list_tmp = dap_chain_datum_tx_items_get((dap_chain_datum_tx_t*) a_tx, TX_ITEM_TYPE_OUT_COND, NULL); - // accumalate value ​from all 'out' & 'out_cond' transactions - if (l_list_tmp) { - l_list_out = dap_list_append(l_list_out, l_list_tmp->data); - } + dap_list_t *l_list_out = dap_chain_datum_tx_items_get((dap_chain_datum_tx_t*) a_tx, TX_ITEM_TYPE_OUT_ALL, NULL); + uint64_t l_value; for (l_list_tmp = l_list_out; l_list_tmp; l_list_tmp = dap_list_next(l_list_tmp)) { - if (*(uint8_t *)l_list_tmp->data == TX_ITEM_TYPE_OUT) { - dap_chain_tx_out_t *l_tx_out = (dap_chain_tx_out_t*) l_list_tmp->data; + dap_chain_tx_item_type_t l_type = *(uint8_t *)l_list_tmp->data; + if (l_type == TX_ITEM_TYPE_OUT) + { + dap_chain_tx_out_t *l_tx_out = (dap_chain_tx_out_t *)l_list_tmp->data; + if (l_multichannel) { // token ticker is mandatory for multichannel transactions + l_err_num = -16; + break; + } if (emission_flag) { - l_value_cur->sum += l_tx_out->header.value; + l_value = l_tx_out->header.value; } l_list_tx_out = dap_list_append(l_list_tx_out, l_tx_out); - } else { - dap_chain_tx_out_cond_t *l_tx_out = (dap_chain_tx_out_cond_t*) l_list_tmp->data; + } else if (l_type == TX_ITEM_TYPE_OUT_EXT) { + dap_chain_tx_out_ext_t *l_tx_out = (dap_chain_tx_out_ext_t *)l_list_tmp->data; + if (!l_multichannel) { // token ticker is depricated for single-channel transactions + l_err_num = -16; + break; + } if (emission_flag) { - l_value_cur->sum += l_tx_out->header.value; + l_value = l_tx_out->header.value; + l_token = l_tx_out->token; } l_list_tx_out = dap_list_append(l_list_tx_out, l_tx_out); + } else if (l_type == TX_ITEM_TYPE_OUT_COND) { + dap_chain_tx_out_cond_t *l_tx_out = (dap_chain_tx_out_cond_t *)l_list_tmp->data; + if (emission_flag) { + l_value = l_tx_out->header.value; + } + if (l_multichannel) { // out_cond have no field .token + log_it(L_WARNING, "No conditional output support for multichannel transaction"); + l_err_num = -18; + break; + } + l_list_tx_out = dap_list_append(l_list_tx_out, l_tx_out); + } + if (l_multichannel) { + HASH_FIND_STR(l_values_from_cur_tx, l_token, l_value_cur); + if (!l_value_cur) { + l_value_cur = DAP_NEW_Z(dap_chain_ledger_tokenizer_t); + strcpy(l_value_cur->token_ticker, l_token); + HASH_ADD_STR(l_values_from_cur_tx, token_ticker, l_value_cur); + } } + l_value_cur->sum += l_value; } if ( l_list_out ) dap_list_free(l_list_out); - l_value_cur = NULL; // Additional check whether the transaction is first - while (l_is_first_transaction) { + while (l_is_first_transaction && !l_err_num) { if (l_ledger_priv->check_token_emission) { // Check the token emission dap_chain_datum_token_emission_t * l_token_emission = dap_chain_ledger_token_emission_find(a_ledger, l_token, l_emission_hash); if (l_token_emission) { - HASH_FIND_STR(l_values_from_cur_tx, l_token, l_value_cur); - if (!l_value_cur || l_token_emission->hdr.value != l_value_cur->sum) { + if (l_token_emission->hdr.value != l_value_cur->sum) { l_err_num = -10; } l_value_cur = NULL; @@ -854,8 +984,7 @@ int dap_chain_ledger_tx_cache_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t } break; } - - while (!l_is_first_transaction) { + while (!l_is_first_transaction && !l_err_num) { HASH_ITER(hh, l_values_from_prev_tx, l_value_cur, l_tmp) { HASH_FIND_STR(l_values_from_cur_tx, l_value_cur->token_ticker, l_res); if (!l_res || l_res->sum != l_value_cur->sum) { @@ -940,7 +1069,7 @@ int dap_chain_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx) //log_it ( L_INFO, "dap_chain_ledger_tx_add() check passed for tx %s",l_tx_hash_str); - char * l_token_ticker = NULL; + char *l_token_ticker = NULL, *l_token_ticker_old = NULL; dap_chain_ledger_tx_item_t *l_item_tmp = NULL; pthread_rwlock_wrlock(&l_ledger_priv->ledger_rwlock); HASH_FIND(hh, l_ledger_priv->ledger_items, l_tx_hash, sizeof(dap_chain_hash_fast_t), l_item_tmp); // tx_hash already in the hash? @@ -959,34 +1088,57 @@ int dap_chain_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx) if (ret == -1) { goto FIN; } + bool l_multichannel = false; // Mark 'out' items in cache if they were used & delete previous transactions from cache if it need // find all bound pairs 'in' and 'out' dap_list_t *l_list_tmp = l_list_bound_items; -// int l_list_tmp_num = 0; + + // Update balance: deducts while(l_list_tmp) { dap_chain_ledger_tx_bound_t *bound_item = l_list_tmp->data; - dap_chain_tx_in_t *l_tx_in = bound_item->tx_cur_in; + void *l_item_in = *(void **)&bound_item->in; + dap_chain_tx_item_type_t l_type = *(uint8_t *)l_item_in; dap_chain_ledger_tx_item_t *l_prev_item_out = bound_item->item_out; - if ( l_token_ticker == NULL) - l_token_ticker = dap_strdup (l_prev_item_out->token_tiker); - - // Update balance: deducts - dap_ledger_wallet_balance_t *wallet_balance = NULL; - char *l_addr_str = dap_chain_addr_to_str(&bound_item->tx_prev_out->addr); - char *l_wallet_balance_key = dap_strjoin(" ", l_addr_str, l_token_ticker, (char*)NULL); - HASH_FIND_STR(PVT(a_ledger)->balance_accounts, l_wallet_balance_key, wallet_balance); - if (wallet_balance) { - //log_it(L_DEBUG,"SPEND %lu from addr: %s", bound_item->tx_prev_out->header.value, l_wallet_balance_key); - wallet_balance->balance -= bound_item->tx_prev_out->header.value; - } else { - log_it(L_ERROR,"!!! Attempt to SPEND from some non-existent balance !!!: %s %s", l_addr_str, l_token_ticker); + if (*l_prev_item_out->token_tiker) { + l_token_ticker = l_prev_item_out->token_tiker; + } else { // Previous multichannel transaction + l_token_ticker = bound_item->out.tx_prev_out_ext->token; + } + if (!l_multichannel && l_token_ticker_old && strcmp(l_token_ticker, l_token_ticker_old)) { + l_multichannel = true; + } + l_token_ticker_old = l_token_ticker; + dap_chain_hash_fast_t *l_tx_prev_hash; + if (l_type == TX_ITEM_TYPE_IN) { + dap_chain_tx_in_t *l_tx_in = bound_item->in.tx_cur_in; + dap_ledger_wallet_balance_t *wallet_balance = NULL; + void *l_item_out = *(void **)&bound_item->out; + dap_chain_tx_item_type_t l_out_type = *(uint8_t *)l_item_out; + dap_chain_addr_t *l_addr = (l_out_type == TX_ITEM_TYPE_OUT) ? + &bound_item->out.tx_prev_out->addr : + &bound_item->out.tx_prev_out_ext->addr; + char *l_addr_str = dap_chain_addr_to_str(l_addr); + char *l_wallet_balance_key = dap_strjoin(" ", l_addr_str, l_token_ticker, (char*)NULL); + HASH_FIND_STR(PVT(a_ledger)->balance_accounts, l_wallet_balance_key, wallet_balance); + if (wallet_balance) { + uint64_t l_value = (l_out_type == TX_ITEM_TYPE_OUT) ? + bound_item->out.tx_prev_out->header.value : + bound_item->out.tx_prev_out_ext->header.value; + //log_it(L_DEBUG,"SPEND %lu from addr: %s", l_value, l_wallet_balance_key); + wallet_balance->balance -= l_value; + } else { + log_it(L_ERROR,"!!! Attempt to SPEND from some non-existent balance !!!: %s %s", l_addr_str, l_token_ticker); + } + DAP_DELETE(l_addr_str); + DAP_DELETE(l_wallet_balance_key); + /// Mark 'out' item in cache because it used + l_tx_prev_hash = &(l_prev_item_out->tx_hash_spent_fast[l_tx_in->header.tx_out_prev_idx]); + } else { // TX_ITEM_TYPE_IN_COND + // all balance deducts performed with previous conditional transaction + dap_chain_tx_in_cond_t *l_tx_in_cond = bound_item->in.tx_cur_in_cond; + /// Mark 'out' item in cache because it used + l_tx_prev_hash = &(l_prev_item_out->tx_hash_spent_fast[l_tx_in_cond->header.tx_out_prev_idx]); } - DAP_DELETE(l_addr_str); - DAP_DELETE(l_wallet_balance_key); - - /// Mark 'out' item in cache because it used - dap_chain_hash_fast_t *l_tx_prev_hash = - &(l_prev_item_out->tx_hash_spent_fast[l_tx_in->header.tx_out_prev_idx]); memcpy(l_tx_prev_hash, l_tx_hash, sizeof(dap_chain_hash_fast_t)); // add a used output l_prev_item_out->n_outs_used++; @@ -1034,30 +1186,47 @@ int dap_chain_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx) if (l_base_tx_count >=1 && l_base_tx_list){ dap_chain_tx_token_t * l_tx_token =(dap_chain_tx_token_t *) l_base_tx_list->data; if ( l_tx_token ) - l_token_ticker = dap_strdup( l_tx_token->header.ticker); + l_token_ticker = l_tx_token->header.ticker; } } //Update balance : raise for (dap_list_t *l_tx_out = l_list_tx_out; l_tx_out; l_tx_out = dap_list_next(l_tx_out)) { - dap_chain_tx_out_t *l_out_item = l_tx_out->data; + dap_chain_tx_item_type_t l_type = *(uint8_t *)l_tx_out->data; + if (l_type == TX_ITEM_TYPE_OUT_COND) { + continue; // balance raise will be with next conditional transaction + } + dap_chain_tx_out_t *l_out_item; + dap_chain_tx_out_ext_t *l_out_item_ext; + if (l_type == TX_ITEM_TYPE_OUT) { + l_out_item = l_tx_out->data; + } else { + l_out_item_ext = l_tx_out->data; + } if (l_out_item && l_token_ticker) { - char *l_addr_str = dap_chain_addr_to_str(&l_out_item->addr); + dap_chain_addr_t *l_addr = (l_type == TX_ITEM_TYPE_OUT) ? + &l_out_item->addr : + &l_out_item_ext->addr; + char *l_addr_str = dap_chain_addr_to_str(l_addr); //log_it (L_DEBUG, "Check unspent %.03Lf %s for addr %s", // (long double) l_out_item->header.value/ 1000000000000.0L, // l_token_ticker, l_addr_str); dap_ledger_wallet_balance_t *wallet_balance = NULL; + if (l_multichannel) { + l_token_ticker = l_out_item_ext->token; + } char *l_wallet_balance_key = dap_strjoin(" ", l_addr_str, l_token_ticker, (char*)NULL); - //log_it (L_DEBUG,"\t\tAddr: %s token: %s", l_addr_str, l_token_ticker); + uint64_t l_value = (l_type == TX_ITEM_TYPE_OUT) ? l_out_item->header.value : l_out_item_ext->header.value; + //log_it (L_DEBUG,"GOT %lu to addr: %s", l_value, l_wallet_balance_key); HASH_FIND_STR(PVT(a_ledger)->balance_accounts, l_wallet_balance_key, wallet_balance); if (wallet_balance) { //log_it(L_DEBUG, "Balance item is present in cache"); - wallet_balance->balance += l_out_item->header.value; + wallet_balance->balance += l_value; DAP_DELETE (l_wallet_balance_key); } else { wallet_balance = DAP_NEW_Z(dap_ledger_wallet_balance_t); wallet_balance->key = l_wallet_balance_key; - wallet_balance->balance += l_out_item->header.value; + wallet_balance->balance += l_value; dap_stpcpy(wallet_balance->token_ticker, l_token_ticker); //log_it(L_DEBUG,"!!! Create new balance item: %s %s", l_addr_str, l_token_ticker); HASH_ADD_KEYPTR(hh, PVT(a_ledger)->balance_accounts, wallet_balance->key, @@ -1098,6 +1267,7 @@ int dap_chain_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx) if( l_item_tmp->n_outs){ dap_list_t *l_tist_tmp = dap_chain_datum_tx_items_get(a_tx, TX_ITEM_TYPE_OUT, &l_item_tmp->n_outs); for (size_t i =0; i < (size_t) l_item_tmp->n_outs; i++){ + // TODO list conditional outputs dap_chain_tx_out_t * l_tx_out = l_tist_tmp->data; char * l_tx_out_addr_str = dap_chain_addr_to_str( &l_tx_out->addr ); //log_it(L_DEBUG,"Added tx out to %s",l_tx_out_addr_str ); @@ -1118,8 +1288,8 @@ int dap_chain_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx) dap_list_free(l_tokens_list); } } - if (l_token_ticker) - strncpy(l_item_tmp->token_tiker,l_token_ticker,sizeof (l_item_tmp->token_tiker)); + if (l_token_ticker && !l_multichannel) + strncpy(l_item_tmp->token_tiker, l_token_ticker, sizeof(l_item_tmp->token_tiker) - 1); memcpy(l_item_tmp->tx, a_tx, dap_chain_datum_tx_get_size(a_tx)); HASH_ADD(hh, l_ledger_priv->ledger_items, tx_hash_fast, sizeof(dap_chain_hash_fast_t), l_item_tmp); // tx_hash_fast: name of key field @@ -1128,9 +1298,6 @@ int dap_chain_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx) FIN: pthread_rwlock_tryrdlock (&l_ledger_priv->ledger_rwlock); pthread_rwlock_unlock(&l_ledger_priv->ledger_rwlock); - if (l_token_ticker) { - DAP_DELETE(l_token_ticker); - } DAP_DELETE(l_tx_hash); return ret; } @@ -1258,7 +1425,7 @@ uint64_t dap_chain_ledger_calc_balance(dap_ledger_t *a_ledger, const dap_chain_a const char *a_token_ticker) { uint64_t l_ret = 0; - dap_ledger_wallet_balance_t *l_balance_item = NULL ,* l_balance_item_tmp = NULL; + dap_ledger_wallet_balance_t *l_balance_item = NULL;// ,* l_balance_item_tmp = NULL; char *l_addr = dap_chain_addr_to_str(a_addr); char *l_wallet_balance_key = dap_strjoin(" ", l_addr, a_token_ticker, (char*)NULL); @@ -1311,39 +1478,51 @@ uint64_t dap_chain_ledger_calc_balance_full(dap_ledger_t *a_ledger, const dap_ch { dap_chain_datum_tx_t *l_cur_tx = l_iter_current->tx; - // Check for token name - if(strcmp(a_token_ticker, l_iter_current->token_tiker) == 0) { - // dap_chain_hash_fast_t *l_cur_tx_hash = &l_iter_current->tx_hash_fast; - // int l_n_outs_used = l_iter_current->n_outs_used; // number of used 'out' items - - // Get 'out' items from transaction - int l_out_item_count = 0; - dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(l_cur_tx, TX_ITEM_TYPE_OUT, &l_out_item_count); - if(l_out_item_count >= MAX_OUT_ITEMS) { - log_it(L_ERROR, "Too many 'out' items=%d in transaction (max=%d)", l_out_item_count, MAX_OUT_ITEMS); - assert(l_out_item_count < MAX_OUT_ITEMS); + // dap_chain_hash_fast_t *l_cur_tx_hash = &l_iter_current->tx_hash_fast; + // int l_n_outs_used = l_iter_current->n_outs_used; // number of used 'out' items + + // Get 'out' items from transaction + int l_out_item_count = 0; + dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(l_cur_tx, TX_ITEM_TYPE_OUT_ALL, &l_out_item_count); + if(l_out_item_count >= MAX_OUT_ITEMS) { + log_it(L_ERROR, "Too many 'out' items=%d in transaction (max=%d)", l_out_item_count, MAX_OUT_ITEMS); + assert(l_out_item_count < MAX_OUT_ITEMS); + } + int l_out_idx_tmp = 0; + 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++) { + assert(l_list_tmp->data); + dap_chain_tx_item_type_t l_type = *(uint8_t *)l_list_tmp->data; + if (l_type == TX_ITEM_TYPE_OUT_COND) { + continue; } - dap_list_t *l_list_tmp = l_list_out_items; - int l_out_idx_tmp = 0; - while(l_list_tmp) { + if (l_type == TX_ITEM_TYPE_OUT) { const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_list_tmp->data; - - // if transaction has the out item with requested addr - if(l_tx_out - && !memcmp(a_addr, &l_tx_out->addr, sizeof(dap_chain_addr_t) - )) { - // if 'out' item not used & transaction is valid - if(!dap_chain_ledger_item_is_used_out(l_iter_current, l_out_idx_tmp) && - dap_chain_datum_tx_verify_sign(l_cur_tx)) - balance += l_tx_out->header.value; + // Check for token name + if (!strcmp(a_token_ticker, l_iter_current->token_tiker)) + { // if transaction has the out item with requested addr + if (!memcmp(a_addr, &l_tx_out->addr, sizeof(dap_chain_addr_t))) { + // if 'out' item not used & transaction is valid + if(!dap_chain_ledger_item_is_used_out(l_iter_current, l_out_idx_tmp) && + dap_chain_datum_tx_verify_sign(l_cur_tx)) + balance += l_tx_out->header.value; + } + } + } + if (l_type == TX_ITEM_TYPE_OUT_EXT) { + const dap_chain_tx_out_ext_t *l_tx_out = (const dap_chain_tx_out_ext_t*) l_list_tmp->data; + // Check for token name + if (!strcmp(a_token_ticker, l_tx_out->token)) + { // if transaction has the out item with requested addr + if (!memcmp(a_addr, &l_tx_out->addr, sizeof(dap_chain_addr_t))) { + // if 'out' item not used & transaction is valid + if(!dap_chain_ledger_item_is_used_out(l_iter_current, l_out_idx_tmp) && + dap_chain_datum_tx_verify_sign(l_cur_tx)) + balance += l_tx_out->header.value; + } } - // go to the next 'out' item in l_tx_tmp transaction - l_out_idx_tmp++; - - l_list_tmp = dap_list_next(l_list_tmp); } - dap_list_free(l_list_tmp); } + dap_list_free(l_list_out_items); } pthread_rwlock_unlock(&l_ledger_priv->ledger_rwlock); return balance; @@ -1370,9 +1549,8 @@ static dap_chain_ledger_tx_item_t* tx_item_find_by_addr(dap_ledger_t *a_ledger, HASH_ITER(hh, l_ledger_priv->ledger_items , l_iter_current, l_item_tmp) { // If a_token is setup we check if its not our token - miss it - if (a_token) - if ( dap_strcmp(l_iter_current->token_tiker,a_token) !=0 ) - continue; + if (a_token && *l_iter_current->token_tiker && dap_strcmp(l_iter_current->token_tiker, a_token)) + continue; // Now work with it dap_chain_datum_tx_t *l_tx = l_iter_current->tx; dap_chain_hash_fast_t *l_tx_hash = &l_iter_current->tx_hash_fast; @@ -1383,21 +1561,36 @@ static dap_chain_ledger_tx_item_t* tx_item_find_by_addr(dap_ledger_t *a_ledger, continue; } // Get 'out' items from transaction - dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT, NULL); - dap_list_t *l_list_tmp = l_list_out_items; - while(l_list_tmp) { - const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_list_tmp->data; - // if transaction has the out item with requested addr - if(l_tx_out && - !memcmp(a_addr, &l_tx_out->addr, sizeof(dap_chain_addr_t)) - ) { - memcpy(a_tx_first_hash, l_tx_hash, sizeof(dap_chain_hash_fast_t)); - is_tx_found = true; - break; + dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_OUT_ALL, NULL); + for(dap_list_t *l_list_tmp = l_list_out_items; l_list_tmp; l_list_tmp = dap_list_next(l_list_tmp)) { + assert(l_list_tmp->data); + dap_chain_tx_item_type_t l_type = *(uint8_t *)l_list_tmp->data; + if (l_type == TX_ITEM_TYPE_OUT_COND) { + continue; + } + if (l_type == TX_ITEM_TYPE_OUT) { + const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t *)l_list_tmp->data; + // if transaction has the out item with requested addr + if(!memcmp(a_addr, &l_tx_out->addr, sizeof(dap_chain_addr_t))) { + memcpy(a_tx_first_hash, l_tx_hash, sizeof(dap_chain_hash_fast_t)); + is_tx_found = true; + break; + } + } + if (l_type == TX_ITEM_TYPE_OUT_EXT) { + const dap_chain_tx_out_ext_t *l_tx_out_ext = (const dap_chain_tx_out_ext_t *)l_list_tmp->data; + // If a_token is setup we check if its not our token - miss it + if (a_token && dap_strcmp(l_tx_out_ext->token, a_token)) { + continue; + } // if transaction has the out item with requested addr + if(!memcmp(a_addr, &l_tx_out_ext->addr, sizeof(dap_chain_addr_t))) { + memcpy(a_tx_first_hash, l_tx_hash, sizeof(dap_chain_hash_fast_t)); + is_tx_found = true; + break; + } } - l_list_tmp = dap_list_next(l_list_tmp); } - dap_list_free(l_list_tmp); + dap_list_free(l_list_out_items); // already found transaction if(is_tx_found) break; @@ -1483,7 +1676,6 @@ dap_chain_datum_tx_t* dap_chain_ledger_tx_cache_find_out_cond(dap_ledger_t *a_le { if(!a_addr || !a_tx_first_hash) return NULL; - int l_ret = -1; 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); @@ -1557,3 +1749,87 @@ uint64_t dap_chain_ledger_tx_cache_get_out_cond_value(dap_ledger_t *a_ledger, da return l_ret_value; } +dap_list_t *dap_chain_ledger_get_list_tx_outs_with_val(dap_ledger_t *a_ledger, const char *a_token_ticker, const dap_chain_addr_t *a_addr_from, + uint64_t a_value_need, uint64_t *a_value_transfer) +{ + dap_list_t *l_list_used_out = NULL; // list of transaction with 'out' items + dap_chain_hash_fast_t l_tx_cur_hash = { 0 }; + uint64_t l_value_transfer = 0; + while(l_value_transfer < a_value_need) + { + // Get the transaction in the cache by the addr in out item + dap_chain_datum_tx_t *l_tx = dap_chain_ledger_tx_find_by_addr(a_ledger, a_token_ticker, a_addr_from, + &l_tx_cur_hash); + if(!l_tx) + break; + // Get all item from transaction by type + dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(l_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) { + continue; + } + uint64_t l_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 (!l_out->header.value || memcmp(a_addr_from, &l_out->addr, sizeof(dap_chain_addr_t))) { + continue; + } + l_value = 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 (!l_out_ext->header.value || memcmp(a_addr_from, &l_out_ext->addr, sizeof(dap_chain_addr_t)) || + strcmp((char *)a_token_ticker, l_out_ext->token)) { + continue; + } + l_value = l_out_ext->header.value; + } + // Check whether used 'out' items + if (!dap_chain_ledger_tx_hash_is_used_out_item (a_ledger, &l_tx_cur_hash, l_out_idx_tmp)) { + list_used_item_t *item = DAP_NEW(list_used_item_t); + memcpy(&item->tx_hash_fast, &l_tx_cur_hash, sizeof(dap_chain_hash_fast_t)); + item->num_idx_out = l_out_idx_tmp; + item->value = l_value; + 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) { + break; + } + } + } + dap_list_free(l_list_out_items); + } + + // nothing to tranfer (not enough funds) + if(!l_list_used_out || l_value_transfer < a_value_need) { + dap_list_free_full(l_list_used_out, free); + return NULL; + } + + if (a_value_transfer) { + *a_value_transfer = l_value_transfer; + } + return l_list_used_out; +} + +// Add new verificator callback with associated subtype. Returns 1 if callback replaced, overwise returns 0 +int dap_chain_ledger_verificator_add(dap_chain_tx_out_cond_subtype_t a_subtype, dap_chain_ledger_verificator_callback_t a_callback) +{ + dap_chain_ledger_verificator_t *l_new_verificator; + int l_tmp = (int)a_subtype; + HASH_FIND_INT(s_verificators, &l_tmp, l_new_verificator); + if (l_new_verificator) { + l_new_verificator->callback = a_callback; + return 1; + } + l_new_verificator = DAP_NEW(dap_chain_ledger_verificator_t); + l_new_verificator->subtype = (int)a_subtype; + l_new_verificator->callback = a_callback; + pthread_rwlock_wrlock(&s_verificators_rwlock); + HASH_ADD_INT(s_verificators, subtype, l_new_verificator); + pthread_rwlock_unlock(&s_verificators_rwlock); + return 0; +} diff --git a/modules/chain/include/dap_chain.h b/modules/chain/include/dap_chain.h index 2556d2e9b41b5aa857ad75b36f858a80a5f5e84a..96694458347514411f164876956119eb02eeb6a0 100644 --- a/modules/chain/include/dap_chain.h +++ b/modules/chain/include/dap_chain.h @@ -165,6 +165,3 @@ dap_chain_t * dap_chain_load_from_cfg(dap_ledger_t* a_ledger,const char * a_chai void dap_chain_delete(dap_chain_t * a_chain); void dap_chain_add_callback_notify(dap_chain_t * a_chain, dap_chain_callback_notify_t a_callback, void * a_arg); - - - diff --git a/modules/chain/include/dap_chain_ledger.h b/modules/chain/include/dap_chain_ledger.h index 3655a83d174ffaa9aa0993b17b4f4c1e5dcf3285..7639ead95a27511bd31451d6fa19825d26d2ec3b 100644 --- a/modules/chain/include/dap_chain_ledger.h +++ b/modules/chain/include/dap_chain_ledger.h @@ -41,6 +41,8 @@ typedef struct dap_ledger { void *_internal; } dap_ledger_t; +typedef bool (* dap_chain_ledger_verificator_callback_t)(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx); + // Checks the emission of the token, usualy on zero chain #define DAP_CHAIN_LEDGER_CHECK_TOKEN_EMISSION 0x0001 @@ -82,6 +84,13 @@ int dap_chain_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx); int dap_chain_ledger_tx_add_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx); +/** + * Check token ticker existance + * + */ + +int dap_chain_ledger_token_ticker_check(dap_ledger_t * a_ledger, const char *a_token_ticker); + /** * Add new token datum * @@ -172,3 +181,10 @@ dap_chain_datum_tx_t* dap_chain_ledger_tx_cache_find_out_cond(dap_ledger_t *a_le // 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, dap_chain_tx_out_cond_t **tx_out_cond); + +// Get the list of 'out' items from previous transactions with summary value >= than a_value_need +// Put this summary value to a_value_transfer +dap_list_t *dap_chain_ledger_get_list_tx_outs_with_val(dap_ledger_t *a_ledger, const char *a_token_ticker, const dap_chain_addr_t *a_addr_from, + uint64_t a_value_need, uint64_t *a_value_transfer); +// Add new verificator callback with associated subtype. Returns 1 if callback replaced, overwise returns 0 +int dap_chain_ledger_verificator_add(dap_chain_tx_out_cond_subtype_t a_subtype, dap_chain_ledger_verificator_callback_t a_callback); diff --git a/modules/channel/chain-net-srv/dap_stream_ch_chain_net_srv.c b/modules/channel/chain-net-srv/dap_stream_ch_chain_net_srv.c index c15223614f02453c2c76bdbd7a07bb908704a451..bccec32e887a1d84e3a497e29f8bdd222ea299ac 100644 --- a/modules/channel/chain-net-srv/dap_stream_ch_chain_net_srv.c +++ b/modules/channel/chain-net-srv/dap_stream_ch_chain_net_srv.c @@ -202,9 +202,9 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch , void* a_arg) } // Check cond output if it equesl or not to request - if ( l_tx_out_cond->subtype.srv_pay.header.srv_uid.uint64 != l_request->hdr.srv_uid.uint64 ){ + if ( l_tx_out_cond->subtype.srv_pay.srv_uid.uint64 != l_request->hdr.srv_uid.uint64 ){ log_it( L_WARNING, "Wrong service uid in request, tx expect to close its output with 0x%016lX", - l_tx_out_cond->subtype.srv_pay.header.srv_uid ); + l_tx_out_cond->subtype.srv_pay.srv_uid ); l_err.code = DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_RESPONSE_ERROR_CODE_TX_COND_WRONG_SRV_UID ; dap_stream_ch_pkt_write( a_ch, DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_RESPONSE_ERROR, &l_err, sizeof (l_err) ); if (l_srv->callback_response_error) @@ -244,7 +244,7 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch , void* a_arg) DL_FOREACH(l_srv->pricelist, l_price_tmp) { if (l_price_tmp->net->pub.id.uint64 == l_request->hdr.net_id.uint64 && dap_strcmp(l_price_tmp->token, l_ticker) == 0 - && l_price_tmp->units_uid.enm == l_tx_out_cond->subtype.srv_pay.header.unit.enm + && l_price_tmp->units_uid.enm == l_tx_out_cond->subtype.srv_pay.unit.enm )//&& (l_price_tmp->value_datoshi/l_price_tmp->units) < l_tx_out_cond->subtype.srv_pay.header.unit_price_max_datoshi) { l_price = l_price_tmp; @@ -386,7 +386,7 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch , void* a_arg) dap_sign_get_pkey_hash( l_receipt_sign, &l_pkey_hash); - if( memcmp ( l_pkey_hash.raw, l_tx_out_cond->subtype.srv_pay.header.pkey_hash.raw , sizeof(l_pkey_hash) ) != 0 ){ + if( memcmp ( l_pkey_hash.raw, l_tx_out_cond->subtype.srv_pay.pkey_hash.raw , sizeof(l_pkey_hash) ) != 0 ){ l_err.code = DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_RESPONSE_ERROR_CODE_RECEIPT_WRONG_PKEY_HASH ; dap_stream_ch_pkt_write( a_ch, DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_RESPONSE_ERROR, &l_err, sizeof (l_err) ); if (l_usage->service->callback_response_error) diff --git a/modules/common/dap_chain_datum_tx.c b/modules/common/dap_chain_datum_tx.c index 094961c22daa3b60b8e928a8850f759af2a79f0b..0bdb9260550bdd590f66badd8d3f5076660aa29e 100644 --- a/modules/common/dap_chain_datum_tx.c +++ b/modules/common/dap_chain_datum_tx.c @@ -99,6 +99,25 @@ int dap_chain_datum_tx_add_in_item(dap_chain_datum_tx_t **a_tx, dap_chain_hash_f return -1; } +/** + * Create 'in' items from list and insert to transaction + * + * return summary value from inserted items + */ +uint64_t dap_chain_datum_tx_add_in_item_list(dap_chain_datum_tx_t **a_tx, dap_list_t *a_list_used_out) +{ + dap_list_t *l_list_tmp = a_list_used_out; + uint64_t l_value_to_items = 0; // how many datoshi to transfer + while (l_list_tmp) { + list_used_item_t *item = l_list_tmp->data; + if (dap_chain_datum_tx_add_in_item(a_tx, &item->tx_hash_fast, item->num_idx_out) == 1) { + l_value_to_items += item->value; + } + l_list_tmp = dap_list_next(l_list_tmp); + } + return l_value_to_items; +} + /** * @brief dap_chain_datum_tx_add_in_cond_item @@ -122,6 +141,7 @@ int dap_chain_datum_tx_add_in_cond_item(dap_chain_datum_tx_t **a_tx, dap_chain_h return -1; } + /** * Create 'out' item and insert to transaction * @@ -138,6 +158,22 @@ int dap_chain_datum_tx_add_out_item(dap_chain_datum_tx_t **a_tx, const dap_chain return -1; } +/** + * Create 'out_ext' item and insert to transaction + * + * return 1 Ok, -1 Error + */ +int dap_chain_datum_tx_add_out_ext_item(dap_chain_datum_tx_t **a_tx, const dap_chain_addr_t *a_addr, uint64_t a_value, const char *a_token) +{ + dap_chain_tx_out_ext_t *l_tx_out = dap_chain_datum_tx_item_out_ext_create(a_addr, a_value, a_token); + if(l_tx_out) { + dap_chain_datum_tx_add_item(a_tx, (const uint8_t *)l_tx_out); + DAP_DELETE(l_tx_out); + return 1; + } + return -1; +} + /** * Create 'out_cond' item and insert to transaction * diff --git a/modules/common/dap_chain_datum_tx_items.c b/modules/common/dap_chain_datum_tx_items.c index d5a627ba84096b0665babc2cc05f70601d055283..6c44465b08399af133461ab20a4e36ea8c868ebf 100644 --- a/modules/common/dap_chain_datum_tx_items.c +++ b/modules/common/dap_chain_datum_tx_items.c @@ -46,6 +46,7 @@ static size_t dap_chain_tx_in_get_size(const dap_chain_tx_in_t *a_item) static size_t dap_chain_tx_in_cond_get_size(const dap_chain_tx_in_cond_t *a_item) { + UNUSED(a_item); size_t size = sizeof(dap_chain_tx_in_cond_t); return size; } @@ -57,14 +58,16 @@ static size_t dap_chain_tx_out_get_size(const dap_chain_tx_out_t *a_item) return size; } +static size_t dap_chain_tx_out_ext_get_size(const dap_chain_tx_out_ext_t *a_item) +{ + (void) a_item; + size_t size = sizeof(dap_chain_tx_out_ext_t); + return size; +} + static size_t dap_chain_tx_out_cond_get_size(const dap_chain_tx_out_cond_t *a_item) { - switch (a_item->header.subtype) { - case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY: - return sizeof (a_item->header) + sizeof (a_item->subtype.srv_pay.header) + a_item->subtype.srv_pay.header.params_size; - default: - return 0; - } + return sizeof(dap_chain_tx_out_cond_t) + a_item->params_size; } static size_t dap_chain_tx_pkey_get_size(const dap_chain_tx_pkey_t *a_item) @@ -114,6 +117,9 @@ size_t dap_chain_datum_item_tx_get_size(const uint8_t *a_item) case TX_ITEM_TYPE_OUT: // Transaction outputs size = dap_chain_tx_out_get_size((const dap_chain_tx_out_t*) a_item); break; + case TX_ITEM_TYPE_OUT_EXT: + size = dap_chain_tx_out_ext_get_size((const dap_chain_tx_out_ext_t*) a_item); + break; case TX_ITEM_TYPE_RECEIPT: // Receipt size = dap_chain_datum_tx_receipt_get_size((const dap_chain_datum_tx_receipt_t*) a_item); case TX_ITEM_TYPE_IN_COND: // Transaction inputs with condition @@ -186,7 +192,7 @@ dap_chain_tx_in_cond_t* dap_chain_datum_tx_item_in_cond_create(dap_chain_hash_fa if(!a_tx_prev_hash ) return NULL; dap_chain_tx_in_cond_t *l_item = DAP_NEW_Z(dap_chain_tx_in_cond_t); - l_item->header.type = TX_ITEM_TYPE_IN; + l_item->header.type = TX_ITEM_TYPE_IN_COND; l_item->header.receipt_idx = a_receipt_idx; l_item->header.tx_out_prev_idx = a_tx_out_prev_idx; memcpy(&l_item->header.tx_prev_hash, a_tx_prev_hash,sizeof(l_item->header.tx_prev_hash) ); @@ -209,6 +215,18 @@ dap_chain_tx_out_t* dap_chain_datum_tx_item_out_create(const dap_chain_addr_t *a return l_item; } +dap_chain_tx_out_ext_t* dap_chain_datum_tx_item_out_ext_create(const dap_chain_addr_t *a_addr, uint64_t a_value, const char *a_token) +{ + if (!a_addr || !a_token) + return NULL; + dap_chain_tx_out_ext_t *l_item = DAP_NEW_Z(dap_chain_tx_out_ext_t); + l_item->header.type = TX_ITEM_TYPE_OUT_EXT; + l_item->header.value = a_value; + memcpy(&l_item->addr, a_addr, sizeof(dap_chain_addr_t)); + strcpy(l_item->token, a_token); + return l_item; +} + /** * Create item dap_chain_tx_out_cond_t * @@ -224,21 +242,41 @@ dap_chain_tx_out_cond_t* dap_chain_datum_tx_item_out_cond_create_srv_pay(dap_enc uint8_t *l_pub_key = dap_enc_key_serealize_pub_key(a_key, &l_pub_key_size); - dap_chain_tx_out_cond_t *l_item = DAP_NEW_Z_SIZE(dap_chain_tx_out_cond_t, - sizeof(l_item->header)+sizeof (l_item->subtype.srv_pay.header) + 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_PAY; // By default creatre cond for service pay. Rework with smth more flexible - l_item->subtype.srv_pay.header.srv_uid = a_srv_uid; - l_item->subtype.srv_pay.header.params_size = (uint32_t) a_params_size; - l_item->subtype.srv_pay.header.unit = a_unit; - l_item->subtype.srv_pay.header.unit_price_max_datoshi = a_value_max_per_unit; - dap_hash_fast( l_pub_key, l_pub_key_size, & l_item->subtype.srv_pay.header.pkey_hash); - memcpy(l_item->subtype.srv_pay.params, a_params, a_params_size); + l_item->subtype.srv_pay.srv_uid = a_srv_uid; + l_item->subtype.srv_pay.unit = a_unit; + l_item->subtype.srv_pay.unit_price_max_datoshi = a_value_max_per_unit; + dap_hash_fast( l_pub_key, l_pub_key_size, & l_item->subtype.srv_pay.pkey_hash); + l_item->params_size = (uint32_t)a_params_size; + memcpy(l_item->params, a_params, a_params_size); return l_item; } +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) +{ + if (!a_token) { + return NULL; + } + 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_XCHANGE; + l_item->subtype.srv_xchange.srv_uid = a_srv_uid; + l_item->subtype.srv_xchange.net_id = a_net_id; + strcpy(l_item->subtype.srv_xchange.token, a_token); + 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 * @@ -298,7 +336,11 @@ uint8_t* dap_chain_datum_tx_item_get( dap_chain_datum_tx_t *a_tx, int *a_item_id // check index if(!a_item_idx_start || l_item_idx >= *a_item_idx_start) { // check type - if(a_type == TX_ITEM_TYPE_ANY || a_type == dap_chain_datum_tx_item_get_type(l_item)) { + dap_chain_tx_item_type_t l_type = dap_chain_datum_tx_item_get_type(l_item); + if (a_type == TX_ITEM_TYPE_ANY || a_type == l_type || + (a_type == TX_ITEM_TYPE_OUT_ALL && l_type == TX_ITEM_TYPE_OUT) || + (a_type == TX_ITEM_TYPE_OUT_ALL && l_type == TX_ITEM_TYPE_OUT_COND) || + (a_type == TX_ITEM_TYPE_OUT_ALL && l_type == TX_ITEM_TYPE_OUT_EXT)) { if(a_item_idx_start) *a_item_idx_start = l_item_idx; if(a_item_out_size) @@ -338,3 +380,22 @@ dap_list_t* dap_chain_datum_tx_items_get(dap_chain_datum_tx_t *a_tx, dap_chain_t *a_item_count = l_items_count; return items_list; } + +dap_chain_tx_out_cond_t *dap_chain_datum_tx_out_cond_get(dap_chain_datum_tx_t *a_tx, int *a_out_num) +{ + dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get(a_tx, TX_ITEM_TYPE_OUT_ALL, NULL); + int l_prev_cond_idx = l_list_out_items ? 0 : -1; + dap_chain_tx_out_cond_t *l_res = NULL; + for (dap_list_t *l_list_tmp = l_list_out_items; l_list_tmp; l_list_tmp = dap_list_next(l_list_tmp), l_prev_cond_idx++) { + if (*(uint8_t *)l_list_tmp->data == TX_ITEM_TYPE_OUT_COND) { + l_res = l_list_tmp->data; + break; + } + } + dap_list_free(l_list_out_items); + if (a_out_num) { + *a_out_num = l_prev_cond_idx; + } + return l_res; +} + diff --git a/modules/common/dap_chain_datum_tx_out_cond.c b/modules/common/dap_chain_datum_tx_out_cond.c deleted file mode 100644 index 543fd32bd209bf7d86865234846d7400347139fe..0000000000000000000000000000000000000000 --- a/modules/common/dap_chain_datum_tx_out_cond.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Authors: - * Dmitriy A. Gearasimov <gerasimov.dmitriy@demlabs.net> - * Alexander Lysikov <alexander.lysikov@demlabs.net> - * DeM Labs Inc. https://demlabs.net - * DeM Labs Open source community https://github.com/demlabsinc - * Copyright (c) 2017-2019 - * 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 <stdint.h> -#include "dap_common.h" -#include "dap_chain_common.h" -#include "dap_chain_datum_tx_out_cond.h" - -uint8_t* dap_chain_datum_tx_out_cond_item_get_params(dap_chain_tx_out_cond_t *a_tx_out_cond, size_t *a_cond_size_out) -{ - if(a_tx_out_cond) { - switch (a_tx_out_cond->header.subtype ) { - case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY:{ - if(a_cond_size_out) - *a_cond_size_out = a_tx_out_cond->subtype.srv_pay.header.params_size; - return a_tx_out_cond->subtype.srv_pay.params; - } - default: return NULL; - } - } - return NULL; -} - diff --git a/modules/common/include/dap_chain_common.h b/modules/common/include/dap_chain_common.h index c2b2d7651ce46a6428a936e82765a974ba73c448..a4a11b7d27703246ffc778bfcb9940b75f291938 100644 --- a/modules/common/include/dap_chain_common.h +++ b/modules/common/include/dap_chain_common.h @@ -173,6 +173,7 @@ typedef union { typedef enum dap_chain_tx_item_type { TX_ITEM_TYPE_IN = 0x00, /// @brief Transaction: inputs TX_ITEM_TYPE_OUT = 0x10, /// @brief Transaction: outputs + TX_ITEM_TYPE_OUT_EXT = 0x11, TX_ITEM_TYPE_PKEY = 0x20, TX_ITEM_TYPE_SIG = 0x30, TX_ITEM_TYPE_TOKEN = 0x40, @@ -181,7 +182,8 @@ typedef enum dap_chain_tx_item_type { TX_ITEM_TYPE_OUT_COND = 0x60, /// @brief Transaction: conditon outputs TX_ITEM_TYPE_RECEIPT = 0x70, - TX_ITEM_TYPE_ANY = 0xff, + TX_ITEM_TYPE_OUT_ALL = 0xfe, + TX_ITEM_TYPE_ANY = 0xff } dap_chain_tx_item_type_t; diff --git a/modules/common/include/dap_chain_datum_tx.h b/modules/common/include/dap_chain_datum_tx.h index ac384233cb7426d4c27109da166e7f7277bb5e38..54885c4863ecfe326112fc46141c54141ac66043 100644 --- a/modules/common/include/dap_chain_datum_tx.h +++ b/modules/common/include/dap_chain_datum_tx.h @@ -23,6 +23,7 @@ */ #pragma once +#include "dap_list.h" #include "dap_enc_key.h" #include "dap_chain_common.h" #include "dap_chain_datum.h" @@ -73,6 +74,12 @@ size_t dap_chain_datum_tx_get_size(dap_chain_datum_tx_t *a_tx); */ int dap_chain_datum_tx_add_item(dap_chain_datum_tx_t **a_tx, const uint8_t *a_item); +/** + * Create 'in' items from list and insert to transaction + * + * return summary value from inserted items + */ +uint64_t dap_chain_datum_tx_add_in_item_list(dap_chain_datum_tx_t **a_tx, dap_list_t *a_list_used_out); /** * Create 'in' item and insert to transaction @@ -99,6 +106,14 @@ int dap_chain_datum_tx_add_in_cond_item(dap_chain_datum_tx_t **a_tx, dap_chain_h */ int dap_chain_datum_tx_add_out_item(dap_chain_datum_tx_t **a_tx, const dap_chain_addr_t *a_addr, uint64_t a_value); +/** + * Create 'out'_ext item and insert to transaction + * + * return 1 Ok, -1 Error + */ +int dap_chain_datum_tx_add_out_ext_item(dap_chain_datum_tx_t **a_tx, const dap_chain_addr_t *a_addr, + uint64_t a_value, const char *a_token); + /** * Create 'out_cond' item and insert to transaction * diff --git a/modules/common/include/dap_chain_datum_tx_in.h b/modules/common/include/dap_chain_datum_tx_in.h index 7ce6fedac78789894eb416a53ef65b430f2fb69c..d52c76aac74bbb030412d864e881d6e1ead281b4 100644 --- a/modules/common/include/dap_chain_datum_tx_in.h +++ b/modules/common/include/dap_chain_datum_tx_in.h @@ -43,3 +43,11 @@ typedef struct dap_chain_tx_in{ // uint32_t seq_no; /// Sequence number, out of the header so could be changed during reorganization // uint8_t sig[]; /// @param sig @brief raw signatura dat } DAP_ALIGN_PACKED dap_chain_tx_in_t; + +typedef struct list_used_item { + dap_chain_hash_fast_t tx_hash_fast; + uint32_t num_idx_out; + uint8_t padding[4]; + uint64_t value; +//dap_chain_tx_out_t *tx_out; +} list_used_item_t; diff --git a/modules/common/include/dap_chain_datum_tx_items.h b/modules/common/include/dap_chain_datum_tx_items.h index a01026be4c5faac7dc38e05308176015af5641a7..7ef0ab207c26e21ea3c702865a6c4c59815528cb 100644 --- a/modules/common/include/dap_chain_datum_tx_items.h +++ b/modules/common/include/dap_chain_datum_tx_items.h @@ -34,6 +34,7 @@ #include "dap_chain_datum_tx.h" #include "dap_chain_datum_tx_in.h" #include "dap_chain_datum_tx_out.h" +#include "dap_chain_datum_tx_out_ext.h" #include "dap_chain_datum_tx_in_cond.h" #include "dap_chain_datum_tx_out_cond.h" #include "dap_chain_datum_tx_sig.h" @@ -79,6 +80,13 @@ dap_chain_tx_in_cond_t* dap_chain_datum_tx_item_in_cond_create(dap_chain_hash_fa */ dap_chain_tx_out_t* dap_chain_datum_tx_item_out_create(const dap_chain_addr_t *a_addr, uint64_t a_value); +/** + * Create item dap_chain_tx_out_ext_t + * + * return item, NULL Error + */ +dap_chain_tx_out_ext_t* dap_chain_datum_tx_item_out_ext_create(const dap_chain_addr_t *a_addr, uint64_t a_value, const char *a_token); + /** * Create item dap_chain_tx_out_cond_t * @@ -87,7 +95,14 @@ dap_chain_tx_out_t* dap_chain_datum_tx_item_out_create(const dap_chain_addr_t *a dap_chain_tx_out_cond_t* dap_chain_datum_tx_item_out_cond_create_srv_pay(dap_enc_key_t *a_key, dap_chain_net_srv_uid_t a_srv_uid, 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 + * + * 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_sig_t * @@ -116,3 +131,5 @@ uint8_t* dap_chain_datum_tx_item_get( dap_chain_datum_tx_t *a_tx, int *a_item_id // Get all item from transaction by type dap_list_t* dap_chain_datum_tx_items_get(dap_chain_datum_tx_t *a_tx, dap_chain_tx_item_type_t a_type, int *a_item_count); +// Get conditional out item with it's idx +dap_chain_tx_out_cond_t *dap_chain_datum_tx_out_cond_get(dap_chain_datum_tx_t *a_tx, int *a_out_num); diff --git a/modules/common/include/dap_chain_datum_tx_out_cond.h b/modules/common/include/dap_chain_datum_tx_out_cond.h index 188d5a124c7339b89eddc8490718f34c7a72684d..d5889678e3fffd92af325926d6567c9582fa6ac3 100644 --- a/modules/common/include/dap_chain_datum_tx_out_cond.h +++ b/modules/common/include/dap_chain_datum_tx_out_cond.h @@ -29,8 +29,11 @@ #include "dap_chain_common.h" #include "dap_chain_datum_tx.h" +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_t; -#define DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY 0x01 /** * @struct dap_chain_tx_out * @brief Transaction item out_cond @@ -40,32 +43,35 @@ typedef struct dap_chain_tx_out_cond { /// Transaction item type dap_chain_tx_item_type_t item_type :8; /// Condition subtype - uint8_t subtype; + dap_chain_tx_out_cond_subtype_t subtype : 8; /// Number of Datoshis ( DAP/10^9 ) to be reserver for service uint64_t value; /// When time expires this output could be used only by transaction owner dap_chain_time_t ts_expires; } header; union { + /// Structure with specific for service pay condition subtype struct { - /// Structure with specific for service pay condition subtype - struct { - /// Public key hash that could use this conditioned outout - dap_chain_hash_fast_t pkey_hash; - /// Service uid that only could be used for this outout - dap_chain_net_srv_uid_t srv_uid; - /// Price unit thats used to check price max - dap_chain_net_srv_price_unit_uid_t unit; - /// Maximum price per unit - uint64_t unit_price_max_datoshi; - /// Condition parameters size - uint32_t params_size; - } DAP_ALIGN_PACKED header; - uint8_t params[]; // condition parameters, pkey, hash or smth like this - } DAP_ALIGN_PACKED srv_pay; + /// Public key hash that could use this conditioned outout + dap_chain_hash_fast_t pkey_hash; + /// Service uid that only could be used for this outout + dap_chain_net_srv_uid_t srv_uid; + /// Price unit thats used to check price max + dap_chain_net_srv_price_unit_uid_t unit; + /// Maximum price per unit + uint64_t unit_price_max_datoshi; + } srv_pay; + struct { + // Service uid that only could be used for this outout + dap_chain_net_srv_uid_t srv_uid; + // Token ticker to change to + char token[DAP_CHAIN_TICKER_SIZE_MAX]; + // Chain network to change to + dap_chain_net_id_t net_id; + // Total amount of datoshi to change to + uint64_t value; + } srv_xchange; } subtype; -}DAP_ALIGN_PACKED dap_chain_tx_out_cond_t; - -uint8_t* dap_chain_datum_tx_out_cond_item_get_params(dap_chain_tx_out_cond_t *a_tx_out_cond, size_t *a_params_size_out); - - + uint32_t params_size; // Condition parameters size + uint8_t params[]; // condition parameters, pkey, hash or smth like this +} DAP_ALIGN_PACKED dap_chain_tx_out_cond_t; diff --git a/modules/common/include/dap_chain_datum_tx_out_ext.h b/modules/common/include/dap_chain_datum_tx_out_ext.h new file mode 100644 index 0000000000000000000000000000000000000000..536012bbd6226418cf05fd7dce55aa98dc19564d --- /dev/null +++ b/modules/common/include/dap_chain_datum_tx_out_ext.h @@ -0,0 +1,43 @@ +/* + * 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 <stdint.h> +#include "dap_common.h" +#include "dap_chain_common.h" +#include "dap_chain_datum_tx.h" + +/** + * @struct dap_chain_tx_out_ext + * @brief Multichannel transaction item output + */ +typedef struct dap_chain_tx_out_ext{ + struct { + dap_chain_tx_item_type_t type : 8; // Transaction item type - should be TX_ITEM_TYPE_OUT_EXT + uint64_t value; // Number of Datoshis ( DAP/10^9 ) to be transfered + } header; // Only header's hash is used for verification + dap_chain_addr_t addr; // Address to transfer to + char token[DAP_CHAIN_TICKER_SIZE_MAX]; // Which token is transferred +} DAP_ALIGN_PACKED dap_chain_tx_out_ext_t; diff --git a/modules/mempool/dap_chain_mempool.c b/modules/mempool/dap_chain_mempool.c index c51072d2830f55eb89833676b988392c21d25f31..d103711c103eb8e8c2fdaab895fea8cbaafa3b2b 100644 --- a/modules/mempool/dap_chain_mempool.c +++ b/modules/mempool/dap_chain_mempool.c @@ -66,14 +66,6 @@ #define LOG_TAG "dap_chain_mempool" -typedef struct list_used_item { - dap_chain_hash_fast_t tx_hash_fast; - int num_idx_out; - uint8_t padding[4]; - uint64_t value; -//dap_chain_tx_out_t *tx_out; -} list_used_item_t; - int dap_datum_mempool_init(void) { @@ -127,71 +119,19 @@ int dap_chain_mempool_tx_create(dap_chain_t * a_chain, dap_enc_key_t *a_key_from return -1; // find the transactions from which to take away coins - dap_list_t *l_list_used_out = NULL; // list of transaction with 'out' items uint64_t l_value_transfer = 0; // how many coins to transfer - { - dap_chain_hash_fast_t l_tx_cur_hash = { 0 }; - uint64_t l_value_need = a_value + a_value_fee; - while(l_value_transfer < l_value_need) - { - // Get the transaction in the cache by the addr in out item - dap_chain_datum_tx_t *l_tx = dap_chain_ledger_tx_find_by_addr(a_chain->ledger,a_token_ticker, a_addr_from, - &l_tx_cur_hash); - if(!l_tx) - break; - // Get all item from transaction by type - int l_item_count = 0; - dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get( l_tx, TX_ITEM_TYPE_OUT, - &l_item_count); - dap_list_t *l_list_tmp = l_list_out_items; - int l_out_idx_tmp = 0; // current index of 'out' item - while(l_list_tmp) { - dap_chain_tx_out_t *out_item = l_list_tmp->data; - // if 'out' item has addr = a_addr_from - if(out_item && !memcmp(a_addr_from, &out_item->addr, sizeof(dap_chain_addr_t))) { - - // Check whether used 'out' items - if(!dap_chain_ledger_tx_hash_is_used_out_item (a_chain->ledger, &l_tx_cur_hash, l_out_idx_tmp)) { - - list_used_item_t *item = DAP_NEW(list_used_item_t); - memcpy(&item->tx_hash_fast, &l_tx_cur_hash, sizeof(dap_chain_hash_fast_t)); - item->num_idx_out = l_out_idx_tmp; - item->value = out_item->header.value; - 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 >= l_value_need) { - break; - } - } - } - // go to the next 'out' item in l_tx transaction - l_out_idx_tmp++; - l_list_tmp = dap_list_next(l_list_tmp); - } - dap_list_free(l_list_out_items); - } - - // nothing to tranfer (not enough funds) - if(!l_list_used_out || l_value_transfer < l_value_need) { - dap_list_free_full(l_list_used_out, free); - return -2; - } + uint64_t l_value_need = a_value + a_value_fee; + dap_list_t *l_list_used_out = dap_chain_ledger_get_list_tx_outs_with_val(a_chain->ledger, a_token_ticker, + a_addr_from, l_value_need, &l_value_transfer); + if (!l_list_used_out) { + log_it(L_WARNING,"Not enough funds to transfer"); + return -2; } - // create empty transaction dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create(); // add 'in' items { - dap_list_t *l_list_tmp = l_list_used_out; - uint64_t l_value_to_items = 0; // how many datoshi to transfer - while(l_list_tmp) { - list_used_item_t *item = l_list_tmp->data; - if(dap_chain_datum_tx_add_in_item(&l_tx, &item->tx_hash_fast,(uint32_t) item->num_idx_out) == 1) { - l_value_to_items += item->value; - } - l_list_tmp = dap_list_next(l_list_tmp); - } + uint64_t l_value_to_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out); assert(l_value_to_items == l_value_transfer); dap_list_free_full(l_list_used_out, free); } @@ -258,56 +198,12 @@ int dap_chain_mempool_tx_create_massive( dap_chain_t * a_chain, dap_enc_key_t *a // Search unused out: uint64_t l_value_need =a_tx_num*( a_value + a_value_fee ); - dap_chain_hash_fast_t l_tx_prev_hash = { 0 }; - dap_list_t *l_list_used_out = NULL; // list of transaction with 'out' items uint64_t l_value_transfer = 0; // how many coins to transfer - log_it(L_DEBUG,"Create %lu transactions, summary %Lf.7", a_tx_num,dap_chain_balance_to_coins(l_value_need) ) ; - - while(l_value_transfer < l_value_need){ - // Get the transaction in the cache by the addr in out item - dap_chain_datum_tx_t *l_tx = dap_chain_ledger_tx_find_by_addr(a_chain->ledger, a_token_ticker,a_addr_from, - &l_tx_prev_hash); - if(!l_tx) - break; - // Get all item from transaction by type - int l_item_count = 0; - dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get( l_tx, TX_ITEM_TYPE_OUT, - &l_item_count); - dap_list_t *l_list_tmp = l_list_out_items; - int l_out_idx_tmp = 0; // current index of 'out' item - while(l_list_tmp) { - dap_chain_tx_out_t *out_item = l_list_tmp->data; - // if 'out' item has addr = a_addr_from - if(out_item && out_item->header.value && !memcmp(a_addr_from, &out_item->addr, sizeof(dap_chain_addr_t))) { - - // Check whether used 'out' items - if(!dap_chain_ledger_tx_hash_is_used_out_item (a_chain->ledger, &l_tx_prev_hash, l_out_idx_tmp)) { - - list_used_item_t *l_it = DAP_NEW(list_used_item_t); - memcpy(&l_it->tx_hash_fast, &l_tx_prev_hash, sizeof(dap_chain_hash_fast_t)); - l_it->num_idx_out = l_out_idx_tmp; - l_it->value = out_item->header.value; - log_it(L_DEBUG," Found output with value %llu", l_it->value ); - l_list_used_out = dap_list_append(l_list_used_out, l_it); - l_value_transfer += l_it->value; - // already accumulated the required value, finish the search for 'out' items - if(l_value_transfer >= l_value_need) { - l_out_idx_tmp++; - break; - } - } - } - // go to the next 'out' item in l_tx transaction - l_out_idx_tmp++; - l_list_tmp = dap_list_next(l_list_tmp); - } - } - - // nothing to tranfer (not enough funds) - if(!l_list_used_out || l_value_transfer < l_value_need) { + dap_list_t *l_list_used_out = dap_chain_ledger_get_list_tx_outs_with_val(a_chain->ledger, a_token_ticker, + a_addr_from, l_value_need, &l_value_transfer); + if (!l_list_used_out) { log_it(L_WARNING,"Not enough funds to transfer"); - dap_list_free_full(l_list_used_out, free); return -2; } @@ -447,9 +343,10 @@ int dap_chain_mempool_tx_create_massive( dap_chain_t * a_chain, dap_enc_key_t *a } dap_chain_hash_fast_t* dap_chain_mempool_tx_create_cond_input(dap_chain_net_t * a_net,dap_chain_hash_fast_t *a_tx_prev_hash, - - const dap_chain_addr_t* a_addr_to, dap_enc_key_t *l_key_tx_sign, dap_chain_datum_tx_receipt_t * l_receipt, size_t l_receipt_size) + const dap_chain_addr_t* a_addr_to, dap_enc_key_t *l_key_tx_sign, + dap_chain_datum_tx_receipt_t * l_receipt, size_t l_receipt_size) { + UNUSED(l_receipt_size); dap_ledger_t * l_ledger = a_net ? dap_chain_ledger_by_net_name( a_net->pub.name ) : NULL; if ( ! a_net || ! l_ledger || ! a_addr_to ) return NULL; @@ -464,9 +361,12 @@ 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++; + // add 'in_cond' items - // TODO - find first cond_item occurance, not just set it to 1 - if (dap_chain_datum_tx_add_in_cond_item(&l_tx,a_tx_prev_hash,1,pos-1) != 0 ){ + dap_chain_datum_tx_t *l_cond_tx = 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); + 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; @@ -526,72 +426,21 @@ static dap_chain_datum_t* dap_chain_tx_create_cond(dap_chain_net_t * a_net, return NULL; // find the transactions from which to take away coins - dap_list_t *l_list_used_out = NULL; // list of transaction with 'out' items uint64_t l_value_transfer = 0; // how many coins to transfer - { - dap_chain_hash_fast_t l_tx_cur_hash = { 0 }; - uint64_t l_value_need = a_value + a_value_fee; - while(l_value_transfer < l_value_need) - { - // Get the transaction in the cache by the addr in out item - dap_chain_datum_tx_t *l_tx = dap_chain_ledger_tx_find_by_addr(l_ledger, a_token_ticker,a_addr_from, - &l_tx_cur_hash); - if(!l_tx) - break; - // Get all item from transaction by type - int l_item_count = 0; - dap_list_t *l_list_out_items = dap_chain_datum_tx_items_get( l_tx, TX_ITEM_TYPE_OUT, - &l_item_count); - dap_list_t *l_list_tmp = l_list_out_items; - int l_out_idx_tmp = 0; // current index of 'out' item - while(l_list_tmp) { - dap_chain_tx_out_t *out_item = l_list_tmp->data; - // if 'out' item has addr = a_addr_from - if(out_item && !memcmp(a_addr_from, &out_item->addr, sizeof(dap_chain_addr_t))) { - - // Check whether used 'out' items - if(!dap_chain_ledger_tx_hash_is_used_out_item(l_ledger, &l_tx_cur_hash, l_out_idx_tmp)) { - - list_used_item_t *item = DAP_NEW(list_used_item_t); - memcpy(&item->tx_hash_fast, &l_tx_cur_hash, sizeof(dap_chain_hash_fast_t)); - item->num_idx_out = l_out_idx_tmp; - item->value = out_item->header.value; - 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 >= l_value_need) { - break; - } - } - } - // go to the next 'out' item in l_tx transaction - l_out_idx_tmp++; - l_list_tmp = dap_list_next(l_list_tmp); - } - dap_list_free(l_list_out_items); - } - - // nothing to tranfer (not enough funds) - if(!l_list_used_out || l_value_transfer < l_value_need) { - dap_list_free_full(l_list_used_out, free); - log_it( L_ERROR, "nothing to tranfer (not enough funds)"); - return NULL; - } + uint64_t l_value_need = a_value + a_value_fee; + // list of transaction with 'out' items + dap_list_t *l_list_used_out = dap_chain_ledger_get_list_tx_outs_with_val(l_ledger, a_token_ticker, + a_addr_from, l_value_need, &l_value_transfer); + if(!l_list_used_out) { + log_it( L_ERROR, "nothing to tranfer (not enough funds)"); + return NULL; } // create empty transaction dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create(); // add 'in' items { - dap_list_t *l_list_tmp = l_list_used_out; - uint64_t l_value_to_items = 0; // how many coins to transfer - while(l_list_tmp) { - list_used_item_t *item = l_list_tmp->data; - if(dap_chain_datum_tx_add_in_item(&l_tx, &item->tx_hash_fast,(uint32_t) item->num_idx_out) == 1) { - l_value_to_items += item->value; - } - l_list_tmp = dap_list_next(l_list_tmp); - } + uint64_t l_value_to_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out); assert(l_value_to_items == l_value_transfer); dap_list_free_full(l_list_used_out, free); } diff --git a/modules/mempool/include/dap_chain_mempool.h b/modules/mempool/include/dap_chain_mempool.h index abe82a61fcae79d6afb4fc9cb3fa33f91b98688d..e4db94b8be5e956217b404eddddd4f07fa9f5e33 100644 --- a/modules/mempool/include/dap_chain_mempool.h +++ b/modules/mempool/include/dap_chain_mempool.h @@ -43,6 +43,7 @@ void dap_datum_mempool_free(dap_datum_mempool_t *datum); void dap_chain_mempool_add_proc(dap_http_t * a_http_server, const char * a_url); +int dap_chain_mempool_datum_add(dap_chain_datum_t *a_datum, dap_chain_t *a_chain); int dap_chain_mempool_tx_create(dap_chain_t * a_chain, dap_enc_key_t *a_key_from, const dap_chain_addr_t* a_addr_from, const dap_chain_addr_t* a_addr_to, const dap_chain_addr_t* a_addr_fee, diff --git a/modules/net/dap_chain_net.c b/modules/net/dap_chain_net.c index 0864c057da64d0c68dfb7a46f57cdb194c8d8622..7ed6ac5e6ca03ac0f2bb8faf943a44a9dcc76874 100644 --- a/modules/net/dap_chain_net.c +++ b/modules/net/dap_chain_net.c @@ -274,6 +274,8 @@ static void s_gbd_history_callback_notify (void * a_arg, const char a_op_code, c */ static void s_chain_callback_notify(void * a_arg, dap_chain_t *a_chain, dap_chain_cell_id_t a_id) { + UNUSED(a_chain); + UNUSED(a_id); if(!a_arg) return; dap_chain_net_t * l_net = (dap_chain_net_t *) a_arg; @@ -1747,7 +1749,7 @@ void dap_chain_net_deinit() { } -dap_chain_net_t **dap_chain_net_list(size_t *a_size) +dap_chain_net_t **dap_chain_net_list(uint16_t *a_size) { *a_size = HASH_COUNT(s_net_items); dap_chain_net_t **l_net_list = DAP_NEW_SIZE(dap_chain_net_t *, (*a_size) * sizeof(dap_chain_net_t *)); @@ -1883,7 +1885,7 @@ char * dap_chain_net_get_gdb_group_mempool_by_chain_type(dap_chain_net_t * l_net */ dap_chain_node_addr_t * dap_chain_net_get_cur_addr( dap_chain_net_t * l_net) { - return PVT(l_net)->node_info? &PVT(l_net)->node_info->hdr.address: PVT(l_net)->node_addr; + return PVT(l_net)->node_info ? &PVT(l_net)->node_info->hdr.address : PVT(l_net)->node_addr; } uint64_t dap_chain_net_get_cur_addr_int(dap_chain_net_t * l_net) diff --git a/modules/net/dap_chain_node_cli.c b/modules/net/dap_chain_node_cli.c index bca667bbdf71ba4a4544490e047f9ab269b4b300..82e0eba706c69e9bf9ffe08831294ad56d4b3e5d 100644 --- a/modules/net/dap_chain_node_cli.c +++ b/modules/net/dap_chain_node_cli.c @@ -935,7 +935,8 @@ int dap_chain_node_cli_init(dap_config_t * g_config) dap_chain_node_cli_cmd_item_create ("tx_create", com_tx_create, NULL, "Make transaction", "tx_create -net <net name> -chain <chain name> -from_wallet <name> -to_addr <addr> -token <token ticker> -value <value> [-fee <addr> -value_fee <val>]\n" ); dap_chain_node_cli_cmd_item_create ("tx_cond_create", com_tx_cond_create, NULL, "Make cond transaction", - "tx_cond_create todo\n" ); + "tx_cond_create -net <net name> -token <token_ticker> -wallet_f <wallet_from> -wallet_t <wallet_to>" + "-value <value_datoshi> -unit <mb|kb|b|sec|day> -service <vpn>\n" ); dap_chain_node_cli_cmd_item_create ("tx_verify", com_tx_verify, NULL, "Verifing transaction", "tx_verify -wallet <wallet name> \n" ); diff --git a/modules/net/dap_chain_node_cli_cmd.c b/modules/net/dap_chain_node_cli_cmd.c index ba91e40fc9b05c5ad5c3dbfc42458a97157924c7..809d073a559952b4be4e489f37c794be47a9a4f5 100644 --- a/modules/net/dap_chain_node_cli_cmd.c +++ b/modules/net/dap_chain_node_cli_cmd.c @@ -1707,10 +1707,7 @@ int com_tx_wallet(int argc, char ** argv, void *arg_func, char **str_reply) break; } - char *l_str_ret_tmp = dap_string_free(l_string_ret, false); - char *str_ret = dap_strdup(l_str_ret_tmp); - dap_chain_node_cli_set_reply_text(str_reply, str_ret); - DAP_DELETE(l_str_ret_tmp); + *str_reply = dap_string_free(l_string_ret, false); return 0; } @@ -2581,12 +2578,14 @@ int com_token_decl(int argc, char ** argv, void *arg_func, char ** a_str_reply) // Token type l_arg_index=dap_chain_node_cli_find_option_val(argv, l_arg_index, argc, "-type", &l_type_str); - if (strcmp( l_type_str, "private") == 0){ - l_type = DAP_CHAIN_DATUM_TOKEN_TYPE_PRIVATE_DECL; - }else if (strcmp( l_type_str, "private_simple") == 0){ - l_type = DAP_CHAIN_DATUM_TOKEN_TYPE_SIMPLE; - }else if (strcmp( l_type_str, "public_simple") == 0){ - l_type = DAP_CHAIN_DATUM_TOKEN_TYPE_PUBLIC; + if (l_type_str) { + if (strcmp( l_type_str, "private") == 0){ + l_type = DAP_CHAIN_DATUM_TOKEN_TYPE_PRIVATE_DECL; + }else if (strcmp( l_type_str, "private_simple") == 0){ + l_type = DAP_CHAIN_DATUM_TOKEN_TYPE_SIMPLE; + }else if (strcmp( l_type_str, "public_simple") == 0){ + l_type = DAP_CHAIN_DATUM_TOKEN_TYPE_PUBLIC; + } } dap_chain_datum_token_t * l_datum_token = NULL; @@ -2862,6 +2861,7 @@ int com_token_decl(int argc, char ** argv, void *arg_func, char ** a_str_reply) l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool_by_chain_type(l_net, CHAIN_TYPE_TOKEN); } + if(dap_chain_global_db_gr_set(dap_strdup(l_key_str), (uint8_t *) l_datum, l_datum_size, l_gdb_group_mempool)) { dap_chain_node_cli_set_reply_text(a_str_reply, "datum %s with token %s is placed in datum pool ", l_key_str, l_ticker); @@ -3681,11 +3681,11 @@ int com_stats(int argc, char ** argv, void *arg_func, char **str_reply) #if (defined DAP_OS_UNIX) || (defined __WIN32) { dap_cpu_monitor_init(); - usleep(500000); + dap_usleep(500000); char *str_reply_prev = dap_strdup_printf(""); char *str_delimiter; dap_cpu_stats_t s_cpu_stats = dap_cpu_get_stats(); - for (int n_cpu_num = 0; n_cpu_num < s_cpu_stats.cpu_cores_count; n_cpu_num++) { + for (uint32_t n_cpu_num = 0; n_cpu_num < s_cpu_stats.cpu_cores_count; n_cpu_num++) { if ((n_cpu_num % 4 == 0) && (n_cpu_num != 0)) { str_delimiter = dap_strdup_printf("\n"); } else if (n_cpu_num == s_cpu_stats.cpu_cores_count - 1) { diff --git a/modules/net/dap_dns_server.c b/modules/net/dap_dns_server.c index b781f9892fc52a2404acecc367f787bc7c24c813..33e4b54ef1e92c52e58646c4b552fe3189fc6aa7 100644 --- a/modules/net/dap_dns_server.c +++ b/modules/net/dap_dns_server.c @@ -310,7 +310,7 @@ void dap_dns_server_start() { s_dns_server->hash_table = NULL; s_dns_server->instance = dap_udp_server_listen(DNS_LISTEN_PORT); if (!s_dns_server->instance) { - log_it(L_ERROR, "Can't star DNS server"); + log_it(L_ERROR, "Can't start DNS server"); return; } s_dns_server->instance->client_read_callback = dap_dns_client_read; diff --git a/modules/net/include/dap_chain_net.h b/modules/net/include/dap_chain_net.h index 3465ff818ac15aa88af1228e8dbf3440dd4f93da..b2cdeef5cbf3823738ad56f9e1b65860c1904a62 100644 --- a/modules/net/include/dap_chain_net.h +++ b/modules/net/include/dap_chain_net.h @@ -151,7 +151,7 @@ DAP_STATIC_INLINE char * dap_chain_net_get_gdb_group_mempool(dap_chain_t * l_cha dap_chain_t * dap_chain_net_get_chain_by_chain_type(dap_chain_net_t * l_net, dap_chain_type_t a_datum_type); char * dap_chain_net_get_gdb_group_mempool_by_chain_type(dap_chain_net_t * l_net, dap_chain_type_t a_datum_type); -dap_chain_net_t **dap_chain_net_list(size_t *a_size); +dap_chain_net_t **dap_chain_net_list(uint16_t *a_size); dap_list_t * dap_chain_net_get_add_gdb_group(dap_chain_net_t * a_net, dap_chain_node_addr_t a_node_addr); int dap_chain_net_verify_datum_for_add(dap_chain_net_t *a_net, dap_chain_datum_t * a_datum ); diff --git a/modules/net/srv/dap_chain_net_srv.c b/modules/net/srv/dap_chain_net_srv.c index 15b1167389a48664c85f1d1ca62d08ef8afdc962..d928fd2ec1dc9af39fa8d4b74672e80b3bebfe4d 100644 --- a/modules/net/srv/dap_chain_net_srv.c +++ b/modules/net/srv/dap_chain_net_srv.c @@ -82,6 +82,7 @@ static void s_load_all(void); */ int dap_chain_net_srv_init(dap_config_t * a_cfg) { + UNUSED(a_cfg); m_uid = NULL; m_uid_count = 0; if( dap_chain_net_srv_order_init() != 0 ) @@ -173,12 +174,13 @@ void dap_chain_net_srv_deinit(void) */ static int s_cli_net_srv( int argc, char **argv, void *arg_func, char **a_str_reply) { + UNUSED(arg_func); int arg_index = 1; dap_chain_net_t * l_net = NULL; int ret = dap_chain_node_cli_cmd_values_parse_net_chain( &arg_index, argc, argv, a_str_reply, NULL, &l_net ); if ( l_net ) { - char * l_orders_group = dap_chain_net_srv_order_get_gdb_group( l_net ); + //char * l_orders_group = dap_chain_net_srv_order_get_gdb_group( l_net ); dap_string_t *l_string_ret = dap_string_new(""); const char *l_order_str = NULL; @@ -260,7 +262,7 @@ static int s_cli_net_srv( int argc, char **argv, void *arg_func, char **a_str_re if(l_ext) { l_order->ext_size = strlen(l_ext) + 1; l_order = DAP_REALLOC(l_order, sizeof(dap_chain_net_srv_order_t) + l_order->ext_size); - strncpy(l_order->ext, l_ext, l_order->ext_size); + strncpy((char *)l_order->ext, l_ext, l_order->ext_size); } else dap_chain_net_srv_order_set_continent_region(&l_order, l_continent_num, l_region_str); @@ -452,10 +454,10 @@ static int s_cli_net_srv( int argc, char **argv, void *arg_func, char **a_str_re dap_chain_str_to_hash_fast (l_tx_cond_hash_str, &l_tx_cond_hash); l_price = (uint64_t) atoll ( l_price_str ); l_price_unit.uint32 = (uint32_t) atol ( l_price_unit_str ); - + 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,l_ext, 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); if (l_order_new_hash_str) dap_string_append_printf( l_string_ret, "Created order %s\n", l_order_new_hash_str); else{ diff --git a/modules/net/srv/dap_chain_net_srv_common.c b/modules/net/srv/dap_chain_net_srv_common.c index 494923184e111d6fe4ed0b24387fdac6b108f073..46f7dcd0831f0a511e3f0b891533ab47e8c24c51 100644 --- a/modules/net/srv/dap_chain_net_srv_common.c +++ b/modules/net/srv/dap_chain_net_srv_common.c @@ -45,4 +45,3 @@ #include "dap_chain_datum_tx_items.h" #include "dap_stream.h" #include "dap_chain_net_srv_common.h" - diff --git a/modules/net/srv/dap_chain_net_srv_order.c b/modules/net/srv/dap_chain_net_srv_order.c index 81c490d689347d6ec19f76c970a27a76edd3f3b6..3030b70ed983b28c18232549336acded255c861e 100644 --- a/modules/net/srv/dap_chain_net_srv_order.c +++ b/modules/net/srv/dap_chain_net_srv_order.c @@ -124,7 +124,7 @@ bool dap_chain_net_srv_order_set_continent_region(dap_chain_net_srv_order_t **a_ */ bool dap_chain_net_srv_order_get_continent_region(dap_chain_net_srv_order_t *a_order, uint8_t *a_continent_num, char **a_region) { - if(!a_order || !a_order->ext_size || !a_order->ext || a_order->ext[0]!=0x52) + if(!a_order || !a_order->ext_size || a_order->ext[0]!=0x52) return false; if(a_continent_num) { if((uint8_t)a_order->ext[1]!=0xff) @@ -205,7 +205,7 @@ int8_t dap_chain_net_srv_order_continent_to_num(const char *a_continent_str) return -1; } -char* dap_chain_net_srv_order_create( +char * dap_chain_net_srv_order_create( dap_chain_net_t * a_net, dap_chain_net_srv_order_direction_t a_direction, dap_chain_net_srv_uid_t a_srv_uid, // Service UID @@ -213,25 +213,33 @@ char* dap_chain_net_srv_order_create( dap_chain_hash_fast_t a_tx_cond_hash, // Hash index of conditioned transaction attached with order uint64_t a_price, // service price in datoshi, for SERV_CLASS_ONCE ONCE for the whole service, for SERV_CLASS_PERMANENT for one unit. dap_chain_net_srv_price_unit_uid_t a_price_unit, // Unit of service (seconds, megabytes, etc.) Only for SERV_CLASS_PERMANENT - char a_price_ticker[DAP_CHAIN_TICKER_SIZE_MAX], + const char a_price_ticker[DAP_CHAIN_TICKER_SIZE_MAX], dap_chain_time_t a_expires, // TS when the service expires - const char *a_ext, + const uint8_t *a_ext, + uint32_t a_ext_size, const char *a_region, int8_t a_continent_num ) { + UNUSED(a_expires); if (a_net) { - dap_chain_net_srv_order_t *l_order = DAP_NEW_Z(dap_chain_net_srv_order_t); - dap_chain_hash_fast_t* l_order_hash = DAP_NEW_Z(dap_chain_hash_fast_t); + dap_chain_net_srv_order_t *l_order; + if (a_ext_size) { + l_order = (dap_chain_net_srv_order_t *)DAP_NEW_Z_SIZE(void, sizeof(dap_chain_net_srv_order_t) + a_ext_size); + memcpy(l_order->ext, a_ext, a_ext_size); + l_order->ext_size = a_ext_size; + } + else { + l_order = DAP_NEW_Z(dap_chain_net_srv_order_t); + dap_chain_net_srv_order_set_continent_region(&l_order, a_continent_num, a_region); + } + + dap_chain_hash_fast_t l_order_hash; l_order->version = 1; l_order->srv_uid = a_srv_uid; l_order->direction = a_direction; l_order->ts_created = (dap_chain_time_t) time(NULL); - if(a_ext) - strncpy(l_order->ext, a_ext, strlen(l_order->ext) + 1); - else - dap_chain_net_srv_order_set_continent_region(&l_order, a_continent_num, a_region); if ( a_node_addr.uint64) l_order->node_addr.uint64 = a_node_addr.uint64; @@ -244,18 +252,14 @@ char* dap_chain_net_srv_order_create( strncpy(l_order->price_ticker, a_price_ticker,sizeof(l_order->price_ticker)-1); 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 ); + 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 ); char * l_gdb_group_str = dap_chain_net_srv_order_get_gdb_group( a_net); if ( !dap_chain_global_db_gr_set( dap_strdup(l_order_hash_str), l_order, l_order_size, l_gdb_group_str ) ){ DAP_DELETE( l_order ); - DAP_DELETE( l_order_hash ); - DAP_DELETE( l_order_hash_str ); DAP_DELETE( l_gdb_group_str ); return NULL; } - DAP_DELETE( l_order_hash ); - //DAP_DELETE(l_order_hash_str ); DAP_DELETE( l_gdb_group_str ); return l_order_hash_str; }else diff --git a/modules/net/srv/include/dap_chain_net_srv_common.h b/modules/net/srv/include/dap_chain_net_srv_common.h index 94886a340cb6faf161b449ece43fca0587aa2319..7ef7c2d8d48a89ffdb5fc37a5b504821326d4480 100755 --- a/modules/net/srv/include/dap_chain_net_srv_common.h +++ b/modules/net/srv/include/dap_chain_net_srv_common.h @@ -169,7 +169,8 @@ typedef struct dap_stream_ch_chain_net_srv_pkt_error{ DAP_STATIC_INLINE const char * dap_chain_net_srv_price_unit_uid_to_str( dap_chain_net_srv_price_unit_uid_t a_uid ) { switch ( a_uid.enm) { - case SERV_UNIT_UNDEFINED: return "BYTE"; + case SERV_UNIT_B: return "BYTE"; + case SERV_UNIT_KB: return "KILOBYTE"; case SERV_UNIT_MB: return "MEGABYTE"; case SERV_UNIT_SEC: return "SECOND"; case SERV_UNIT_DAY: return "DAY"; diff --git a/modules/net/srv/include/dap_chain_net_srv_order.h b/modules/net/srv/include/dap_chain_net_srv_order.h index d99377fcbf8935a2601800a3915bee50b0d8a8c5..369fba674c2c195c7c788a5c15f87edb6069237d 100644 --- a/modules/net/srv/include/dap_chain_net_srv_order.h +++ b/modules/net/srv/include/dap_chain_net_srv_order.h @@ -45,7 +45,7 @@ typedef struct dap_chain_net_srv_order //uint8_t continent; //char region[32]; uint32_t ext_size; - char ext[]; + uint8_t ext[]; } DAP_ALIGN_PACKED dap_chain_net_srv_order_t; // Init/deinit should be call only if private @@ -71,6 +71,7 @@ DAP_STATIC_INLINE dap_chain_net_srv_order_t * dap_chain_net_srv_order_find_by_ha dap_chain_hash_fast_to_str(a_hash,l_hash_str,sizeof(l_hash_str)-1); return dap_chain_net_srv_order_find_by_hash_str(a_net, l_hash_str ); } + return NULL; } int dap_chain_net_srv_order_find_all_by(dap_chain_net_t * a_net,const dap_chain_net_srv_order_direction_t a_direction, const dap_chain_net_srv_uid_t a_srv_uid, @@ -91,17 +92,17 @@ DAP_STATIC_INLINE int dap_chain_net_srv_order_delete_by_hash(dap_chain_net_t * a return dap_chain_net_srv_order_delete_by_hash_str ( a_net, l_hash_str); } -char* dap_chain_net_srv_order_create( - dap_chain_net_t * a_net, +char *dap_chain_net_srv_order_create(dap_chain_net_t * a_net, dap_chain_net_srv_order_direction_t a_direction, dap_chain_net_srv_uid_t a_srv_uid, // Service UID dap_chain_node_addr_t a_node_addr, // Node address that servs the order (if present) dap_chain_hash_fast_t a_tx_cond_hash, // Hash index of conditioned transaction attached with order uint64_t a_price, // service price in datoshi, for SERV_CLASS_ONCE ONCE for the whole service, for SERV_CLASS_PERMANENT for one unit. dap_chain_net_srv_price_unit_uid_t a_price_unit, // Unit of service (seconds, megabytes, etc.) Only for SERV_CLASS_PERMANENT - char a_price_ticker[DAP_CHAIN_TICKER_SIZE_MAX], + const char a_price_ticker[], dap_chain_time_t a_expires, // TS when the service expires - const char *a_ext, + const uint8_t *a_ext, + uint32_t a_ext_size, const char *a_region, int8_t a_continent_num ); diff --git a/modules/service/xchange/CMakeLists.txt b/modules/service/xchange/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..0d2888afb0c413051f9beceded4c57e21a9c1b23 --- /dev/null +++ b/modules/service/xchange/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 2.8) +project (dap_chain_net_srv_xchange) + +file(GLOB DAP_SRV_XCHANGE_SRCS *.c) + +file(GLOB DAP_SRV_XCHANGE_HEADERS include/*.h) + +add_library(${PROJECT_NAME} STATIC ${DAP_SRV_XCHANGE_SRCS} ${DAP_SRV_XCHANGE_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) diff --git a/modules/service/xchange/dap_chain_net_srv_xchange.c b/modules/service/xchange/dap_chain_net_srv_xchange.c new file mode 100644 index 0000000000000000000000000000000000000000..75b4c2b48d5fd000e6582fbb8640319d375656a4 --- /dev/null +++ b/modules/service/xchange/dap_chain_net_srv_xchange.c @@ -0,0 +1,801 @@ +/* + * 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 "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_xchange.h" + +#define LOG_TAG "dap_chain_net_srv_xchange" + +static int s_cli_srv_xchange(int a_argc, char **a_argv, void *a_arg_func, char **a_str_reply); +static int s_callback_requested(dap_chain_net_srv_t *a_srv, uint32_t a_usage_id, dap_chain_net_srv_client_t *a_srv_client, const void *a_data, size_t a_data_size); +static int s_callback_response_success(dap_chain_net_srv_t *a_srv, uint32_t a_usage_id, dap_chain_net_srv_client_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_t *s_srv_xchange; + +/** + * @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_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" + "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" + "\tUpdate price with specified tickers within specified net names\n" + "srv_xchange purchase <order hash> -net <net name> -wallet <wallet_name>\n" + "\tExchange tokens with specified order within specified net name\n" + "srv_xchange enable\n" + "\tEnable eXchange service\n" + "srv_xchange disable\n" + "\tDisable eXchange service\n" + ); + 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); + l_srv->_inhertor = s_srv_xchange; + s_srv_xchange->enabled = false; + return 0; +} + +void dap_chain_net_srv_xchange_deinit() +{ + dap_chain_net_srv_xchange_price_t *l_price = NULL, *l_tmp; + HASH_ITER(hh, s_srv_xchange->pricelist, l_price, l_tmp) { + HASH_DEL(s_srv_xchange->pricelist, l_price); + DAP_DELETE(l_price->key_ptr); + DAP_DELETE(l_price); + } + dap_chain_net_srv_del(s_srv_xchange->parent); + DAP_DELETE(s_srv_xchange); +} + +bool dap_chain_net_srv_xchange_verificator(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx) +{ + /* Check only one of following conditions for verification success + * 1. addr(a_cond.params).data.key == a_tx.sign.pkey -- for condition owner + * 2. a_cond.srv_xchange.(value && token && addr) == a_tx.out.(value && token && addr) -- for exchange + */ + dap_chain_addr_t *l_seller_addr = (dap_chain_addr_t *)a_cond->params; + 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); + if (!memcmp(l_seller_addr->data.key, l_pkey_ser, l_pkey_ser_size)) { + // it's the condition owner, let the transaction to be performed + 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) { + 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)) { + continue; + } + l_out_val += l_tx_out->header.value; + } + if (l_out_val != a_cond->subtype.srv_xchange.value) { + return false; + } + } + return true; +} + +static dap_chain_datum_tx_receipt_t *s_xchage_receipt_create(dap_chain_net_srv_xchange_price_t *a_price) +{ + 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); + 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, + l_ext, l_ext_size); + return l_receipt; +} + +static dap_chain_datum_tx_t *s_xchange_tx_create_request(dap_chain_net_srv_xchange_price_t *a_price, dap_chain_wallet_t *a_wallet) +{ + if (!a_price || !a_price->net_sell || !a_price->net_buy || !*a_price->token_sell || !*a_price->token_buy || !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_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_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 + // 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); + if(!l_list_used_out) { + dap_chain_datum_tx_delete(l_tx); + DAP_DELETE(l_seller_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_seller_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_XCHANGE_ID }; + dap_chain_tx_out_cond_t *l_tx_out = dap_chain_datum_tx_item_out_cond_create_srv_xchange(l_uid, a_price->net_sell->pub.id, + a_price->token_sell, a_price->datoshi_sell, + (void *)l_seller_addr, sizeof(dap_chain_addr_t)); + if (!l_tx_out) { + dap_chain_datum_tx_delete(l_tx); + DAP_DELETE(l_seller_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_price->datoshi_sell; + if (l_value_back) { + if (dap_chain_datum_tx_add_out_item(&l_tx, l_seller_addr, l_value_back) != 1) { + dap_chain_datum_tx_delete(l_tx); + DAP_DELETE(l_seller_addr); + log_it(L_ERROR, "Cant add coin back output"); + return NULL; + } + } + } + DAP_DELETE(l_seller_addr); + + // add 'sign' item + if(dap_chain_datum_tx_add_sign_item(&l_tx, l_seller_key) != 1) { + dap_chain_datum_tx_delete(l_tx); + log_it(L_ERROR, "Can't add sign output"); + return NULL; + } + + return l_tx; +} + +static dap_chain_datum_tx_t *s_xchange_tx_create_exchange(dap_chain_net_srv_xchange_price_t *a_price, dap_chain_hash_fast_t *a_tx_cond_hash, dap_chain_wallet_t *a_wallet) +{ + if (!a_price || !a_price->net_sell || !a_price->net_buy || !*a_price->token_sell || !*a_price->token_buy || !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_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_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 + // 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); + if(!l_list_used_out) { + dap_chain_datum_tx_delete(l_tx); + log_it(L_WARNING, "Nothing to change (not enough funds)"); + return NULL; + } + + // create and add reciept + dap_chain_datum_tx_receipt_t *l_receipt = s_xchage_receipt_create(a_price); + dap_chain_datum_tx_add_item(&l_tx, (byte_t *)l_receipt); + DAP_DELETE(l_receipt); + // 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_seller_addr); + log_it(L_ERROR, "Can't compose the transaction input"); + return NULL; + } + // 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_tx_cond_hash); + if (!l_cond_tx) { + log_it(L_WARNING, "Requested conditional transaction not found"); + return NULL; + } + 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); + 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) { + 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; + 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) { + dap_chain_datum_tx_delete(l_tx); + DAP_DELETE(l_seller_addr); + log_it(L_ERROR, "Can't add selling coins back output"); + return NULL; + } + } + //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) { + dap_chain_datum_tx_delete(l_tx); + DAP_DELETE(l_seller_addr); + log_it(L_ERROR, "Cant 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; + 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"); + return NULL; + //} + } + } + + // add 'sign' items + if(dap_chain_datum_tx_add_sign_item(&l_tx, l_seller_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_xchange_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; + } + 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) { + DAP_DELETE(l_datum); + return false; + } + return true; +} + +static bool s_xchage_tx_invalidate(dap_chain_net_srv_xchange_price_t *a_price, 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_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_enc_key_t *l_seller_key = dap_chain_wallet_get_key(a_wallet, 0); + + // create and add reciept + dap_chain_datum_tx_receipt_t *l_receipt = s_xchage_receipt_create(a_price); + 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_price->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_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_price->tx_hash, l_prev_cond_idx, 0); + + // add 'out' item + const dap_chain_addr_t *l_buyer_addr = (dap_chain_addr_t *)l_tx_out_cond->params; + if (memcmp(l_seller_addr->data.hash, l_buyer_addr->data.hash, sizeof(dap_chain_hash_fast_t))) { + log_it(L_WARNING, "Only owner can invalidate exchange transaction"); + return false; + } + if (dap_chain_datum_tx_add_out_item(&l_tx, l_seller_addr, l_tx_out_cond->header.value) == -1) { + dap_chain_datum_tx_delete(l_tx); + DAP_DELETE(l_seller_addr); + log_it(L_ERROR, "Cant add returning coins output"); + return false; + } + DAP_DELETE(l_seller_addr); + + // add 'sign' items + if(dap_chain_datum_tx_add_sign_item(&l_tx, l_seller_key) != 1) { + dap_chain_datum_tx_delete(l_tx); + log_it( L_ERROR, "Can't add sign output"); + return false; + } + if (!s_xchange_tx_put(l_tx, a_price->net_sell)) { + return false; + } + return true; +} + +char *s_xchange_order_create(dap_chain_net_srv_xchange_price_t *a_price, dap_chain_datum_tx_t *a_tx) +{ + dap_chain_hash_fast_t l_tx_hash = {}; + dap_hash_fast(a_tx, dap_chain_datum_tx_get_size(a_tx), &l_tx_hash); + memcpy(&a_price->tx_hash, &l_tx_hash, sizeof(dap_chain_hash_fast_t)); + dap_srv_xchange_order_ext_t l_ext; + dap_lendian_put64((uint8_t *)&l_ext.net_sell_id, a_price->net_sell->pub.id.uint64); + dap_lendian_put64((uint8_t *)&l_ext.datoshi_sell, a_price->datoshi_sell); + strcpy(l_ext.token_sell, a_price->token_sell); + uint32_t l_ext_size = sizeof(dap_srv_xchange_order_ext_t); + 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 }; + 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, + (uint8_t *)&l_ext, l_ext_size, NULL, 0); + return l_order_hash_str; +} + +static int s_cli_srv_xchange_price(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; + const char *l_net_sell_str = NULL, *l_net_buy_str = NULL; + const char *l_token_sell_str = NULL, *l_token_buy_str = NULL; + dap_chain_net_t *l_net_sell = NULL, *l_net_buy = NULL; + char *l_strkey; + if (l_cmd_num == CMD_CREATE || l_cmd_num == CMD_REMOVE || l_cmd_num == CMD_UPDATE) { + dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-net_sell", &l_net_sell_str); + if (!l_net_sell_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'price %s' required parameter -net_sell", + l_cmd_num == CMD_CREATE ? "create" : (l_cmd_num == CMD_REMOVE ? "remove" : "update")); + return -2; + } + l_net_sell = dap_chain_net_by_name(l_net_sell_str); + if (!l_net_sell) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Network %s not found", l_net_sell_str); + return -3; + } + dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-net_buy", &l_net_buy_str); + if (!l_net_buy_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'price %s' required parameter -net_buy", + l_cmd_num == CMD_CREATE ? "create" : (l_cmd_num == CMD_REMOVE ? "remove" : "update")); + return -2; + } + l_net_buy = dap_chain_net_by_name(l_net_buy_str); + if (!l_net_sell) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Network %s not found", l_net_buy_str); + return -3; + } + dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-token_sell", &l_token_sell_str); + if (!l_token_sell_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'price %s' required parameter -token_sell", + l_cmd_num == CMD_CREATE ? "create" : (l_cmd_num == CMD_REMOVE ? "remove" : "update")); + return -5; + } + if (!dap_chain_ledger_token_ticker_check(l_net_sell->pub.ledger, l_token_sell_str)) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Token ticker %s not found", l_token_sell_str); + return -6; + } + dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-token_buy", &l_token_buy_str); + if (!l_token_buy_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'price %s' required parameter -token_buy", + l_cmd_num == CMD_CREATE ? "create" : (l_cmd_num == CMD_REMOVE ? "remove" : "update")); + return -5; + } + if (!dap_chain_ledger_token_ticker_check(l_net_buy->pub.ledger, l_token_buy_str)) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Token ticker %s not found", l_token_buy_str); + return -6; + } + l_strkey = DAP_NEW_SIZE(char, dap_strlen(l_token_sell_str) + dap_strlen(l_net_sell_str) + + dap_strlen(l_token_buy_str) + dap_strlen(l_net_buy_str) + 1); + dap_stpcpy(l_strkey, l_token_sell_str); + strcat(l_strkey, l_net_sell_str); + strcat(l_strkey, l_token_buy_str); + strcat(l_strkey, l_net_buy_str); + } + switch (l_cmd_num) { + case CMD_CREATE: { + dap_chain_net_srv_xchange_price_t *l_price = NULL; + HASH_FIND_STR(s_srv_xchange->pricelist, l_strkey, l_price); + if (l_price) { + 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); + if (!l_val_sell_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'price create' required parameter -datoshi_sell"); + 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>"); + 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"); + 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>"); + return -9; + } + 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 'price create' required parameter -wallet"); + return -10; + } + 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 -11; + } + if (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; + } + // Create the price + l_price = DAP_NEW_Z(dap_chain_net_srv_xchange_price_t); + l_price->wallet_str = dap_strdup(l_wallet_str); + dap_stpcpy(l_price->token_sell, l_token_sell_str); + l_price->net_sell = l_net_sell; + dap_stpcpy(l_price->token_buy, l_token_buy_str); + 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; + // 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; + } + // 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_net_srv_order_delete_by_hash_str(l_net_buy, l_order_hash_str); + DAP_DELETE(l_order_hash_str); + break; + } + dap_chain_node_cli_set_reply_text(a_str_reply, "Successfully created order %s", l_order_hash_str); + DAP_DELETE(l_order_hash_str); + // Add active price to pricelist + HASH_ADD_KEYPTR(hh, s_srv_xchange->pricelist, l_price->key_ptr, strlen(l_price->key_ptr), l_price); + } 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; + case CMD_REMOVE: + 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_val_buy_str) { + l_price->datoshi_buy = l_datoshi_buy; + } + // 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); + 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 + 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; + } + // 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); + } + 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; + } + // Update the pricelist + HASH_ADD_KEYPTR(hh, s_srv_xchange->pricelist, l_price->key_ptr, strlen(l_price->key_ptr), l_price); + } + } 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; + } + } 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) { + 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%lu\t%lu\t%s\t%s\n", 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->datoshi_buy, l_price->wallet_str, l_order_hash_str); + DAP_DELETE(l_order_hash_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; + default: { + dap_chain_node_cli_set_reply_text(a_str_reply, "Subcommand %s not recognized", a_argv[a_arg_index]); + return -4; + } + } + return 0; +} + +static int s_cli_srv_xchange(int a_argc, char **a_argv, void *a_arg_func, char **a_str_reply) +{ + UNUSED(a_arg_func); + enum { + CMD_NONE, CMD_PRICE, CMD_PURCHASE, CMD_ENABLE, CMD_DISABLE + }; + 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), "price", NULL)) { + l_cmd_num = CMD_PRICE; + } + else if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "purchase", NULL)) { + l_cmd_num = CMD_PURCHASE; + } + else if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "enable", NULL)) { + l_cmd_num = CMD_ENABLE; + } + else if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "disable", NULL)) { + l_cmd_num = CMD_DISABLE; + } + switch (l_cmd_num) { + case CMD_PRICE: + return s_cli_srv_xchange_price(a_argc, a_argv, l_arg_index + 1, a_str_reply); + case CMD_PURCHASE: { + const char *l_net_str = NULL, *l_wallet_str = 0; + dap_chain_node_cli_find_option_val(a_argv, ++l_arg_index + 1, a_argc, "-net", &l_net_str); + if (!l_net_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'purchase' required parameter -net"); + return -2; + } + 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 -3; + } + dap_chain_node_cli_find_option_val(a_argv, l_arg_index + 1, a_argc, "-wallet", &l_wallet_str); + if (!l_wallet_str) { + dap_chain_node_cli_set_reply_text(a_str_reply, "Command 'purchase' required parameter -wallet"); + return -10; + } + 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 -11; + } + char *l_order_hash_str = a_argv[l_arg_index]; + 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); + // 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 + dap_chain_net_srv_order_delete_by_hash_str(l_price->net_buy, l_order_hash_str); + } + DAP_DELETE(l_price); + DAP_DELETE(l_order); + dap_chain_node_cli_set_reply_text(a_str_reply, l_tx ? "Exchange transaction has done" : + "Exchange transaction error"); + } + } break; + case CMD_ENABLE: { + s_srv_xchange->enabled = true; + } break; + case CMD_DISABLE: { + s_srv_xchange->enabled = false; + } 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; +} + +static int s_callback_requested(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) +{ + return 0; +} + +static int s_callback_response_success(dap_chain_net_srv_t *a_srv, uint32_t a_usage_id, dap_chain_net_srv_client_t *a_srv_client, const void *a_data, size_t a_data_size) +{ + return 0; +} + +static int s_callback_response_error(dap_chain_net_srv_t *a_srv, uint32_t a_usage_id, dap_chain_net_srv_client_t *a_srv_client, const void *a_data, size_t a_data_size) +{ + return 0; +} + +static int s_callback_receipt_next_success(dap_chain_net_srv_t *a_srv, uint32_t a_usage_id, dap_chain_net_srv_client_t *a_srv_client, const void *a_data, size_t a_data_size) +{ + return 0; +} diff --git a/modules/service/xchange/include/dap_chain_net_srv_xchange.h b/modules/service/xchange/include/dap_chain_net_srv_xchange.h new file mode 100644 index 0000000000000000000000000000000000000000..0ab6610b71c5dd5e0491d7e8021ec87045090b7d --- /dev/null +++ b/modules/service/xchange/include/dap_chain_net_srv_xchange.h @@ -0,0 +1,60 @@ +/* + * 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_XCHANGE_ID 0x2 + +typedef struct dap_chain_net_srv_xchange_price { + char *wallet_str; + dap_chain_net_t *net_sell; + char token_sell[DAP_CHAIN_TICKER_SIZE_MAX]; + uint64_t datoshi_sell; + dap_chain_net_t *net_buy; + char token_buy[DAP_CHAIN_TICKER_SIZE_MAX]; + uint64_t datoshi_buy; + 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_srv_xchange_order_ext { + uint64_t net_sell_id; + uint64_t datoshi_sell; + char token_sell[DAP_CHAIN_TICKER_SIZE_MAX]; +} dap_srv_xchange_order_ext_t; + +typedef struct dap_chain_net_srv_xchange { + dap_chain_net_srv_t *parent; + dap_chain_net_srv_xchange_price_t *pricelist; + bool enabled; +} dap_chain_net_srv_xchange_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); diff --git a/modules/wallet/dap_chain_wallet.c b/modules/wallet/dap_chain_wallet.c index 0b30adb47cf7b59d71787ce167e9df1de6a8b0f6..ac14456bde419486265b174b99dd99f16ae910b8 100644 --- a/modules/wallet/dap_chain_wallet.c +++ b/modules/wallet/dap_chain_wallet.c @@ -412,7 +412,7 @@ dap_chain_wallet_t * dap_chain_wallet_open(const char * a_wallet_name, const cha * @param a_net_id * @return */ -uint64_t dap_chain_wallet_get_balance(dap_chain_wallet_t *a_wallet, dap_chain_net_id_t a_net_id, char *a_token_ticker) +uint64_t dap_chain_wallet_get_balance(dap_chain_wallet_t *a_wallet, dap_chain_net_id_t a_net_id, const char *a_token_ticker) { dap_chain_net_t *l_net = dap_chain_net_by_id(a_net_id); dap_chain_addr_t *l_addr =dap_chain_wallet_get_addr(a_wallet, a_net_id); diff --git a/modules/wallet/include/dap_chain_wallet.h b/modules/wallet/include/dap_chain_wallet.h index c58ca634974a7dc6048d90d9470aba0302ab8f09..86c113f348f7b5dba0fecdb9e64c771d9e804313 100644 --- a/modules/wallet/include/dap_chain_wallet.h +++ b/modules/wallet/include/dap_chain_wallet.h @@ -58,6 +58,6 @@ size_t dap_chain_wallet_get_certs_number( dap_chain_wallet_t * a_wallet); dap_pkey_t * dap_chain_wallet_get_pkey( dap_chain_wallet_t * a_wallet,uint32_t a_key_idx); dap_enc_key_t * dap_chain_wallet_get_key( dap_chain_wallet_t * a_wallet,uint32_t a_key_idx); -uint64_t dap_chain_wallet_get_balance(dap_chain_wallet_t *a_wallet, dap_chain_net_id_t a_net_id, char *a_token_ticker); +uint64_t dap_chain_wallet_get_balance(dap_chain_wallet_t *a_wallet, dap_chain_net_id_t a_net_id, const char *a_token_ticker); int dap_chain_wallet_save_file( dap_chain_wallet_t * a_wallet);