diff --git a/dap-sdk b/dap-sdk index 1d5008d5e80b14e81c4dc044280619c0310bcbfc..788b90b061da6c6b462785876c349f2886135bab 160000 --- a/dap-sdk +++ b/dap-sdk @@ -1 +1 @@ -Subproject commit 1d5008d5e80b14e81c4dc044280619c0310bcbfc +Subproject commit 788b90b061da6c6b462785876c349f2886135bab diff --git a/modules/chain/include/dap_chain_common.h b/modules/chain/include/dap_chain_common.h index 7265855a3c6b251e50db47114df671c39d5a2692..edd061b252458ad77e9c86aaaa9fc8d357c52bdd 100644 --- a/modules/chain/include/dap_chain_common.h +++ b/modules/chain/include/dap_chain_common.h @@ -43,6 +43,7 @@ #define DAP_CHAIN_HASH_SLOW_SIZE 32 #define DAP_CHAIN_TIMESTAMP_SIZE 8 #define DAP_CHAIN_TICKER_SIZE_MAX 10 +#define DAP_CHAIN_DATUM_NONCE_SIZE 64 #define DATOSHI_LD 1000000000.0L // Deprecated #define DATOSHI_DEGREE 18 diff --git a/modules/chain/include/dap_chain_srv.h b/modules/chain/include/dap_chain_srv.h index 62c5e51facc0eb902eb9a123028adda64e45b7cd..d96f4f3b0561b7fd11265584543e00b5873ed650 100644 --- a/modules/chain/include/dap_chain_srv.h +++ b/modules/chain/include/dap_chain_srv.h @@ -28,6 +28,10 @@ along with any CellFrame SDK based project. If not, see <http://www.gnu.org/lic #include "dap_chain_common.h" #include "dap_tsd.h" +// System services literlas +#define DAP_CHAIN_NET_SRV_STAKE_POS_DELEGATE_LITERAL "PoS-delegate" +#define DAP_CHAIN_NET_SRV_XCHANGE_LITERAL "eXchange" + // Start service callback typedef void * (*dap_chain_srv_callback_start_t)(dap_chain_net_id_t a_net_id, dap_config_t *a_config); // Process service decree diff --git a/modules/chain/tests/dap_chain_ledger_tests.c b/modules/chain/tests/dap_chain_ledger_tests.c index 687cca0e0d6e1606b40e5472e9fb394a7d04b972..2a06c5d9866fd4b6eea10a7dcfdeae818417cf88 100644 --- a/modules/chain/tests/dap_chain_ledger_tests.c +++ b/modules/chain/tests/dap_chain_ledger_tests.c @@ -11,7 +11,7 @@ #include "dap_chain_net_srv_vpn.h" #include "dap_chain_net_srv_stake_lock.h" #include "dap_chain_net_srv_stake_pos_delegate.h" -#include "dap_chain_net_decree.h" +#include "dap_chain_ledger.h" #include "dap_chain_block.h" #include "dap_chain_cs_blocks.h" #include "dap_chain_cs_esbocs.h" @@ -387,7 +387,7 @@ int dap_ledger_test_create_reward_decree(dap_chain_t *a_chain, dap_chain_net_id_ dap_hash_fast_t l_decree_hash = {}; dap_hash_fast(l_decree, l_decree_size, &l_decree_hash); // a_chain->callback_atom_add(); - dap_assert_PIF(dap_chain_net_decree_apply(&l_decree_hash, l_decree, a_chain, false)==0, "Decree applying:"); + dap_assert_PIF(dap_ledger_decree_apply(&l_decree_hash, l_decree, a_chain, false)==0, "Decree applying:"); return 0; } @@ -1027,7 +1027,7 @@ void dap_ledger_test_run(void){ dap_assert_PIF(dap_chain_cs_create(l_chain_main, &l_cfg) == 0, "Chain esbocs cs creating: "); DL_APPEND(l_net->pub.chains, l_chain_main); - dap_assert_PIF(!dap_chain_net_decree_init(l_net), "Decree initialization:"); + dap_assert_PIF(!dap_ledger_decree_create(l_net), "Decree initialization:"); char *l_seed_ph = "H58i9GJKbn91238937^#$t6cjdf"; size_t l_seed_ph_size = strlen(l_seed_ph); diff --git a/modules/datum/include/dap_chain_datum_token.h b/modules/datum/include/dap_chain_datum_token.h index 8cbd69b20fa7d35b9a9eecc6eeabbed1c85d517a..4797e0a7bf63fea06282d2928909783f9d6bf425 100644 --- a/modules/datum/include/dap_chain_datum_token.h +++ b/modules/datum/include/dap_chain_datum_token.h @@ -30,10 +30,8 @@ #include "dap_string.h" #include "dap_tsd.h" #include "dap_strfuncs.h" -#include "json_object.h" - +#include "dap_chain_common.h" -#define DAP_CHAIN_DATUM_NONCE_SIZE 64 // Token declaration typedef struct dap_chain_datum_token_old { uint16_t type; @@ -316,6 +314,16 @@ DAP_STATIC_INLINE bool dap_chain_datum_token_is_old(uint8_t a_type) || a_type == DAP_CHAIN_DATUM_TOKEN_TYPE_OLD_PUBLIC; } +DAP_STATIC_INLINE bool dap_chain_datum_token_check_ticker(const char *a_ticker) +{ + const char *c = a_ticker; + for (int i = 0; i < DAP_CHAIN_TICKER_SIZE_MAX; i++, c++) + if (*c == '\0') + return true; + return false; +} + + /* Token emission section */ struct DAP_ALIGN_PACKED dap_chain_emission_header_v0 { diff --git a/modules/ledger/dap_chain_ledger.c b/modules/ledger/dap_chain_ledger.c index a55c1e62f238f4ce0fb8889548af2e42d6202278..ca10ccf3c1ea518bf1743b527b7919ec1be32eca 100644 --- a/modules/ledger/dap_chain_ledger.c +++ b/modules/ledger/dap_chain_ledger.c @@ -2,9 +2,10 @@ * Authors: * Dmitriy A. Gearasimov <gerasimov.dmitriy@demlabs.net> * Alexander Lysikov <alexander.lysikov@demlabs.net> + * Roman Khlopkov <roman.khlopkov@demlabs.net> * DeM Labs Inc. https://demlabs.net * DeM Labs Open source community https://github.com/demlabsinc - * Copyright (c) 2017-2019 + * Copyright (c) 2017-2024 * All rights reserved. This file is part of CellFrame SDK the open source project @@ -25,20 +26,9 @@ #include "dap_common.h" #include <dirent.h> -#ifdef _WIN32 -#include <winsock2.h> -#include <windows.h> -#include <mswsock.h> -#include <ws2tcpip.h> -#include <io.h> -#include <time.h> -#endif - #include "uthash.h" -#include "utlist.h" - +#include "dap_chain_ledger_pvt.h" #include "dap_chain_common.h" -#include "dap_chain_net_decree.h" #include "dap_math_ops.h" #include "dap_list.h" #include "dap_hash.h" @@ -50,24 +40,9 @@ #include "dap_global_db.h" #include "dap_chain_ledger.h" #include "json_object.h" -#include "dap_notify_srv.h" -#include "dap_chain_net_srv_stake_pos_delegate.h" #define LOG_TAG "dap_ledger" -typedef struct dap_ledger_verificator { - int subtype; // hash key - dap_ledger_verificator_callback_t callback; - dap_ledger_updater_callback_t callback_added; - dap_ledger_delete_callback_t callback_deleted; - UT_hash_handle hh; -} dap_ledger_verificator_t; - -typedef struct dap_chain_ledger_votings_callbacks { - dap_chain_ledger_voting_callback_t voting_callback; - dap_chain_ledger_voting_delete_callback_t voting_delete_callback; -} dap_chain_ledger_votings_callbacks_t; - typedef struct dap_ledger_service_info { dap_chain_srv_uid_t service_uid; // hash key char tag_str[32]; // tag string name @@ -75,222 +50,15 @@ typedef struct dap_ledger_service_info { UT_hash_handle hh; } dap_ledger_service_info_t; -static dap_ledger_verificator_t *s_verificators; static dap_ledger_service_info_t *s_services; +static pthread_rwlock_t s_services_rwlock = PTHREAD_RWLOCK_INITIALIZER; -static pthread_rwlock_t s_verificators_rwlock; -static pthread_rwlock_t s_services_rwlock; - -static dap_chain_ledger_votings_callbacks_t s_voting_callbacks; - -#define MAX_OUT_ITEMS 10 - -typedef struct dap_ledger_stake_lock_item { - dap_chain_hash_fast_t tx_for_stake_lock_hash; - dap_chain_hash_fast_t tx_used_out; - UT_hash_handle hh; -} dap_ledger_stake_lock_item_t; - -typedef struct dap_ledger_token_emission_item { - dap_chain_hash_fast_t datum_token_emission_hash; - dap_chain_datum_token_emission_t *datum_token_emission; - size_t datum_token_emission_size; - dap_chain_hash_fast_t tx_used_out; - dap_nanotime_t ts_added; - UT_hash_handle hh; -} dap_ledger_token_emission_item_t; - -typedef struct dap_ledger_token_update_item { - dap_hash_fast_t update_token_hash; - dap_chain_datum_token_t *datum_token_update; - size_t datum_token_update_size; - time_t updated_time; - UT_hash_handle hh; -} dap_ledger_token_update_item_t; - -typedef struct dap_ledger_token_item { - char ticker[DAP_CHAIN_TICKER_SIZE_MAX]; - uint16_t subtype; - dap_chain_datum_token_t *datum_token; - uint64_t datum_token_size; - - uint256_t total_supply; - uint256_t current_supply; - - pthread_rwlock_t token_emissions_rwlock; - dap_ledger_token_emission_item_t * token_emissions; - - pthread_rwlock_t token_ts_updated_rwlock; - dap_ledger_token_update_item_t * token_ts_updated; - time_t last_update_token_time; - - // for auth operations - - dap_pkey_t ** auth_pkeys; - dap_chain_hash_fast_t *auth_pkey_hashes; - size_t auth_signs_total; - size_t auth_signs_valid; - uint32_t flags; - dap_chain_addr_t * tx_recv_allow; - size_t tx_recv_allow_size; - dap_chain_addr_t * tx_recv_block; - size_t tx_recv_block_size; - dap_chain_addr_t * tx_send_allow; - size_t tx_send_allow_size; - dap_chain_addr_t * tx_send_block; - size_t tx_send_block_size; - char *description; - // For delegated tokens - bool is_delegated; - char delegated_from[DAP_CHAIN_TICKER_SIZE_MAX]; - uint256_t emission_rate; - - UT_hash_handle hh; -} dap_ledger_token_item_t; - -// ledger cache item - one of unspent outputs -typedef struct dap_ledger_tx_item { - dap_chain_hash_fast_t tx_hash_fast; - dap_chain_datum_tx_t *tx; - dap_nanotime_t ts_added; - struct { - dap_time_t ts_created; // Transation datum timestamp mirrored & cached - uint32_t n_outs; - uint32_t n_outs_used; - char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX]; - byte_t padding[6]; - byte_t multichannel; - dap_time_t ts_spent; - byte_t pad[7]; - dap_chain_srv_uid_t tag; //tag (or service this tx is belong to) - dap_chain_tx_tag_action_type_t action; - // TODO dynamically allocates the memory in order not to limit the number of outputs in transaction - dap_chain_hash_fast_t tx_hash_spent_fast[MAX_OUT_ITEMS]; // spent outs list - } DAP_ALIGN_PACKED cache_data; - UT_hash_handle hh; -} dap_ledger_tx_item_t; - -typedef struct dap_ledger_tokenizer { - char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX]; - uint256_t sum; - UT_hash_handle hh; -} dap_ledger_tokenizer_t; - -typedef struct dap_ledger_reward_key { - dap_hash_fast_t block_hash; - dap_hash_fast_t sign_pkey_hash; -} DAP_ALIGN_PACKED dap_ledger_reward_key_t; - -typedef struct dap_ledger_reward_item { - dap_ledger_reward_key_t key; - dap_hash_fast_t spender_tx; - UT_hash_handle hh; -} dap_ledger_reward_item_t; - -typedef struct dap_ledger_tx_bound { - uint8_t type; - uint16_t prev_out_idx; - uint256_t value; - union { - dap_ledger_token_item_t *token_item; // For current_supply update on emissions - dap_chain_tx_out_cond_t *cond; // For conditional output - struct { - char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX]; - dap_chain_addr_t addr_from; - } in; - }; - union { - dap_ledger_tx_item_t *prev_item; // For not emission TX - dap_ledger_token_emission_item_t *emission_item; - dap_ledger_stake_lock_item_t *stake_lock_item; - dap_ledger_reward_key_t reward_key; - }; -} dap_ledger_tx_bound_t; - -// in-memory wallet balance -typedef struct dap_ledger_wallet_balance { - char *key; - char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX]; - uint256_t balance; - UT_hash_handle hh; -} dap_ledger_wallet_balance_t; - -typedef struct dap_ledger_cache_item { - dap_chain_hash_fast_t *hash; - bool found; -} dap_ledger_cache_item_t; - -typedef struct dap_ledger_cache_str_item { - char *key; - bool found; -} dap_ledger_cache_str_item_t; - -typedef struct dap_ledger_tx_notifier { - dap_ledger_tx_add_notify_t callback; - void *arg; -} dap_ledger_tx_notifier_t; - -typedef struct dap_ledger_bridged_tx_notifier { - dap_ledger_bridged_tx_notify_t callback; - void *arg; -} dap_ledger_bridged_tx_notifier_t; - -typedef struct dap_ledger_hal_item { - dap_chain_hash_fast_t hash; - UT_hash_handle hh; -} dap_ledger_hal_item_t; - -// dap_ledger_t private section -typedef struct dap_ledger_private { - // separate access to transactions - pthread_rwlock_t ledger_rwlock; - dap_ledger_tx_item_t *ledger_items; - // separate access to tokens - pthread_rwlock_t tokens_rwlock; - dap_ledger_token_item_t *tokens; - // separate acces to stake items - pthread_rwlock_t stake_lock_rwlock; - dap_ledger_stake_lock_item_t *emissions_for_stake_lock; - // separate access to rewards - pthread_rwlock_t rewards_rwlock; - dap_ledger_reward_item_t *rewards; - // separate access to balances - pthread_rwlock_t balance_accounts_rwlock; - dap_ledger_wallet_balance_t *balance_accounts; - // separate access to threshold - dap_ledger_tx_item_t *threshold_txs; - pthread_rwlock_t threshold_txs_rwlock; - dap_interval_timer_t threshold_txs_free_timer; - // Save/load operations condition - pthread_mutex_t load_mutex; - pthread_cond_t load_cond; - bool load_end; - // Ledger flags - bool check_ds, check_cells_ds, check_token_emission, cached, mapped, threshold_enabled; - dap_chain_cell_id_t local_cell_id; - //notifiers - dap_list_t *bridged_tx_notifiers; - dap_list_t *tx_add_notifiers; - dap_ledger_cache_tx_check_callback_t cache_tx_check_callback; - // White- and blacklist - dap_ledger_hal_item_t *hal_items, *hrl_items; -} dap_ledger_private_t; - -#define PVT(a) ( (dap_ledger_private_t *) a->_internal ) +bool g_debug_ledger = true; -static dap_ledger_tx_item_t* tx_item_find_by_addr(dap_ledger_t *a_ledger, - const dap_chain_addr_t *a_addr, const char * a_token, dap_chain_hash_fast_t *a_tx_first_hash); -static void s_threshold_emissions_proc( dap_ledger_t * a_ledger); -static void s_threshold_txs_proc( dap_ledger_t * a_ledger); static void s_threshold_txs_free(dap_ledger_t *a_ledger); -static int s_sort_ledger_tx_item(dap_ledger_tx_item_t* a, dap_ledger_tx_item_t* b); - -static size_t s_threshold_emissions_max = 1000; static size_t s_threshold_txs_max = 10000; -static bool s_debug_more = true; static size_t s_threshold_free_timer_tick = 900000; // 900000 ms = 15 minutes. -struct json_object *wallet_info_json_collect(dap_ledger_t *a_ledger, dap_ledger_wallet_balance_t* a_bal); //add a service declaration for tx tagging and more static bool s_tag_check_block_reward(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_chain_datum_tx_item_groups_t *a_items_grp, dap_chain_tx_tag_action_type_t *a_action) { @@ -442,16 +210,13 @@ int dap_ledger_service_add(dap_chain_srv_uid_t a_uid, char *tag_str, dap_ledger_ /** * @brief dap_ledger_init - * current function version set s_debug_more parameter, if it define in config, and returns 0 + * current function version set g_debug_ledger parameter, if it define in config, and returns 0 * @return */ int dap_ledger_init() { - s_debug_more = dap_config_get_item_bool_default(g_config,"ledger","debug_more",false); + g_debug_ledger = dap_config_get_item_bool_default(g_config, "ledger", "debug_more",false); - pthread_rwlock_init(&s_verificators_rwlock, NULL); - pthread_rwlock_init(&s_services_rwlock, NULL); - //register native ledger services dap_chain_srv_uid_t l_uid_transfer = { .uint64 = DAP_CHAIN_NET_SRV_TRANSFER_ID }; dap_ledger_service_add(l_uid_transfer, "transfer", s_tag_check_transfer); @@ -467,7 +232,6 @@ int dap_ledger_init() */ void dap_ledger_deinit() { - pthread_rwlock_destroy(&s_verificators_rwlock); pthread_rwlock_destroy(&s_services_rwlock); } @@ -522,4489 +286,692 @@ void dap_ledger_handle_free(dap_ledger_t *a_ledger) } -struct json_object *wallet_info_json_collect(dap_ledger_t *a_ledger, dap_ledger_wallet_balance_t *a_bal) { - struct json_object *l_json = json_object_new_object(); - json_object_object_add(l_json, "class", json_object_new_string("Wallet")); - struct json_object *l_network = json_object_new_object(); - json_object_object_add(l_network, "name", json_object_new_string(a_ledger->net->pub.name)); - char *pos = strrchr(a_bal->key, ' '); - if (pos) { - size_t l_addr_len = pos - a_bal->key; - char *l_addr_str = DAP_NEW_STACK_SIZE(char, l_addr_len + 1); - if ( !l_addr_str ) - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - memcpy(l_addr_str, a_bal->key, pos - a_bal->key); - *(l_addr_str + l_addr_len) = '\0'; - json_object_object_add(l_network, "address", json_object_new_string(l_addr_str)); - } else { - json_object_object_add(l_network, "address", json_object_new_string("Unknown")); - } - struct json_object *l_token = json_object_new_object(); - json_object_object_add(l_token, "name", json_object_new_string(a_bal->token_ticker)); - const char *l_balance_coins, *l_balance_datoshi = dap_uint256_to_char(a_bal->balance, &l_balance_coins); - json_object_object_add(l_token, "full_balance", json_object_new_string(l_balance_coins)); - json_object_object_add(l_token, "datoshi", json_object_new_string(l_balance_datoshi)); - json_object_object_add(l_network, "tokens", l_token); - json_object_object_add(l_json, "networks", l_network); - return l_json; -} - -inline static dap_ledger_hal_item_t *s_check_hal(dap_ledger_t *a_ledger, dap_hash_fast_t *a_hal_hash) -{ - dap_ledger_hal_item_t *ret = NULL; - HASH_FIND(hh, PVT(a_ledger)->hal_items, a_hal_hash, sizeof(dap_hash_fast_t), ret); - debug_if(s_debug_more && ret, L_MSG, "Datum %s is whitelisted", dap_hash_fast_to_str_static(a_hal_hash)); - return ret; -} - bool dap_ledger_datum_is_blacklisted(dap_ledger_t *a_ledger, dap_hash_fast_t a_hash) { dap_ledger_hal_item_t *ret = NULL; HASH_FIND(hh, PVT(a_ledger)->hrl_items, &a_hash, sizeof(dap_hash_fast_t), ret); - return debug_if(s_debug_more && ret, L_MSG, "Datum %s is blacklisted", dap_hash_fast_to_str_static(&a_hash)), !!ret; + return debug_if(g_debug_ledger && ret, L_MSG, "Datum %s is blacklisted", dap_hash_fast_to_str_static(&a_hash)), !!ret; } -inline static dap_ledger_token_item_t *s_ledger_find_token(dap_ledger_t *a_ledger, const char *a_token_ticker) -{ - dap_return_val_if_fail(a_ledger && a_token_ticker, NULL); - dap_ledger_token_item_t *l_token_item = NULL; - pthread_rwlock_rdlock(&PVT(a_ledger)->tokens_rwlock); - HASH_FIND_STR(PVT(a_ledger)->tokens, a_token_ticker, l_token_item); - pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); - return l_token_item; -} /** - * @brief s_token_tsd_parse + * @brief s_tx_header_print + * prepare data for print, add time * - * @param a_ledger - * @param a_item_apply_to - * @param a_token - * @param a_token_size - * @return int + * return history string + * @param a_tx + * @param a_tx_hash + * @param a_hash_out_type + * @return a_json_out */ -static int s_token_tsd_parse(dap_ledger_token_item_t *a_item_apply_to, dap_chain_datum_token_t *a_current_datum, - dap_ledger_t *a_ledger, byte_t *a_tsd, size_t a_tsd_total_size, bool a_apply) + +static void s_tx_header_print(json_object *a_json_out, dap_chain_datum_tx_t *a_tx, + const char *a_hash_out_type, dap_chain_hash_fast_t *a_tx_hash) { - if (!a_tsd_total_size) { - debug_if(a_item_apply_to, L_NOTICE, "No TSD sections in datum token"); - return DAP_LEDGER_CHECK_OK; - } - dap_return_val_if_pass(a_apply && !a_item_apply_to, DAP_LEDGER_CHECK_INVALID_ARGS); - size_t l_new_signs_valid = a_item_apply_to ? a_item_apply_to->auth_signs_valid : 0; - size_t l_new_signs_total = a_item_apply_to ? a_item_apply_to->auth_signs_total : 0; - dap_pkey_t **l_new_pkeys = NULL; - dap_hash_fast_t *l_new_pkey_hashes = NULL; - bool l_was_pkeys_copied = false; - size_t l_new_tx_recv_allow_size = a_item_apply_to ? a_item_apply_to->tx_recv_allow_size : 0; - size_t l_new_tx_recv_block_size = a_item_apply_to ? a_item_apply_to->tx_recv_block_size : 0; - size_t l_new_tx_send_allow_size = a_item_apply_to ? a_item_apply_to->tx_send_allow_size : 0; - size_t l_new_tx_send_block_size = a_item_apply_to ? a_item_apply_to->tx_send_block_size : 0; - dap_chain_addr_t *l_new_tx_recv_allow = NULL, *l_new_tx_recv_block = NULL, - *l_new_tx_send_allow = NULL, *l_new_tx_send_block = NULL; - bool l_was_tx_recv_allow_copied = false, l_was_tx_recv_block_copied = false, - l_was_tx_send_allow_copied = false, l_was_tx_send_block_copied = false; + char l_time_str[DAP_TIME_STR_SIZE] = "unknown"; + if (a_tx->header.ts_created) + dap_time_to_str_rfc822(l_time_str, DAP_TIME_STR_SIZE, a_tx->header.ts_created); + const char *l_tx_hash_str = dap_strcmp(a_hash_out_type, "hex") + ? dap_enc_base58_encode_hash_to_str_static(a_tx_hash) + : dap_chain_hash_fast_to_str_static(a_tx_hash); + json_object_object_add(a_json_out, "TX hash ", json_object_new_string(l_tx_hash_str)); + json_object_object_add(a_json_out, "time ", json_object_new_string(l_time_str)); +} -#define m_ret_cleanup(ret_code) ({ \ - DAP_DELETE_COUNT(l_new_pkeys, l_new_signs_total); \ - DAP_DEL_MULTY(l_new_tx_recv_allow, l_new_tx_recv_block, \ - l_new_tx_send_allow, l_new_tx_send_block, \ - l_new_pkeys, l_new_pkey_hashes); \ - ret_code; }) - uint64_t l_tsd_size = 0; - dap_tsd_t *l_tsd = (dap_tsd_t *)a_tsd; - for (uint64_t l_offset = 0; l_offset < a_tsd_total_size; l_offset += l_tsd_size) { - if (l_offset + sizeof(dap_tsd_t) > a_tsd_total_size || l_offset + sizeof(dap_tsd_t) < l_offset) { - log_it(L_WARNING, "Incorrect TSD section size, less than header"); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - l_tsd = (dap_tsd_t *)((byte_t *)l_tsd + l_tsd_size); - l_tsd_size = dap_tsd_size(l_tsd); - if (l_offset + l_tsd_size > a_tsd_total_size || l_offset + l_tsd_size < l_offset) { - log_it(L_WARNING, "Wrong TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - switch (l_tsd->type) { - // set flags - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_SET_FLAGS: { - if (l_tsd->size != sizeof(uint16_t)) { - log_it(L_WARNING, "Wrong SET_FLAGS TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - if (!a_apply) - break; - a_item_apply_to->flags |= dap_tsd_get_scalar(l_tsd, uint16_t); +static void s_dump_datum_tx_for_addr(dap_ledger_tx_item_t *a_item, bool a_unspent, dap_ledger_t *a_ledger, + dap_chain_addr_t *a_addr, const char *a_hash_out_type, json_object *json_arr_out) { + if (a_unspent && a_item->cache_data.ts_spent) { + // With 'unspent' flag spent ones are ignored + return; + } + dap_chain_datum_tx_t *l_tx = a_item->tx; + dap_chain_hash_fast_t l_tx_hash = a_item->tx_hash_fast; + dap_chain_addr_t l_src_addr = { }, l_dst_addr = { }; + bool l_base_tx = false; + const char *l_src_token = NULL; + int l_src_subtype = DAP_CHAIN_TX_OUT_COND_SUBTYPE_UNDEFINED; + dap_hash_fast_t l_tx_prev_hash = { }; + byte_t *l_item; size_t l_size; int idx, l_tx_prev_out_idx; + TX_ITEM_ITER_TX_TYPE(l_item, TX_ITEM_TYPE_IN_ALL, l_size, idx, l_tx) { + switch (*l_item) { + case TX_ITEM_TYPE_IN: { + dap_chain_tx_in_t *l_tx_in = (dap_chain_tx_in_t*)l_item; + l_tx_prev_hash = l_tx_in->header.tx_prev_hash; + l_tx_prev_out_idx = l_tx_in->header.tx_out_prev_idx; } break; - - // unset flags - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_UNSET_FLAGS: { - if (l_tsd->size != sizeof(uint16_t)) { - log_it(L_WARNING, "Wrong UNSET_FLAGS TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - if (!a_apply) - break; - a_item_apply_to->flags &= ~dap_tsd_get_scalar(l_tsd, uint16_t); + case TX_ITEM_TYPE_IN_COND: { + dap_chain_tx_in_cond_t *l_tx_in_cond = (dap_chain_tx_in_cond_t*)l_item; + l_tx_prev_hash = l_tx_in_cond->header.tx_prev_hash; + l_tx_prev_out_idx = l_tx_in_cond->header.tx_out_prev_idx; } break; + default: + continue; + } + if ( dap_hash_fast_is_blank(&l_tx_prev_hash) ) { + l_base_tx = true; + dap_chain_tx_in_ems_t *l_token = (dap_chain_tx_in_ems_t*) + dap_chain_datum_tx_item_get(l_tx, NULL, NULL, TX_ITEM_TYPE_IN_EMS, NULL); + if (l_token) + l_src_token = l_token->header.ticker; + break; + } + dap_chain_datum_tx_t *l_tx_prev = dap_ledger_tx_find_by_hash(a_ledger, &l_tx_prev_hash); + if ( !l_tx_prev ) + continue; + uint8_t *l_prev_out_union = dap_chain_datum_tx_item_get_nth(l_tx_prev, TX_ITEM_TYPE_OUT_ALL, l_tx_prev_out_idx); + if (!l_prev_out_union) + continue; + switch (*l_prev_out_union) { + case TX_ITEM_TYPE_OUT: + l_src_addr = ((dap_chain_tx_out_t *)l_prev_out_union)->addr; + break; + case TX_ITEM_TYPE_OUT_EXT: + l_src_addr = ((dap_chain_tx_out_ext_t *)l_prev_out_union)->addr; + l_src_token = (const char*)(((dap_chain_tx_out_ext_t *)l_prev_out_union)->token); + break; + case TX_ITEM_TYPE_OUT_COND: + l_src_subtype = ((dap_chain_tx_out_cond_t *)l_prev_out_union)->header.subtype; + default: + break; + } + if ( !dap_chain_addr_compare(&l_src_addr, a_addr) ) + break; //it's not our addr + if (!l_src_token) { + l_src_token = a_item->cache_data.token_ticker; + } + } - // set total supply - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SUPPLY: { // 256 - if (l_tsd->size != sizeof(uint256_t)) { - log_it(L_WARNING, "Wrong TOTAL_SUPPLY TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - if (!a_item_apply_to) { - log_it(L_WARNING, "Unexpected TOTAL_SUPPLY TSD section in datum token declaration"); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_FORBIDDEN); - } - uint256_t l_new_supply = dap_tsd_get_scalar(l_tsd, uint256_t); - if (IS_ZERO_256(a_item_apply_to->total_supply)){ - log_it(L_WARNING, "Cannot update total_supply for token %s because the current value is set to infinity.", a_item_apply_to->ticker); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_SUPPLY); + bool l_header_printed = false; + l_item = NULL; + TX_ITEM_ITER_TX_TYPE(l_item, TX_ITEM_TYPE_OUT_ALL, l_size, idx, l_tx) { + dap_chain_tx_item_type_t l_type = *l_item; + uint256_t l_value; + switch (l_type) { + case TX_ITEM_TYPE_OUT: + l_dst_addr = ((dap_chain_tx_out_t*)l_item)->addr; + l_value = ((dap_chain_tx_out_t*)l_item)->header.value; + break; + case TX_ITEM_TYPE_OUT_EXT: + l_dst_addr = ((dap_chain_tx_out_ext_t*)l_item)->addr; + l_value = ((dap_chain_tx_out_ext_t *)l_item)->header.value; + break; + case TX_ITEM_TYPE_OUT_COND: + l_value = ((dap_chain_tx_out_cond_t *)l_item)->header.value; + default: + break; + } + if ( !dap_chain_addr_is_blank(&l_src_addr) && !dap_chain_addr_is_blank(&l_dst_addr) + && dap_chain_addr_compare(&l_dst_addr, &l_src_addr) ) + continue; // send to self + if ( dap_chain_addr_compare(&l_src_addr, a_addr)) { + json_object * l_json_obj_datum = json_object_new_object(); + if (!l_header_printed) { + s_tx_header_print(l_json_obj_datum, l_tx, a_hash_out_type, &l_tx_hash); + l_header_printed = true; } - if (!IS_ZERO_256(l_new_supply) && compare256(a_item_apply_to->total_supply, l_new_supply) > -1) { - log_it(L_WARNING, "Can't update token with ticker '%s' because the new 'total_supply' can't be smaller than the old one", a_item_apply_to->ticker); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_SUPPLY); + //const char *l_token_ticker = dap_ledger_tx_get_token_ticker_by_hash(l_ledger, &l_tx_hash); + const char *l_dst_addr_str = !dap_chain_addr_is_blank(&l_dst_addr) + ? dap_chain_addr_to_str_static(&l_dst_addr) + : dap_chain_tx_out_cond_subtype_to_str( ((dap_chain_tx_out_cond_t *)l_item)->header.subtype ); + json_object_object_add(l_json_obj_datum, "send", json_object_new_string(dap_uint256_to_char(l_value, NULL))); + json_object_object_add(l_json_obj_datum, "to addr", json_object_new_string(l_dst_addr_str)); + json_object_object_add(l_json_obj_datum, "token", l_src_token ? json_object_new_string(l_src_token) : json_object_new_string("UNKNOWN")); + json_object_array_add(json_arr_out, l_json_obj_datum); + } + if ( dap_chain_addr_compare(&l_dst_addr, a_addr) ) { + json_object * l_json_obj_datum = json_object_new_object(); + if (!l_header_printed) { + s_tx_header_print(l_json_obj_datum, l_tx, a_hash_out_type, &l_tx_hash); + l_header_printed = true; } - if (!a_apply) - break; - a_item_apply_to->total_supply = l_new_supply; - } break; + const char *l_dst_token = (l_type == TX_ITEM_TYPE_OUT_EXT) ? + (const char *)(((dap_chain_tx_out_ext_t *)l_item)->token) : NULL; + const char *l_src_addr_str = l_base_tx + ? "emission" + : ( !dap_chain_addr_is_blank(&l_src_addr) + ? dap_chain_addr_to_str_static(&l_src_addr) + : dap_chain_tx_out_cond_subtype_to_str(l_src_subtype) ); + json_object_object_add(l_json_obj_datum, "recv ", json_object_new_string(dap_uint256_to_char(l_value, NULL))); + json_object_object_add(l_json_obj_datum, "token ", l_dst_token ? json_object_new_string(l_dst_token) : + (l_src_token ? json_object_new_string(l_src_token) : json_object_new_string("UNKNOWN"))); + json_object_object_add(l_json_obj_datum, "from ", json_object_new_string(l_src_addr_str)); + json_object_array_add(json_arr_out, l_json_obj_datum); + } + } +} - // Allowed tx receiver addres list add, remove or clear - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_ALLOWED_ADD: { - if (l_tsd->size != sizeof(dap_chain_addr_t)) { - log_it(L_WARNING, "Wrong TX_RECEIVER_ALLOWED_ADD TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - // Check if its correct - dap_chain_addr_t *l_add_addr = dap_tsd_get_object(l_tsd, dap_chain_addr_t); - if (dap_chain_addr_check_sum(l_add_addr)) { - log_it(L_WARNING, "Wrong address checksum in TSD param TX_RECEIVER_ALLOWED_ADD"); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_ADDR); - } - if (!l_new_tx_recv_allow && l_new_tx_recv_allow_size && !l_was_tx_recv_allow_copied) { - assert(a_item_apply_to->tx_recv_allow); - // Deep copy addrs to sandbox - l_new_tx_recv_allow = DAP_DUP_SIZE(a_item_apply_to->tx_recv_allow, l_new_tx_recv_allow_size * sizeof(dap_chain_addr_t)); - if (!l_new_tx_recv_allow) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - } - l_was_tx_recv_allow_copied = true; - // Check if its already present - for (size_t i = 0; i < l_new_tx_recv_allow_size; i++) { // Check for all the list - if (dap_chain_addr_compare(l_new_tx_recv_allow + i, l_add_addr)) { // Found - log_it(L_WARNING, "TSD param TX_RECEIVER_ALLOWED_ADD has address %s thats already present in list", - dap_chain_addr_to_str_static(l_add_addr)); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_ADDR_MISMATCH); - } - } - l_new_tx_recv_allow = l_new_tx_recv_allow - ? DAP_REALLOC(l_new_tx_recv_allow, (l_new_tx_recv_allow_size + 1) * sizeof(dap_chain_addr_t)) - : DAP_NEW_Z(dap_chain_addr_t); - if (!l_new_tx_recv_allow) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - l_new_tx_recv_allow[l_new_tx_recv_allow_size++] = *l_add_addr; - } break; +json_object *dap_ledger_token_tx_item_list(dap_ledger_t * a_ledger, dap_chain_addr_t *a_addr, const char *a_hash_out_type, bool a_unspent_only) +{ + json_object * json_arr_out = json_object_new_array(); + if (!json_arr_out) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return NULL; + } - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_ALLOWED_REMOVE: { - if (l_tsd->size != sizeof(dap_chain_addr_t)) { - log_it(L_WARNING, "Wrong TX_RECEIVER_ALLOWED_REMOVE TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - // Check if its correct - dap_chain_addr_t *l_add_addr = dap_tsd_get_object(l_tsd, dap_chain_addr_t); - if (dap_chain_addr_check_sum(l_add_addr)) { - log_it(L_WARNING, "Wrong address checksum in TSD param TX_RECEIVER_ALLOWED_REMOVE"); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_ADDR); - } - if (!l_new_tx_recv_allow && l_new_tx_recv_allow_size && !l_was_tx_recv_allow_copied) { - assert(a_item_apply_to->tx_recv_allow); - // Deep copy addrs to sandbox - l_new_tx_recv_allow = DAP_DUP_SIZE(a_item_apply_to->tx_recv_allow, l_new_tx_recv_allow_size * sizeof(dap_chain_addr_t)); - if (!l_new_tx_recv_allow) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - } - l_was_tx_recv_allow_copied = true; - // Check if its already present - size_t i = 0; - for ( ; i < l_new_tx_recv_allow_size; i++) // Check for all the list - if (dap_chain_addr_compare(l_new_tx_recv_allow + i, l_add_addr)) - break; - if (i == l_new_tx_recv_allow_size) { - log_it(L_WARNING, "TSD param TX_RECEIVER_ALLOWED_REMOVE has address %s thats not present in list", - dap_chain_addr_to_str_static(l_add_addr)); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_ADDR_MISMATCH); - } - // Addr removing - if (--l_new_tx_recv_allow_size > i) - memmove(l_new_tx_recv_allow + i, l_new_tx_recv_allow + i + 1, - (l_new_tx_recv_allow_size - i - 1) * sizeof(dap_chain_addr_t)); - // Memory clearing - if (l_new_tx_recv_allow_size) - l_new_tx_recv_allow = DAP_REALLOC(l_new_tx_recv_allow, - l_new_tx_recv_allow_size * sizeof(dap_chain_addr_t)); - else - DAP_DEL_Z(l_new_tx_recv_allow); - } break; + dap_ledger_tx_item_t *l_tx_item, *l_tx_tmp; + dap_ledger_private_t * l_ledger_pvt = PVT(a_ledger); - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_ALLOWED_CLEAR: { - if (l_tsd->size != 0) { - log_it(L_WARNING, "Wrong TX_RECEIVER_ALLOWED_CLEAR TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - DAP_DEL_Z(l_new_tx_recv_allow); - l_new_tx_recv_allow_size = 0; - l_was_tx_recv_block_copied = true; - } break; + pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); + HASH_ITER(hh, l_ledger_pvt->ledger_items, l_tx_item, l_tx_tmp) { + s_dump_datum_tx_for_addr(l_tx_item, a_unspent_only, a_ledger, a_addr, a_hash_out_type, json_arr_out); + } + pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); - // Blocked tx receiver addres list add, remove or clear - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_BLOCKED_ADD: { - if (l_tsd->size != sizeof(dap_chain_addr_t)) { - log_it(L_WARNING, "Wrong TX_RECEIVER_BLOCKED_ADD TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - // Check if its correct - dap_chain_addr_t *l_add_addr = dap_tsd_get_object(l_tsd, dap_chain_addr_t); - if (dap_chain_addr_check_sum(l_add_addr)) { - log_it(L_WARNING, "Wrong address checksum in TSD param TX_RECEIVER_BLOCKED_ADD"); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_ADDR); - } - if (!l_new_tx_recv_block && l_new_tx_recv_block_size && !l_was_tx_recv_block_copied) { - assert(a_item_apply_to->tx_recv_block); - // Deep copy addrs to sandbox - l_new_tx_recv_block = DAP_DUP_SIZE(a_item_apply_to->tx_recv_block, l_new_tx_recv_block_size * sizeof(dap_chain_addr_t)); - if (!l_new_tx_recv_block) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - } - l_was_tx_recv_block_copied = true; - // Check if its already present - for (size_t i = 0; i < l_new_tx_recv_block_size; i++) { // Check for all the list - if (dap_chain_addr_compare(l_new_tx_recv_block + i, l_add_addr)) { // Found - log_it(L_WARNING, "TSD param TX_RECEIVER_BLOCKED_ADD has address %s thats already present in list", - dap_chain_addr_to_str_static(l_add_addr)); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_ADDR_MISMATCH); - } - } - l_new_tx_recv_block = l_new_tx_recv_block - ? DAP_REALLOC(l_new_tx_recv_block, (l_new_tx_recv_block_size + 1) * sizeof(dap_chain_addr_t)) - : DAP_NEW_Z(dap_chain_addr_t); - if (!l_new_tx_recv_block) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - l_new_tx_recv_block[l_new_tx_recv_block_size++] = *l_add_addr; - } break; + // if no history + if(!json_arr_out) + { + json_object * json_obj_addr = json_object_new_object(); + json_object_object_add(json_obj_addr, "status:", json_object_new_string("empty")); + json_object_array_add(json_arr_out, json_obj_addr); + } + return json_arr_out; +} - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_BLOCKED_REMOVE: { - if (l_tsd->size != sizeof(dap_chain_addr_t)) { - log_it(L_WARNING, "Wrong TX_RECEIVER_BLOCKED_REMOVE TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - // Check if its correct - dap_chain_addr_t *l_add_addr = dap_tsd_get_object(l_tsd, dap_chain_addr_t); - if (dap_chain_addr_check_sum(l_add_addr)) { - log_it(L_WARNING, "Wrong address checksum in TSD param TX_RECEIVER_BLOCKED_REMOVE"); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_ADDR); - } - if (!l_new_tx_recv_block && l_new_tx_recv_block_size && !l_was_tx_recv_block_copied) { - assert(a_item_apply_to->tx_recv_block); - // Deep copy addrs to sandbox - l_new_tx_recv_block = DAP_DUP_SIZE(a_item_apply_to->tx_recv_block, l_new_tx_recv_block_size * sizeof(dap_chain_addr_t)); - if (!l_new_tx_recv_block) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - } - l_was_tx_recv_block_copied = true; - // Check if its already present - size_t i = 0; - for ( ; i < l_new_tx_recv_block_size; i++) // Check for all the list - if (dap_chain_addr_compare(l_new_tx_recv_block + i, l_add_addr)) - break; - if (i == l_new_tx_recv_block_size) { - log_it(L_WARNING, "TSD param TX_RECEIVER_BLOCKED_REMOVE has address %s thats not present in list", - dap_chain_addr_to_str_static(l_add_addr)); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_ADDR_MISMATCH); - } - // Addr removing - if (--l_new_tx_recv_block_size > i) - memmove(l_new_tx_recv_block + i, l_new_tx_recv_block + i + 1, - (l_new_tx_recv_block_size - i - 1) * sizeof(dap_chain_addr_t)); - // Memory clearing - if (l_new_tx_recv_block_size) - l_new_tx_recv_block = DAP_REALLOC(l_new_tx_recv_block, - l_new_tx_recv_block_size * sizeof(dap_chain_addr_t)); - else - DAP_DEL_Z(l_new_tx_recv_block); - } break; - - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_BLOCKED_CLEAR: { - if (l_tsd->size != 0) { - log_it(L_WARNING, "Wrong TX_RECEIVER_BLOCKED_CLEAR TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - DAP_DEL_Z(l_new_tx_recv_block); - l_new_tx_recv_block_size = 0; - l_was_tx_recv_block_copied = true; - } break; - - // Blocked tx sender addres list add, remove or clear - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_ALLOWED_ADD: { - if (l_tsd->size != sizeof(dap_chain_addr_t)) { - log_it(L_WARNING, "Wrong TX_SENDER_ALLOWED_ADD TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - // Check if its correct - dap_chain_addr_t *l_add_addr = dap_tsd_get_object(l_tsd, dap_chain_addr_t); - if (dap_chain_addr_check_sum(l_add_addr)) { - log_it(L_WARNING, "Wrong address checksum in TSD param TX_SENDER_ALLOWED_ADD"); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_ADDR); - } - if (!l_new_tx_send_allow && l_new_tx_send_allow_size && !l_was_tx_send_allow_copied) { - assert(a_item_apply_to->tx_send_allow); - // Deep copy addrs to sandbox - l_new_tx_send_allow = DAP_DUP_SIZE(a_item_apply_to->tx_send_allow, l_new_tx_send_allow_size * sizeof(dap_chain_addr_t)); - if (!l_new_tx_send_allow) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - } - l_was_tx_send_allow_copied = true; - // Check if its already present - for (size_t i = 0; i < l_new_tx_send_allow_size; i++) { // Check for all the list - if (dap_chain_addr_compare(l_new_tx_send_allow + i, l_add_addr)) { // Found - log_it(L_WARNING, "TSD param TX_SENDER_ALLOWED_ADD has address %s thats already present in list", - dap_chain_addr_to_str_static(l_add_addr)); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_ADDR_MISMATCH); - } - } - l_new_tx_send_allow = l_new_tx_send_allow - ? DAP_REALLOC(l_new_tx_send_allow, (l_new_tx_send_allow_size + 1) * sizeof(dap_chain_addr_t)) - : DAP_NEW_Z(dap_chain_addr_t); - if (!l_new_tx_send_allow) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - l_new_tx_send_allow[l_new_tx_send_allow_size++] = *l_add_addr; - } break; - - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_ALLOWED_REMOVE: { - if (l_tsd->size != sizeof(dap_chain_addr_t)) { - log_it(L_WARNING, "Wrong TX_SENDER_ALLOWED_REMOVE TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - // Check if its correct - dap_chain_addr_t *l_add_addr = dap_tsd_get_object(l_tsd, dap_chain_addr_t); - if (dap_chain_addr_check_sum(l_add_addr)) { - log_it(L_WARNING, "Wrong address checksum in TSD param TX_SENDER_ALLOWED_REMOVE"); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_ADDR); - - } - if (!l_new_tx_send_allow && l_new_tx_send_allow_size && !l_was_tx_send_allow_copied) { - assert(a_item_apply_to->tx_send_allow); - // Deep copy addrs to sandbox - l_new_tx_send_allow = DAP_DUP_SIZE(a_item_apply_to->tx_send_allow, l_new_tx_send_allow_size * sizeof(dap_chain_addr_t)); - if (!l_new_tx_send_allow) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - } - l_was_tx_send_allow_copied = true; - // Check if its already present - size_t i = 0; - for ( ; i < l_new_tx_send_allow_size; i++) // Check for all the list - if (dap_chain_addr_compare(l_new_tx_send_allow + i, l_add_addr)) - break; - if (i == l_new_tx_send_allow_size) { - log_it(L_WARNING, "TSD param TX_SENDER_ALLOWED_REMOVE has address %s thats not present in list", - dap_chain_addr_to_str_static(l_add_addr)); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_ADDR_MISMATCH); - } - // Addr removing - if (--l_new_tx_send_allow_size > i) - memmove(l_new_tx_send_allow + i, l_new_tx_send_allow + i + 1, - (l_new_tx_send_allow_size - i - 1) * sizeof(dap_chain_addr_t)); - // Memory clearing - if (l_new_tx_send_allow_size) - l_new_tx_send_allow = DAP_REALLOC(l_new_tx_send_allow, - l_new_tx_send_allow_size * sizeof(dap_chain_addr_t)); - else - DAP_DEL_Z(l_new_tx_send_allow); - } break; - - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_ALLOWED_CLEAR: { - if (l_tsd->size != 0) { - log_it(L_WARNING, "Wrong TX_SENDER_ALLOWED_CLEAR TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - DAP_DEL_Z(l_new_tx_send_allow); - l_new_tx_send_allow_size = 0; - l_was_tx_send_allow_copied = true; - } break; - - // Blocked tx sender addres list add, remove or clear - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_BLOCKED_ADD: { - if (l_tsd->size != sizeof(dap_chain_addr_t)) { - log_it(L_WARNING, "Wrong TX_SENDER_BLOCKED_ADD TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - // Check if its correct - dap_chain_addr_t *l_add_addr = dap_tsd_get_object(l_tsd, dap_chain_addr_t); - if (dap_chain_addr_check_sum(l_add_addr)) { - log_it(L_WARNING, "Wrong address checksum in TSD param TX_SENDER_BLOCKED_ADD"); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_ADDR); - } - if (!l_new_tx_send_block && l_new_tx_send_block_size && !l_was_tx_send_block_copied) { - assert(a_item_apply_to->tx_send_block); - // Deep copy addrs to sandbox - l_new_tx_send_block = DAP_DUP_SIZE(a_item_apply_to->tx_send_block, l_new_tx_send_block_size * sizeof(dap_chain_addr_t)); - if (!l_new_tx_send_block) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - } - l_was_tx_send_block_copied = true; - // Check if its already present - for (size_t i = 0; i < l_new_tx_send_block_size; i++) { // Check for all the list - if (dap_chain_addr_compare(l_new_tx_send_block + i, l_add_addr)) { // Found - log_it(L_WARNING, "TSD param TX_SENDER_BLOCKED_ADD has address %s thats already present in list", - dap_chain_addr_to_str_static(l_add_addr)); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_ADDR_MISMATCH); - } - } - if (!a_apply) - break; - l_new_tx_send_block = l_new_tx_send_block - ? DAP_REALLOC(l_new_tx_send_block, (l_new_tx_send_block_size + 1) * sizeof(dap_chain_addr_t)) - : DAP_NEW_Z(dap_chain_addr_t); - if (!l_new_tx_send_block) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - l_new_tx_send_block[l_new_tx_send_block_size++] = *l_add_addr; - } break; - - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_BLOCKED_REMOVE: { - if (l_tsd->size != sizeof(dap_chain_addr_t)) { - log_it(L_WARNING, "Wrong TX_SENDER_BLOCKED_REMOVE TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - // Check if its correct - dap_chain_addr_t *l_add_addr = dap_tsd_get_object(l_tsd, dap_chain_addr_t); - if (dap_chain_addr_check_sum(l_add_addr)) { - log_it(L_WARNING, "Wrong address checksum in TSD param TX_SENDER_BLOCKED_REMOVE"); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_ADDR); - } - if (!l_new_tx_send_block && l_new_tx_send_block_size && !l_was_tx_send_block_copied) { - assert(a_item_apply_to->tx_send_block); - // Deep copy addrs to sandbox - l_new_tx_send_block = DAP_DUP_SIZE(a_item_apply_to->tx_send_block, l_new_tx_send_block_size * sizeof(dap_chain_addr_t)); - if (!l_new_tx_send_block) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - } - l_was_tx_send_block_copied = true; - // Check if its already present - size_t i = 0; - for ( ; i < l_new_tx_send_block_size; i++) // Check for all the list - if (dap_chain_addr_compare(l_new_tx_send_block + i, l_add_addr)) - break; - if (i == l_new_tx_send_block_size) { - log_it(L_WARNING, "TSD param TX_SENDER_BLOCKED_REMOVE has address %s thats not present in list", - dap_chain_addr_to_str_static(l_add_addr)); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_ADDR_MISMATCH); - } - // Addr removing - if (--l_new_tx_send_block_size > i) - memmove(l_new_tx_send_block + i, l_new_tx_send_block + i + 1, - (l_new_tx_send_block_size - i - 1) * sizeof(dap_chain_addr_t)); - // Memory clearing - if (l_new_tx_send_block_size) - l_new_tx_send_block = DAP_REALLOC(l_new_tx_send_block, - l_new_tx_send_block_size * sizeof(dap_chain_addr_t)); - else - DAP_DEL_Z(l_new_tx_send_block); - } break; - - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_BLOCKED_CLEAR: { - if (l_tsd->size != 0) { - log_it(L_WARNING, "Wrong TX_SENDER_BLOCKED_CLEAR TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - DAP_DEL_Z(l_new_tx_send_block); - l_new_tx_send_block_size = 0; - l_was_tx_send_block_copied = true; - } break; - - case DAP_CHAIN_DATUM_TOKEN_TSD_TOKEN_DESCRIPTION: { - if (l_tsd->size == 0 || l_tsd->data[l_tsd->size - 1] != 0) { - log_it(L_ERROR, "Wrong TOKEN_DESCRIPTION TSD format or size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - if (!a_apply) - break; - DAP_DEL_Z(a_item_apply_to->description); - a_item_apply_to->description = strdup((char *)l_tsd->data); - } break; - - // Set signs count value need to emission be valid - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_VALID: { - if (l_tsd->size != sizeof(uint16_t)) { - log_it(L_WARNING, "Wrong SIGNS_VALID TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - l_new_signs_valid = dap_tsd_get_scalar(l_tsd, uint16_t); - } break; - - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_ADD: { - if (l_tsd->size < sizeof(dap_pkey_t) || l_tsd->size != dap_pkey_get_size((dap_pkey_t *)l_tsd->data)) { - log_it(L_WARNING, "Wrong TOTAL_PKEYS_ADD TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - if (!l_new_pkeys && l_new_signs_total && !l_was_pkeys_copied) { - assert(a_item_apply_to->auth_pkeys); - assert(a_item_apply_to->auth_pkey_hashes); - // Deep copy pkeys & its hashes to sandbox - l_new_pkeys = DAP_NEW_SIZE(dap_pkey_t *, l_new_signs_total * sizeof(dap_pkey_t *)); - if (!l_new_pkeys) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - for (size_t i = 0; i < l_new_signs_total; i++) { - l_new_pkeys[i] = DAP_DUP_SIZE(a_item_apply_to->auth_pkeys[i], dap_pkey_get_size(a_item_apply_to->auth_pkeys[i])); - if (!l_new_pkeys[i]) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - } - assert(!l_new_pkey_hashes); - l_new_pkey_hashes = DAP_DUP_SIZE(a_item_apply_to->auth_pkey_hashes, l_new_signs_total * sizeof(dap_hash_t)); - if (!l_new_pkey_hashes) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - } - l_was_pkeys_copied = true; - dap_pkey_t *l_new_auth_pkey = dap_tsd_get_object(l_tsd, dap_pkey_t); - dap_pkey_type_t l_pkey_type_correction = { .type = DAP_PKEY_TYPE_NULL }; - if (dap_pkey_type_to_enc_key_type(l_new_auth_pkey->header.type) == DAP_ENC_KEY_TYPE_INVALID) { - dap_sign_type_t l_sign_type = { .type = l_new_auth_pkey->header.type.type }; // Legacy cratch - l_pkey_type_correction = dap_pkey_type_from_sign_type(l_sign_type); - if (l_pkey_type_correction.type == DAP_PKEY_TYPE_NULL) { - log_it(L_WARNING, "Unknonw public key type %hu", l_new_auth_pkey->header.type.type); - return m_ret_cleanup(DAP_LEDGER_CHECK_PARSE_ERROR); - } - } - // Check if its already present - dap_hash_t l_new_auth_pkey_hash; - dap_pkey_get_hash(l_new_auth_pkey, &l_new_auth_pkey_hash); - for (size_t i = 0; i < l_new_signs_total; i++) { - if (dap_pkey_compare(l_new_auth_pkey, l_new_pkeys[i])) { - log_it(L_WARNING, "TSD param TOTAL_PKEYS_ADD has pkey %s thats already present in list", - dap_hash_fast_to_str_static(&l_new_auth_pkey_hash)); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_PKEY_MISMATCH); - } - } - l_new_pkeys = l_new_pkeys ? DAP_REALLOC(l_new_pkeys, (l_new_signs_total + 1) * sizeof(dap_pkey_t *)) - : DAP_NEW_Z(dap_pkey_t *); - if (!l_new_pkeys) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - // Pkey adding - l_new_pkeys[l_new_signs_total] = DAP_DUP_SIZE(l_new_auth_pkey, dap_pkey_get_size(l_new_auth_pkey)); - if (!l_new_pkeys[l_new_signs_total]) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - if (l_pkey_type_correction.type != DAP_PKEY_TYPE_NULL) - l_new_pkeys[l_new_signs_total]->header.type = l_pkey_type_correction; - - l_new_pkey_hashes = l_new_pkey_hashes ? DAP_REALLOC(l_new_pkey_hashes, (l_new_signs_total + 1) * sizeof(dap_hash_t)) - : DAP_NEW_Z(dap_hash_t); - if (!l_new_pkey_hashes) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - l_new_pkey_hashes[l_new_signs_total++] = l_new_auth_pkey_hash; - } break; - - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_REMOVE: { - if (l_tsd->size != sizeof(dap_hash_t)) { - log_it(L_WARNING, "Wrong TOTAL_PKEYS_REMOVE TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - if (!l_new_pkeys && l_new_signs_total && !l_was_pkeys_copied) { - assert(a_item_apply_to->auth_pkeys); - assert(a_item_apply_to->auth_pkey_hashes); - // Deep copy pkeys & its hashes to sandbox - l_new_pkeys = DAP_NEW_SIZE(dap_pkey_t *, l_new_signs_total * sizeof(dap_pkey_t *)); - if (!l_new_pkeys) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - for (size_t i = 0; i < l_new_signs_total; i++) { - l_new_pkeys[i] = DAP_DUP_SIZE(a_item_apply_to->auth_pkeys[i], dap_pkey_get_size(a_item_apply_to->auth_pkeys[i])); - if (!l_new_pkeys[i]) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - } - assert(!l_new_pkey_hashes); - l_new_pkey_hashes = DAP_DUP_SIZE(a_item_apply_to->auth_pkey_hashes, l_new_signs_total * sizeof(dap_hash_t)); - if (!l_new_pkey_hashes) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - } - } - l_was_pkeys_copied = true; - dap_hash_t l_new_auth_pkey_hash = dap_tsd_get_scalar(l_tsd, dap_hash_t); - // Check if its already present - size_t i = 0; - for ( ; i < l_new_signs_total; i++) // Check for all the list - if (dap_hash_fast_compare(l_new_pkey_hashes + i, &l_new_auth_pkey_hash)) - break; - if (i == l_new_signs_total) { - log_it(L_WARNING, "TSD param TOTAL_PKEYS_REMOVE has public key hash %s thats not present in list", - dap_hash_fast_to_str_static(&l_new_auth_pkey_hash)); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_PKEY_MISMATCH); - } - // Pkey removing - DAP_DELETE(l_new_pkeys[i]); - if (--l_new_signs_total > i) { - memmove(l_new_pkeys + i, l_new_pkeys + i + 1, (l_new_signs_total - i - 1) * sizeof(dap_pkey_t *)); - memmove(l_new_pkey_hashes + i, l_new_pkey_hashes + i + 1, (l_new_signs_total - i - 1) * sizeof(dap_hash_t)); - } - // Memory clearing - if (l_new_signs_total) { - l_new_pkeys = DAP_REALLOC(l_new_pkeys, l_new_signs_total * sizeof(dap_pkey_t *)); - l_new_pkey_hashes = DAP_REALLOC(l_new_pkey_hashes, l_new_signs_total * sizeof(dap_hash_t)); - } else - DAP_DEL_MULTY(l_new_pkeys, l_new_pkey_hashes); - } break; - - case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DELEGATE_EMISSION_FROM_STAKE_LOCK: { - if (a_current_datum->subtype != DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE) { - log_it(L_WARNING, "TSD section DELEGATE_EMISSION_FROM_STAKE_LOCK allowed for NATIVE subtype only"); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_FORBIDDEN); - } - if (l_tsd->size != sizeof(dap_chain_datum_token_tsd_delegate_from_stake_lock_t) && - l_tsd->size != sizeof(dap_chain_datum_token_tsd_delegate_from_stake_lock_t) + 256 /* Legacy size */) { - log_it(L_WARNING, "Wrong DELEGATE_EMISSION_FROM_STAKE_LOCK TSD size %zu, exiting TSD parse", l_tsd_size); - return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); - } - dap_chain_datum_token_tsd_delegate_from_stake_lock_t *l_delegate = dap_tsd_get_object(l_tsd, dap_chain_datum_token_tsd_delegate_from_stake_lock_t); - const char *l_basic_token_ticker = (const char *)l_delegate->ticker_token_from; - char l_delegated_ticker[DAP_CHAIN_TICKER_SIZE_MAX]; - dap_chain_datum_token_get_delegated_ticker(l_delegated_ticker, l_basic_token_ticker); - if (dap_strcmp(l_delegated_ticker, a_current_datum->ticker)) { - log_it(L_WARNING, "Unexpected delegated token ticker %s (expected %s)", a_current_datum->ticker, l_delegated_ticker); - return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_OTHER_TICKER_EXPECTED); - } - dap_ledger_token_item_t *l_basic_token = NULL; - HASH_FIND_STR(PVT(a_ledger)->tokens, l_basic_token_ticker, l_basic_token); - if (!l_basic_token) { - log_it(L_WARNING, "Basic token ticker %s for delegated token isn't found", l_basic_token_ticker); - return m_ret_cleanup(DAP_LEDGER_CHECK_TICKER_NOT_FOUND); - } - if (IS_ZERO_256(l_delegate->emission_rate)) { - log_it(L_WARNING, "Emission rate for delegated toke should not be a zero"); - return m_ret_cleanup(DAP_LEDGER_CHECK_ZERO_VALUE); - } - if (!a_apply) - break; - assert(a_item_apply_to); - a_item_apply_to->is_delegated = true; - strcpy(a_item_apply_to->delegated_from, l_basic_token->ticker); - a_item_apply_to->emission_rate = l_delegate->emission_rate; - } break; - - default: - log_it(L_ERROR, "Unexpected TSD type %hu", l_tsd->type); - return m_ret_cleanup(DAP_LEDGER_CHECK_PARSE_ERROR); - } - } - if (l_new_signs_total < l_new_signs_valid) - return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_VALID_SIGNS); - - if (!a_apply) - return m_ret_cleanup(DAP_LEDGER_CHECK_OK); -#undef m_ret_cleanup - - if (l_was_tx_recv_allow_copied) { - a_item_apply_to->tx_recv_allow_size = l_new_tx_recv_allow_size; - DAP_DEL_Z(a_item_apply_to->tx_recv_allow); - a_item_apply_to->tx_recv_allow = l_new_tx_recv_allow; - } - if (l_was_tx_recv_block_copied) { - a_item_apply_to->tx_recv_block_size = l_new_tx_recv_block_size; - DAP_DEL_Z(a_item_apply_to->tx_recv_block); - a_item_apply_to->tx_recv_block = l_new_tx_recv_block; - } - if (l_was_tx_send_allow_copied) { - a_item_apply_to->tx_send_allow_size = l_new_tx_send_allow_size; - DAP_DEL_Z(a_item_apply_to->tx_send_allow); - a_item_apply_to->tx_send_allow = l_new_tx_send_allow; - } - if (l_was_tx_send_block_copied) { - a_item_apply_to->tx_send_block_size = l_new_tx_send_block_size; - DAP_DEL_Z(a_item_apply_to->tx_send_block); - a_item_apply_to->tx_send_block = l_new_tx_send_block; - } - a_item_apply_to->auth_signs_valid = l_new_signs_valid; - if (l_was_pkeys_copied) { - for (size_t i = 0; i < a_item_apply_to->auth_signs_total; i++) - DAP_DELETE(a_item_apply_to->auth_pkeys[i]); - DAP_DEL_Z(a_item_apply_to->auth_pkeys); - DAP_DEL_Z(a_item_apply_to->auth_pkey_hashes); - a_item_apply_to->auth_signs_total = l_new_signs_total; - a_item_apply_to->auth_pkeys = l_new_pkeys; - a_item_apply_to->auth_pkey_hashes = l_new_pkey_hashes; - } - return DAP_LEDGER_CHECK_OK; -} - -/** - * @brief dap_ledger_token_check - * @param a_ledger - * @param a_token - * @param a_token_size - * @return - */ -int s_token_add_check(dap_ledger_t *a_ledger, byte_t *a_token, size_t a_token_size, - dap_ledger_token_item_t **a_token_item, dap_chain_datum_token_t **a_token_out, - size_t *a_tsd_total_size, size_t *a_signs_size, - dap_hash_fast_t *a_token_update_hash) -{ - size_t l_token_size = a_token_size; - dap_chain_datum_token_t *l_token = dap_chain_datum_token_read(a_token, &l_token_size); - if (!l_token) - return DAP_LEDGER_CHECK_INVALID_SIZE; - bool l_legacy_type = a_token_size != l_token_size; - if (l_legacy_type && !a_token_item) { // It's mempool check - log_it(L_WARNING, "Legacy token type %hu isn't supported for a new declaration", l_token->type); - DAP_DELETE(l_token); - return DAP_LEDGER_TOKEN_ADD_CHECK_LEGACY_FORBIDDEN; - } - if (l_token->type != DAP_CHAIN_DATUM_TOKEN_TYPE_UPDATE && l_token->type != DAP_CHAIN_DATUM_TOKEN_TYPE_DECL) { - log_it(L_WARNING, "Unknown token type %hu", l_token->type); - DAP_DELETE(l_token); - return DAP_LEDGER_CHECK_PARSE_ERROR; - } - if (!l_token->ticker[0] || l_token->ticker[DAP_CHAIN_TICKER_SIZE_MAX - 1]) { - log_it(L_WARNING, "Unreadable token ticker"); - DAP_DELETE(l_token); - return DAP_LEDGER_CHECK_PARSE_ERROR; - } - char *ptr = l_token->ticker; - while (*ptr) { - if (!dap_ascii_isalnum(*ptr++)) { - log_it(L_WARNING, "Token ticker is not alpha-numeric"); - DAP_DELETE(l_token); - return DAP_LEDGER_CHECK_PARSE_ERROR; - } - } - if (!l_token->signs_total) { - log_it(L_WARNING, "No auth signs in token '%s' datum!", l_token->ticker); - DAP_DELETE(l_token); - return DAP_LEDGER_TOKEN_ADD_CHECK_NOT_ENOUGH_UNIQUE_SIGNS; - } - bool l_update_token = l_token->type == DAP_CHAIN_DATUM_TOKEN_TYPE_UPDATE; - dap_ledger_token_item_t *l_token_item = s_ledger_find_token(a_ledger, l_token->ticker); - dap_hash_fast_t l_token_update_hash = {}; - if (l_token_item) { - if (!l_update_token) { - log_it(L_WARNING, "Duplicate token declaration for ticker '%s'", l_token->ticker); - DAP_DELETE(l_token); - return DAP_LEDGER_CHECK_ALREADY_CACHED; - } - if (l_token->signs_total < l_token_item->auth_signs_valid) { - log_it(L_WARNING, "Datum token for ticker '%s' has only %hu signatures out of %zu", - l_token->ticker, l_token->signs_total, l_token_item->auth_signs_valid); - DAP_DELETE(l_token); - return DAP_LEDGER_TOKEN_ADD_CHECK_NOT_ENOUGH_UNIQUE_SIGNS; - } - dap_hash_fast(l_token, l_token_size, &l_token_update_hash); - dap_ledger_token_update_item_t *l_token_update_item = NULL; - pthread_rwlock_rdlock(&l_token_item->token_ts_updated_rwlock); - HASH_FIND(hh, l_token_item->token_ts_updated, &l_token_update_hash, sizeof(dap_hash_fast_t), l_token_update_item); - pthread_rwlock_unlock(&l_token_item->token_ts_updated_rwlock); - if (l_token_update_item) { - log_it(L_WARNING, "This update for token '%s' was already applied", l_token->ticker); - DAP_DELETE(l_token); - return DAP_LEDGER_CHECK_ALREADY_CACHED; - } - if (a_token_update_hash) - *a_token_update_hash = l_token_update_hash; - } else if (l_update_token) { - log_it(L_WARNING, "Can't update token that doesn't exist for ticker '%s'", l_token->ticker); - DAP_DELETE(l_token); - return DAP_LEDGER_CHECK_TICKER_NOT_FOUND; - } else if (l_token->signs_total < l_token->signs_valid) { - log_it(L_WARNING, "Datum token for ticker '%s' has only %hu signatures out of %hu", - l_token->ticker, l_token->signs_total, l_token->signs_valid); - DAP_DELETE(l_token); - return DAP_LEDGER_TOKEN_ADD_CHECK_NOT_ENOUGH_UNIQUE_SIGNS; - } - // Check TSD - size_t l_size_tsd_section = 0; - if (l_update_token) { - switch (l_token->subtype) { - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PRIVATE: - l_size_tsd_section = l_token->header_private_decl.tsd_total_size; break; - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE: - l_size_tsd_section = l_token->header_native_decl.tsd_total_size; break; - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_SIMPLE: - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PUBLIC: - break; - default: - /* Bogdanoff, unknown token subtype update. What shall we TODO? */ - log_it(L_WARNING, "Unknown token subtype '0x%0hX' update! Ticker: %s, total_supply: %s, signs_valid: %hu, signs_total: %hu", - l_token->type, l_token->ticker, dap_uint256_to_char(l_token->total_supply, NULL), - l_token->signs_valid, l_token->signs_total); - /* Dump it right now */ - DAP_DELETE(l_token); - return DAP_LEDGER_CHECK_PARSE_ERROR; - } - } else { - switch (l_token->subtype) { - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PRIVATE: - l_size_tsd_section = l_token->header_private_update.tsd_total_size; break; - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE: - l_size_tsd_section = l_token->header_native_update.tsd_total_size; break; - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_SIMPLE: - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PUBLIC: - break; - default: - /* Bogdanoff, unknown token subtype declaration. What shall we TODO? */ - log_it(L_WARNING, "Unknown token subtype '0x%0hX' declaration! Ticker: %s, total_supply: %s, signs_valid: %hu, signs_total: %hu", - l_token->type, l_token->ticker, dap_uint256_to_char(l_token->total_supply, NULL), - l_token->signs_valid, l_token->signs_total); - /* Dump it right now */ - DAP_DELETE(l_token); - return DAP_LEDGER_CHECK_PARSE_ERROR; - } - } - if (sizeof(dap_chain_datum_token_t) + l_size_tsd_section > l_token_size || - sizeof(dap_chain_datum_token_t) + l_size_tsd_section < l_size_tsd_section) { - log_it(L_WARNING, "Incorrect size %zu of datum token, expected at least %zu", l_token_size, - sizeof(dap_chain_datum_token_t) + l_size_tsd_section); - DAP_DELETE(l_token); - return DAP_LEDGER_CHECK_INVALID_SIZE; - } - // Check signs - byte_t *l_signs_ptr = l_token->tsd_n_signs + l_size_tsd_section; - uint64_t l_signs_size = 0, l_signs_offset = sizeof(dap_chain_datum_token_t) + l_size_tsd_section; - for (uint16_t l_signs_passed = 0; l_signs_passed < l_token->signs_total; l_signs_passed++) { - dap_sign_t *l_sign = (dap_sign_t *)(l_signs_ptr + l_signs_size); - if (l_signs_offset + l_signs_size + sizeof(dap_sign_t) > l_token_size || - l_signs_offset + l_signs_size + sizeof(dap_sign_t) < l_signs_offset) { - log_it(L_WARNING, "Incorrect size %zu of datum token, expected at least %zu", l_token_size, - l_signs_offset + l_signs_size + sizeof(dap_sign_t)); - DAP_DELETE(l_token); - return DAP_LEDGER_CHECK_INVALID_SIZE; - } - uint64_t l_sign_size = dap_sign_get_size(l_sign); - if (!l_sign_size || l_sign_size + l_signs_size < l_signs_size) { - log_it(L_WARNING, "Incorrect size %zu of datum token sign", l_sign_size); - DAP_DELETE(l_token); - return DAP_LEDGER_CHECK_INVALID_SIZE; - } - l_signs_size += l_sign_size; - } - if (l_token_size != l_signs_offset + l_signs_size) { - log_it(L_WARNING, "Incorrect size %zu of datum token, expected %zu", l_token_size, l_signs_offset + l_signs_size); - DAP_DELETE(l_token); - return DAP_LEDGER_CHECK_INVALID_SIZE; - } - size_t l_signs_unique = l_token->signs_total; - dap_sign_t **l_signs = dap_sign_get_unique_signs(l_signs_ptr, l_signs_size, &l_signs_unique); - if (l_signs_unique != l_token->signs_total) { - DAP_DEL_Z(l_signs); - log_it(L_WARNING, "The number of unique token signs %zu is less than total token signs set to %hu", - l_signs_unique, l_token->signs_total); - DAP_DELETE(l_token); - return DAP_LEDGER_TOKEN_ADD_CHECK_NOT_ENOUGH_UNIQUE_SIGNS; - } - size_t l_signs_approve = 0; - size_t l_verify_size = 0; - uint16_t l_tmp_auth_signs = 0; - if (l_legacy_type) - l_verify_size = sizeof(dap_chain_datum_token_old_t) - sizeof(uint16_t); - else { - l_verify_size = l_signs_offset; - l_tmp_auth_signs = l_token->signs_total; - l_token->signs_total = 0; - } - for (size_t i = 0; i < l_signs_unique; i++) { - if (!dap_sign_verify(l_signs[i], l_legacy_type ? a_token : (void *)l_token, l_verify_size)) { - if (l_update_token) { - for (size_t j = 0; j < l_token_item->auth_signs_total; j++) { - if (dap_pkey_compare_with_sign(l_token_item->auth_pkeys[j], l_signs[i])) { - l_signs_approve++; - break; - } - } - } else - l_signs_approve++; - } - } - DAP_DELETE(l_signs); - if (!l_legacy_type) - l_token->signs_total = l_tmp_auth_signs; - size_t l_signs_need = l_update_token ? l_token_item->auth_signs_valid : l_token->signs_total; - if (l_signs_approve < l_signs_need) { - log_it(L_WARNING, "Datum token for ticker '%s' has only %zu valid signatures out of %zu", - l_token->ticker, l_signs_approve, l_signs_need); - DAP_DELETE(l_token); - return DAP_LEDGER_CHECK_NOT_ENOUGH_VALID_SIGNS; - } - // Check content & size of enclosed TSD sections - pthread_rwlock_rdlock(&PVT(a_ledger)->tokens_rwlock); - int ret = s_token_tsd_parse(l_token_item, l_token, a_ledger, l_token->tsd_n_signs, l_size_tsd_section, false); - pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); - dap_ledger_hal_item_t *l_hash_found = NULL; - if (ret != DAP_LEDGER_CHECK_OK) { - if (PVT(a_ledger)->hal_items) { - dap_hash_fast_t l_token_hash; - if (!dap_hash_fast_is_blank(&l_token_update_hash)) - l_token_hash = l_token_update_hash; - else - dap_hash_fast(a_token, a_token_size, &l_token_hash); - l_hash_found = s_check_hal(a_ledger, &l_token_hash); - } - if (!l_hash_found) { - DAP_DELETE(l_token); - return ret; - } - } - if (a_token_item) - *a_token_item = l_token_item; - if (a_token_out) - *a_token_out = l_token; - else - DAP_DELETE(l_token); - if (a_tsd_total_size) - *a_tsd_total_size = l_size_tsd_section; - if (a_signs_size) - *a_signs_size = l_signs_size; - return l_hash_found ? DAP_LEDGER_CHECK_WHITELISTED : DAP_LEDGER_CHECK_OK; -} - -int dap_ledger_token_add_check(dap_ledger_t *a_ledger, byte_t *a_token, size_t a_token_size) -{ - dap_return_val_if_fail(a_ledger && a_token && a_token_size, DAP_LEDGER_CHECK_INVALID_ARGS); - int ret = s_token_add_check(a_ledger, a_token, a_token_size, NULL, NULL, NULL, NULL, NULL); - if (ret == DAP_LEDGER_CHECK_WHITELISTED) - ret = DAP_LEDGER_CHECK_OK; - return ret; -} - -/** - * @brief dap_ledger_token_ticker_check - * @param a_ledger - * @param a_token_ticker - * @return - */ -dap_chain_datum_token_t *dap_ledger_token_ticker_check(dap_ledger_t *a_ledger, const char *a_token_ticker) -{ - dap_return_val_if_fail(a_ledger && a_token_ticker, NULL); - dap_ledger_token_item_t *l_token_item = s_ledger_find_token(a_ledger, a_token_ticker); - return l_token_item ? l_token_item->datum_token : NULL; -} - -/** - * @brief update current_supply in token cache - * - * @param a_ledger ledger object - * @param l_token_item token item object - */ -void s_ledger_token_cache_update(dap_ledger_t *a_ledger, dap_ledger_token_item_t *l_token_item) -{ - if (!PVT(a_ledger)->cached) - return; - char *l_gdb_group = dap_ledger_get_gdb_group(a_ledger, DAP_LEDGER_TOKENS_STR); - size_t l_cache_size = l_token_item->datum_token_size + sizeof(uint256_t); - uint8_t *l_cache = DAP_NEW_STACK_SIZE(uint8_t, l_cache_size); - if ( !l_cache ) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return; - } - memcpy(l_cache, &l_token_item->current_supply, sizeof(uint256_t)); - memcpy(l_cache + sizeof(uint256_t), l_token_item->datum_token, l_token_item->datum_token_size); - if (dap_global_db_set(l_gdb_group, l_token_item->ticker, l_cache, l_cache_size, false, NULL, NULL)) { - char *l_supply = dap_chain_balance_datoshi_print(l_token_item->current_supply); - log_it(L_WARNING, "Ledger cache mismatch, can't add token [%s] with supply %s", l_token_item->ticker, l_supply); - DAP_DELETE(l_supply); - } - DAP_DELETE(l_gdb_group); -} - -static bool s_ledger_token_supply_check(dap_ledger_token_item_t *a_token_item, uint256_t a_value) -{ - if ((IS_ZERO_256(a_token_item->total_supply) || IS_ZERO_256(a_value))) - return true; - if (compare256(a_token_item->current_supply, a_value) >= 0) - return true; - char *l_supply_str = dap_chain_balance_datoshi_print(a_token_item->current_supply); - char *l_value_str = dap_chain_balance_datoshi_print(a_value); - log_it(L_WARNING, "Token current supply %s < emission value %s", l_supply_str, l_value_str); - DAP_DEL_MULTY(l_supply_str, l_value_str); - return false; -} - -static bool s_ledger_token_supply_check_update(dap_ledger_t *a_ledger, dap_ledger_token_item_t *a_token_item, uint256_t a_value, bool a_for_removing) -{ - assert(a_token_item); - if ((IS_ZERO_256(a_token_item->total_supply) || IS_ZERO_256(a_value))) - return true; - if (!s_ledger_token_supply_check(a_token_item, a_value) && !a_for_removing) - return false; - int l_overflow = false; - if(a_for_removing) - l_overflow = SUM_256_256(a_token_item->current_supply, a_value, &a_token_item->current_supply); - else - l_overflow = SUBTRACT_256_256(a_token_item->current_supply, a_value, &a_token_item->current_supply); - assert(!l_overflow); - const char *l_balance; dap_uint256_to_char(a_token_item->current_supply, &l_balance); - log_it(L_NOTICE, "New current supply %s for token %s", l_balance, a_token_item->ticker); - s_ledger_token_cache_update(a_ledger, a_token_item); - return true; -} - -/** - * @brief dap_ledger_token_add - * @param a_token - * @param a_token_size - * @return - */ -int dap_ledger_token_add(dap_ledger_t *a_ledger, byte_t *a_token, size_t a_token_size) -{ - dap_return_val_if_fail(a_ledger && a_token && a_token_size, DAP_LEDGER_CHECK_INVALID_ARGS); - dap_ledger_token_item_t *l_token_item = NULL; - dap_chain_datum_token_t *l_token = NULL; - size_t l_tsd_total_size = 0, l_signs_size = 0; - dap_hash_fast_t l_token_update_hash; - int ret = s_token_add_check(a_ledger, a_token, a_token_size, &l_token_item, &l_token, - &l_tsd_total_size, &l_signs_size, &l_token_update_hash); - if (ret != DAP_LEDGER_CHECK_OK && ret != DAP_LEDGER_CHECK_WHITELISTED) - return ret; - - if (!l_token_item) { - assert(l_token->type == DAP_CHAIN_DATUM_TOKEN_TYPE_DECL); - l_token_item = DAP_NEW_Z(dap_ledger_token_item_t); - if ( !l_token_item ) { - DAP_DELETE(l_token); - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; - } - *l_token_item = (dap_ledger_token_item_t) { - .subtype = l_token->subtype, - .total_supply = l_token->total_supply, - .current_supply = l_token->total_supply, - .auth_signs_total = l_token->signs_total, - .auth_signs_valid = l_token->signs_valid, - .token_emissions_rwlock = PTHREAD_RWLOCK_INITIALIZER, - .token_ts_updated_rwlock = PTHREAD_RWLOCK_INITIALIZER, - .auth_pkeys = DAP_NEW_Z_SIZE(dap_pkey_t*, sizeof(dap_pkey_t*) * l_token->signs_total), - .auth_pkey_hashes = DAP_NEW_Z_SIZE(dap_chain_hash_fast_t, sizeof(dap_chain_hash_fast_t) * l_token->signs_total), - .flags = 0 - }; - switch (l_token->subtype) { - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PRIVATE: - l_token_item->flags = l_token->header_private_decl.flags; break; - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE: - l_token_item->flags = l_token->header_native_decl.flags; break; - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PUBLIC: - l_token_item->flags = l_token->header_public.flags; break; - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_SIMPLE: - default:; - } - if ( !l_token_item->auth_pkeys ) { - DAP_DEL_MULTY(l_token, l_token_item); - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; - }; - if ( !l_token_item->auth_pkey_hashes ) { - DAP_DEL_MULTY(l_token, l_token_item->auth_pkeys, l_token_item); - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; - } - size_t l_auth_signs_total = l_token->signs_total; - dap_sign_t **l_signs = dap_sign_get_unique_signs(l_token->tsd_n_signs + l_tsd_total_size, - l_signs_size, - &l_auth_signs_total); -#define CLEAN_UP DAP_DEL_MULTY(l_token, l_token_item->auth_pkeys, l_token_item->auth_pkey_hashes, l_token_item) - if (!l_signs) { - CLEAN_UP; - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; - } - dap_stpcpy((char *)l_token_item->ticker, l_token->ticker); - for (uint16_t k = 0; k < l_token_item->auth_signs_total; k++) { - l_token_item->auth_pkeys[k] = dap_pkey_get_from_sign(l_signs[k]); - if (!l_token_item->auth_pkeys[k]) { - CLEAN_UP; - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; - } - dap_pkey_get_hash(l_token_item->auth_pkeys[k], &l_token_item->auth_pkey_hashes[k]); - } -#undef CLEAN_UP - DAP_DELETE(l_signs); - l_token_item->datum_token_size = sizeof(dap_chain_datum_token_t) + l_tsd_total_size + l_signs_size; - l_token_item->datum_token = l_token; - pthread_rwlock_wrlock(&PVT(a_ledger)->tokens_rwlock); - HASH_ADD_STR(PVT(a_ledger)->tokens, ticker, l_token_item); - } else { - assert(l_token->type == DAP_CHAIN_DATUM_TOKEN_TYPE_UPDATE); - pthread_rwlock_wrlock(&PVT(a_ledger)->tokens_rwlock); - dap_ledger_token_update_item_t *l_token_update_item = NULL; - pthread_rwlock_wrlock(&l_token_item->token_ts_updated_rwlock); - HASH_FIND(hh, l_token_item->token_ts_updated, &l_token_update_hash, sizeof(dap_hash_fast_t), l_token_update_item); - if (l_token_update_item) { - pthread_rwlock_unlock(&l_token_item->token_ts_updated_rwlock); - pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); - log_it(L_ERROR, "Token update with hash %s already exist in token %s hash-table", - dap_hash_fast_to_str_static(&l_token_update_hash), l_token->ticker); - DAP_DELETE(l_token); - return DAP_LEDGER_CHECK_APPLY_ERROR; - } - l_token_update_item = DAP_NEW(dap_ledger_token_update_item_t); - if (!l_token_update_item) { - pthread_rwlock_unlock(&l_token_item->token_ts_updated_rwlock); - pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - DAP_DELETE(l_token); - return DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; - } - *l_token_update_item = (dap_ledger_token_update_item_t) { - .update_token_hash = l_token_update_hash, - .datum_token_update = l_token, - .datum_token_update_size = sizeof(dap_chain_datum_token_t) + l_tsd_total_size + l_signs_size, - .updated_time = dap_time_now() - }; - HASH_ADD(hh, l_token_item->token_ts_updated, update_token_hash, sizeof(dap_chain_hash_fast_t), l_token_update_item); - pthread_rwlock_unlock(&l_token_item->token_ts_updated_rwlock); - l_token_item->last_update_token_time = l_token_update_item->updated_time; - } - if (ret != DAP_LEDGER_CHECK_WHITELISTED) { - ret = s_token_tsd_parse(l_token_item, l_token, a_ledger, l_token->tsd_n_signs, l_tsd_total_size, true); - assert(ret == DAP_LEDGER_CHECK_OK); - } - pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); - const char *l_balance_dbg = NULL, *l_declare_update_str = NULL, *l_type_str = NULL; - if (s_debug_more) - dap_uint256_to_char(l_token->total_supply, &l_balance_dbg); - switch (l_token->type) { - case DAP_CHAIN_DATUM_TOKEN_TYPE_DECL: l_declare_update_str = "declared"; break; - case DAP_CHAIN_DATUM_TOKEN_TYPE_UPDATE: l_declare_update_str = "updated"; break; - default: assert(false); break; - } - switch (l_token->subtype) { - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_SIMPLE: l_type_str = "Simple"; break; - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PRIVATE: l_type_str = "Private"; break; - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE: l_type_str = "CF20"; break; - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PUBLIC: l_type_str = "Public"; break; - default: assert(false); break; - } - debug_if(s_debug_more, L_INFO, "%s token %s has been %s, total_supply: %s, signs_valid: %zu, signs_total: %zu", - l_type_str, l_token_item->ticker, l_declare_update_str, - l_balance_dbg, l_token_item->auth_signs_valid, l_token_item->auth_signs_total); - s_ledger_token_cache_update(a_ledger, l_token_item); - return ret; -} - -int dap_ledger_token_load(dap_ledger_t *a_ledger, byte_t *a_token, size_t a_token_size) -{ - if (dap_chain_net_get_load_mode(a_ledger->net)) { - const char *l_ticker = NULL; - switch (*(uint16_t *)a_token) { - case DAP_CHAIN_DATUM_TOKEN_TYPE_DECL: - l_ticker = ((dap_chain_datum_token_t *)a_token)->ticker; - break; - case DAP_CHAIN_DATUM_TOKEN_TYPE_OLD_SIMPLE: - case DAP_CHAIN_DATUM_TOKEN_TYPE_OLD_PUBLIC: - case DAP_CHAIN_DATUM_TOKEN_TYPE_OLD_NATIVE_DECL: - case DAP_CHAIN_DATUM_TOKEN_TYPE_OLD_PRIVATE_DECL: - l_ticker = ((dap_chain_datum_token_old_t *)a_token)->ticker; - break; - } - if (l_ticker && s_ledger_find_token(a_ledger, l_ticker)) - return DAP_LEDGER_CHECK_OK; - } - return dap_ledger_token_add(a_ledger, a_token, a_token_size); -} - -/** - * @brief s_tx_header_print - * prepare data for print, add time - * - * return history string - * @param a_tx - * @param a_tx_hash - * @param a_hash_out_type - * @return a_json_out - */ - -static void s_tx_header_print(json_object *a_json_out, dap_chain_datum_tx_t *a_tx, - const char *a_hash_out_type, dap_chain_hash_fast_t *a_tx_hash) -{ - char l_time_str[DAP_TIME_STR_SIZE] = "unknown"; - if (a_tx->header.ts_created) - dap_time_to_str_rfc822(l_time_str, DAP_TIME_STR_SIZE, a_tx->header.ts_created); - const char *l_tx_hash_str = dap_strcmp(a_hash_out_type, "hex") - ? dap_enc_base58_encode_hash_to_str_static(a_tx_hash) - : dap_chain_hash_fast_to_str_static(a_tx_hash); - json_object_object_add(a_json_out, "TX hash ", json_object_new_string(l_tx_hash_str)); - json_object_object_add(a_json_out, "time ", json_object_new_string(l_time_str)); -} - -static void s_dump_datum_tx_for_addr(dap_ledger_tx_item_t *a_item, bool a_unspent, dap_ledger_t *a_ledger, - dap_chain_addr_t *a_addr, const char *a_hash_out_type, json_object *json_arr_out) { - if (a_unspent && a_item->cache_data.ts_spent) { - // With 'unspent' flag spent ones are ignored - return; - } - dap_chain_datum_tx_t *l_tx = a_item->tx; - dap_chain_hash_fast_t l_tx_hash = a_item->tx_hash_fast; - dap_chain_addr_t l_src_addr = { }, l_dst_addr = { }; - bool l_base_tx = false; - const char *l_src_token = NULL; - int l_src_subtype = DAP_CHAIN_TX_OUT_COND_SUBTYPE_UNDEFINED; - dap_hash_fast_t l_tx_prev_hash = { }; - byte_t *l_item; size_t l_size; int idx, l_tx_prev_out_idx; - TX_ITEM_ITER_TX_TYPE(l_item, TX_ITEM_TYPE_IN_ALL, l_size, idx, l_tx) { - switch (*l_item) { - case TX_ITEM_TYPE_IN: { - dap_chain_tx_in_t *l_tx_in = (dap_chain_tx_in_t*)l_item; - l_tx_prev_hash = l_tx_in->header.tx_prev_hash; - l_tx_prev_out_idx = l_tx_in->header.tx_out_prev_idx; - } break; - case TX_ITEM_TYPE_IN_COND: { - dap_chain_tx_in_cond_t *l_tx_in_cond = (dap_chain_tx_in_cond_t*)l_item; - l_tx_prev_hash = l_tx_in_cond->header.tx_prev_hash; - l_tx_prev_out_idx = l_tx_in_cond->header.tx_out_prev_idx; - } break; - default: - continue; - } - if ( dap_hash_fast_is_blank(&l_tx_prev_hash) ) { - l_base_tx = true; - dap_chain_tx_in_ems_t *l_token = (dap_chain_tx_in_ems_t*) - dap_chain_datum_tx_item_get(l_tx, NULL, NULL, TX_ITEM_TYPE_IN_EMS, NULL); - if (l_token) - l_src_token = l_token->header.ticker; - break; - } - dap_chain_datum_tx_t *l_tx_prev = dap_ledger_tx_find_by_hash(a_ledger, &l_tx_prev_hash); - if ( !l_tx_prev ) - continue; - uint8_t *l_prev_out_union = dap_chain_datum_tx_item_get_nth(l_tx_prev, TX_ITEM_TYPE_OUT_ALL, l_tx_prev_out_idx); - if (!l_prev_out_union) - continue; - switch (*l_prev_out_union) { - case TX_ITEM_TYPE_OUT: - l_src_addr = ((dap_chain_tx_out_t *)l_prev_out_union)->addr; - break; - case TX_ITEM_TYPE_OUT_EXT: - l_src_addr = ((dap_chain_tx_out_ext_t *)l_prev_out_union)->addr; - l_src_token = (const char*)(((dap_chain_tx_out_ext_t *)l_prev_out_union)->token); - break; - case TX_ITEM_TYPE_OUT_COND: - l_src_subtype = ((dap_chain_tx_out_cond_t *)l_prev_out_union)->header.subtype; - default: - break; - } - if ( !dap_chain_addr_compare(&l_src_addr, a_addr) ) - break; //it's not our addr - if (!l_src_token) { - l_src_token = a_item->cache_data.token_ticker; - } - } - - bool l_header_printed = false; - l_item = NULL; - TX_ITEM_ITER_TX_TYPE(l_item, TX_ITEM_TYPE_OUT_ALL, l_size, idx, l_tx) { - dap_chain_tx_item_type_t l_type = *l_item; - uint256_t l_value; - switch (l_type) { - case TX_ITEM_TYPE_OUT: - l_dst_addr = ((dap_chain_tx_out_t*)l_item)->addr; - l_value = ((dap_chain_tx_out_t*)l_item)->header.value; - break; - case TX_ITEM_TYPE_OUT_EXT: - l_dst_addr = ((dap_chain_tx_out_ext_t*)l_item)->addr; - l_value = ((dap_chain_tx_out_ext_t *)l_item)->header.value; - break; - case TX_ITEM_TYPE_OUT_COND: - l_value = ((dap_chain_tx_out_cond_t *)l_item)->header.value; - default: - break; - } - if ( !dap_chain_addr_is_blank(&l_src_addr) && !dap_chain_addr_is_blank(&l_dst_addr) - && dap_chain_addr_compare(&l_dst_addr, &l_src_addr) ) - continue; // send to self - if ( dap_chain_addr_compare(&l_src_addr, a_addr)) { - json_object * l_json_obj_datum = json_object_new_object(); - if (!l_header_printed) { - s_tx_header_print(l_json_obj_datum, l_tx, a_hash_out_type, &l_tx_hash); - l_header_printed = true; - } - //const char *l_token_ticker = dap_ledger_tx_get_token_ticker_by_hash(l_ledger, &l_tx_hash); - const char *l_dst_addr_str = !dap_chain_addr_is_blank(&l_dst_addr) - ? dap_chain_addr_to_str_static(&l_dst_addr) - : dap_chain_tx_out_cond_subtype_to_str( ((dap_chain_tx_out_cond_t *)l_item)->header.subtype ); - json_object_object_add(l_json_obj_datum, "send", json_object_new_string(dap_uint256_to_char(l_value, NULL))); - json_object_object_add(l_json_obj_datum, "to addr", json_object_new_string(l_dst_addr_str)); - json_object_object_add(l_json_obj_datum, "token", l_src_token ? json_object_new_string(l_src_token) : json_object_new_string("UNKNOWN")); - json_object_array_add(json_arr_out, l_json_obj_datum); - } - if ( dap_chain_addr_compare(&l_dst_addr, a_addr) ) { - json_object * l_json_obj_datum = json_object_new_object(); - if (!l_header_printed) { - s_tx_header_print(l_json_obj_datum, l_tx, a_hash_out_type, &l_tx_hash); - l_header_printed = true; - } - const char *l_dst_token = (l_type == TX_ITEM_TYPE_OUT_EXT) ? - (const char *)(((dap_chain_tx_out_ext_t *)l_item)->token) : NULL; - const char *l_src_addr_str = l_base_tx - ? "emission" - : ( !dap_chain_addr_is_blank(&l_src_addr) - ? dap_chain_addr_to_str_static(&l_src_addr) - : dap_chain_tx_out_cond_subtype_to_str(l_src_subtype) ); - json_object_object_add(l_json_obj_datum, "recv ", json_object_new_string(dap_uint256_to_char(l_value, NULL))); - json_object_object_add(l_json_obj_datum, "token ", l_dst_token ? json_object_new_string(l_dst_token) : - (l_src_token ? json_object_new_string(l_src_token) : json_object_new_string("UNKNOWN"))); - json_object_object_add(l_json_obj_datum, "from ", json_object_new_string(l_src_addr_str)); - json_object_array_add(json_arr_out, l_json_obj_datum); - } - } -} - -json_object *dap_ledger_token_tx_item_list(dap_ledger_t * a_ledger, dap_chain_addr_t *a_addr, const char *a_hash_out_type, bool a_unspent_only) -{ - json_object * json_arr_out = json_object_new_array(); - if (!json_arr_out) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return NULL; - } - - dap_ledger_tx_item_t *l_tx_item, *l_tx_tmp; - dap_ledger_private_t * l_ledger_pvt = PVT(a_ledger); - - pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); - HASH_ITER(hh, l_ledger_pvt->ledger_items, l_tx_item, l_tx_tmp) { - s_dump_datum_tx_for_addr(l_tx_item, a_unspent_only, a_ledger, a_addr, a_hash_out_type, json_arr_out); - } - pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); - - // if no history - if(!json_arr_out) - { - json_object * json_obj_addr = json_object_new_object(); - json_object_object_add(json_obj_addr, "status:", json_object_new_string("empty")); - json_object_array_add(json_arr_out, json_obj_addr); - } - return json_arr_out; -} - -static bool s_pack_ledger_threshold_info_json (json_object *a_json_arr_out, dap_ledger_tx_item_t *a_tx_item) -{ - json_object *json_obj_tx = json_object_new_object(); - if (!json_obj_tx) - return 1; - char l_tx_prev_hash_str[DAP_HASH_FAST_STR_SIZE]={0}; - char l_time[DAP_TIME_STR_SIZE] = {0}; - dap_chain_hash_fast_to_str(&a_tx_item->tx_hash_fast,l_tx_prev_hash_str,sizeof(l_tx_prev_hash_str)); - dap_time_to_str_rfc822(l_time, sizeof(l_time), a_tx_item->cache_data.ts_created); - json_object_object_add(json_obj_tx, "Ledger thresholded tx_hash_fast", json_object_new_string(l_tx_prev_hash_str)); - json_object_object_add(json_obj_tx, "time_created", json_object_new_string(l_time)); - json_object_object_add(json_obj_tx, "tx_item_size", json_object_new_int(a_tx_item->tx->header.tx_items_size)); - json_object_array_add(a_json_arr_out, json_obj_tx); - return 0; -} -static bool s_pack_ledger_balance_info_json (json_object *a_json_arr_out, dap_ledger_wallet_balance_t *a_balance_item) -{ - json_object* json_obj_tx = json_object_new_object(); - - json_object_object_add(json_obj_tx, "Ledger balance key", json_object_new_string(a_balance_item->key)); - json_object_object_add(json_obj_tx, "token_ticker", json_object_new_string(a_balance_item->token_ticker)); - json_object_object_add(json_obj_tx, "balance", json_object_new_string(dap_uint256_to_char(a_balance_item->balance, NULL))); - json_object_array_add(a_json_arr_out, json_obj_tx); - return 0; -} - -json_object *dap_ledger_threshold_info(dap_ledger_t *a_ledger, size_t a_limit, size_t a_offset, dap_chain_hash_fast_t *a_threshold_hash, bool a_head) -{ - dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); - dap_ledger_tx_item_t *l_tx_item = NULL, *l_tx_tmp; - json_object *json_arr_out = json_object_new_array(); - if (!json_arr_out) - return NULL; - uint32_t l_counter = 0; - size_t l_arr_start = 0; - size_t l_arr_end = 0; - dap_chain_set_offset_limit_json(json_arr_out, &l_arr_start, &l_arr_end, a_limit, a_offset, HASH_COUNT(l_ledger_pvt->threshold_txs)); - - pthread_rwlock_rdlock(&l_ledger_pvt->threshold_txs_rwlock); - if (a_threshold_hash) { - json_object *json_obj_tx = json_object_new_object(); - if (!json_obj_tx) { - pthread_rwlock_unlock(&l_ledger_pvt->threshold_txs_rwlock); - json_object_put(json_arr_out); - return NULL; - } - HASH_FIND(hh, l_ledger_pvt->threshold_txs, a_threshold_hash, sizeof(dap_hash_t), l_tx_item); - if (l_tx_item) { - json_object_object_add(json_obj_tx, "Hash was found in ledger tx threshold", json_object_new_string(dap_hash_fast_to_str_static(a_threshold_hash))); - json_object_array_add(json_arr_out, json_obj_tx); - } else { - json_object_object_add(json_obj_tx, "Hash wasn't found in ledger", json_object_new_string("empty")); - json_object_array_add(json_arr_out, json_obj_tx); - } - } else { - size_t i_tmp = 0; - if (a_head) - HASH_ITER(hh, l_ledger_pvt->threshold_txs, l_tx_item, l_tx_tmp) { - if (i_tmp < l_arr_start || i_tmp >= l_arr_end) - { - i_tmp++; - continue; - } - i_tmp++; - if (s_pack_ledger_threshold_info_json(json_arr_out, l_tx_item)) { - pthread_rwlock_unlock(&l_ledger_pvt->threshold_txs_rwlock); - json_object_put(json_arr_out); - return NULL; - } - l_counter++; - } - else - { - l_tx_item = HASH_LAST(l_ledger_pvt->threshold_txs); - for(; l_tx_item; l_tx_item = l_tx_item->hh.prev, i_tmp++){ - if (i_tmp < l_arr_start || i_tmp >= l_arr_end) - continue; - if (s_pack_ledger_threshold_info_json(json_arr_out, l_tx_item)) { - pthread_rwlock_unlock(&l_ledger_pvt->threshold_txs_rwlock); - json_object_put(json_arr_out); - return NULL; - } - l_counter++; - } - } - if (!l_counter) { - json_object* json_obj_tx = json_object_new_object(); - json_object_object_add(json_obj_tx, "status", json_object_new_string("0 items in ledger tx threshold")); - json_object_array_add(json_arr_out, json_obj_tx); - } - pthread_rwlock_unlock(&l_ledger_pvt->threshold_txs_rwlock); - } - - return json_arr_out; -} - -json_object *dap_ledger_balance_info(dap_ledger_t *a_ledger, size_t a_limit, size_t a_offset, bool a_head) -{ - dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); - json_object * json_arr_out = json_object_new_array(); - pthread_rwlock_rdlock(&l_ledger_pvt->balance_accounts_rwlock); - uint32_t l_counter = 0; - dap_ledger_wallet_balance_t *l_balance_item, *l_balance_tmp; - size_t l_arr_start = 0; - size_t l_arr_end = 0; - dap_chain_set_offset_limit_json(json_arr_out, &l_arr_start, &l_arr_end, a_limit, a_offset, HASH_COUNT(l_ledger_pvt->balance_accounts)); - - size_t i_tmp = 0; - if (a_head) - HASH_ITER(hh, l_ledger_pvt->balance_accounts, l_balance_item, l_balance_tmp) { - if (i_tmp < l_arr_start || i_tmp >= l_arr_end) { - i_tmp++; - continue; - } - i_tmp++; - s_pack_ledger_balance_info_json(json_arr_out, l_balance_item); - l_counter +=1; - } - else { - l_balance_item = HASH_LAST(l_ledger_pvt->balance_accounts); - for(; l_balance_item; l_balance_item = l_balance_item->hh.prev, i_tmp++){ - if (i_tmp < l_arr_start || i_tmp >= l_arr_end) - continue; - s_pack_ledger_balance_info_json(json_arr_out, l_balance_item); - l_counter++; - } - } - if (!l_counter){ - json_object* json_obj_tx = json_object_new_object(); - json_object_object_add(json_obj_tx, "No items in ledger balance_accounts", json_object_new_string("empty")); - json_object_array_add(json_arr_out, json_obj_tx); - } - pthread_rwlock_unlock(&l_ledger_pvt->balance_accounts_rwlock); - return json_arr_out; -} - -/** - * @breif dap_ledger_token_get_auth_signs_valid - * @param a_ledger - * @param a_token_ticker - * @return 0 if no ticker found - */ -size_t dap_ledger_token_get_auth_signs_valid(dap_ledger_t *a_ledger, const char *a_token_ticker) -{ - dap_ledger_token_item_t *l_token_item = s_ledger_find_token(a_ledger, a_token_ticker); - if (!l_token_item) - return 0; - return l_token_item->auth_signs_valid; -} - -/** - * @breif dap_ledger_token_get_auth_signs_total - * @param a_ledger - * @param a_token_ticker - * @return - */ -size_t dap_ledger_token_get_auth_signs_total(dap_ledger_t *a_ledger, const char *a_token_ticker) -{ - dap_ledger_token_item_t *l_token_item = s_ledger_find_token(a_ledger, a_token_ticker); - if (!l_token_item) - return 0; - return l_token_item->auth_signs_total; -} - -/** - * @breif dap_ledger_token_auth_signs_hashes - * @param a_ledger - * @param a_token_ticker - * @return - */ -dap_list_t *dap_ledger_token_get_auth_pkeys_hashes(dap_ledger_t *a_ledger, const char *a_token_ticker) -{ - dap_list_t *l_ret = NULL; - dap_ledger_token_item_t *l_token_item = s_ledger_find_token(a_ledger, a_token_ticker); - if (!l_token_item) - return l_ret; - debug_if(s_debug_more, L_INFO, " ! Token %s : total %lu auth signs", a_token_ticker, l_token_item->auth_signs_total); - for (size_t i = 0; i < l_token_item->auth_signs_total; i++) - l_ret = dap_list_append(l_ret, l_token_item->auth_pkey_hashes + i); - return l_ret; -} - -uint256_t dap_ledger_token_get_emission_rate(dap_ledger_t *a_ledger, const char *a_token_ticker) -{ - dap_ledger_token_item_t *l_token_item = s_ledger_find_token(a_ledger, a_token_ticker); - if (!l_token_item || !l_token_item->is_delegated) - return uint256_0; - return l_token_item->emission_rate; -} - -json_object *s_token_item_to_json(dap_ledger_token_item_t *a_token_item) -{ - json_object *json_obj_datum = json_object_new_object(); - const char *l_type_str = NULL; - switch (a_token_item->subtype) { - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_SIMPLE: - l_type_str = "SIMPLE"; break; - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PRIVATE: - l_type_str = "PRIVATE"; break; - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE: - l_type_str = "CF20"; break; - case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PUBLIC: - l_type_str = "PUBLIC"; break; - default: l_type_str = "UNKNOWN"; break; - } - json_object_object_add(json_obj_datum, "-->Token name", json_object_new_string(a_token_item->ticker)); - json_object_object_add(json_obj_datum, "type", json_object_new_string(l_type_str)); - if (a_token_item->subtype != DAP_CHAIN_DATUM_TOKEN_SUBTYPE_SIMPLE && a_token_item->subtype != DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PUBLIC) { - dap_chain_datum_token_flags_dump_to_json(json_obj_datum, "flags", a_token_item->flags); - json_object_object_add(json_obj_datum, "description", a_token_item->description ? - json_object_new_string(a_token_item->description) : - json_object_new_string("The token description is not set")); - } - json_object_object_add(json_obj_datum, "Supply current", json_object_new_string(dap_uint256_to_char(a_token_item->current_supply, NULL))); - json_object_object_add(json_obj_datum, "Supply total", json_object_new_string(dap_uint256_to_char(a_token_item->total_supply, NULL))); - json_object_object_add(json_obj_datum, "Decimals", json_object_new_string("18")); - json_object_object_add(json_obj_datum, "Auth signs valid", json_object_new_int(a_token_item->auth_signs_valid)); - json_object_object_add(json_obj_datum, "Auth signs total", json_object_new_int(a_token_item->auth_signs_total)); - json_object *l_json_arr_pkeys = json_object_new_array(); - for (uint16_t i = 0; i < a_token_item->auth_signs_total; i++) { - json_object *l_json_obj_out = json_object_new_object(); - json_object_object_add(l_json_obj_out, "line", json_object_new_int(i)); - json_object_object_add(l_json_obj_out, "hash", json_object_new_string(dap_hash_fast_to_str_static(a_token_item->auth_pkey_hashes + i))); - json_object_object_add(l_json_obj_out, "pkey_type", json_object_new_string(dap_pkey_type_to_str(a_token_item->auth_pkeys[i]->header.type))); - json_object_object_add(l_json_obj_out, "bytes", json_object_new_int(a_token_item->auth_pkeys[i]->header.size)); - json_object_array_add(l_json_arr_pkeys, l_json_obj_out); - } - json_object *l_json_arr_tx_recv_allow = json_object_new_array(); - for (size_t i = 0; i < a_token_item->tx_recv_allow_size; i++) { - dap_chain_addr_t l_addr = a_token_item->tx_recv_allow[i]; - const char *l_addr_str = dap_chain_addr_to_str_static(&l_addr); - json_object_array_add(l_json_arr_tx_recv_allow, json_object_new_string(l_addr_str)); - } - json_object *l_json_arr_tx_recv_block = json_object_new_array(); - for (size_t i = 0; i < a_token_item->tx_recv_block_size; i++) { - dap_chain_addr_t l_addr = a_token_item->tx_recv_block[i]; - const char *l_addr_str = dap_chain_addr_to_str_static(&l_addr); - json_object_array_add(l_json_arr_tx_recv_block, json_object_new_string(l_addr_str)); - } - json_object *l_json_arr_tx_send_allow = json_object_new_array(); - for (size_t i = 0; i < a_token_item->tx_send_allow_size; i++) { - dap_chain_addr_t l_addr = a_token_item->tx_send_allow[i]; - const char *l_addr_str = dap_chain_addr_to_str_static(&l_addr); - json_object_array_add(l_json_arr_tx_send_allow, json_object_new_string(l_addr_str)); - } - json_object *l_json_arr_tx_send_block = json_object_new_array(); - for (size_t i = 0; i < a_token_item->tx_send_block_size; i++) { - dap_chain_addr_t l_addr = a_token_item->tx_send_block[i]; - const char *l_addr_str = dap_chain_addr_to_str_static(&l_addr); - json_object_array_add(l_json_arr_tx_send_block, json_object_new_string(l_addr_str)); - } - json_object_object_add(json_obj_datum, "Signatures public keys", l_json_arr_pkeys); - a_token_item->tx_recv_allow_size ? json_object_object_add(json_obj_datum, "tx_recv_allow", l_json_arr_tx_recv_allow) : - json_object_put(l_json_arr_tx_recv_allow); - a_token_item->tx_recv_block_size ? json_object_object_add(json_obj_datum, "tx_recv_block", l_json_arr_tx_recv_block) : - json_object_put(l_json_arr_tx_recv_block); - a_token_item->tx_send_allow_size ? json_object_object_add(json_obj_datum, "tx_send_allow", l_json_arr_tx_send_allow) : - json_object_put(l_json_arr_tx_send_allow); - a_token_item->tx_send_block_size ? json_object_object_add(json_obj_datum, "tx_send_block", l_json_arr_tx_send_block) : - json_object_put(l_json_arr_tx_send_block); - json_object_object_add(json_obj_datum, "Total emissions", json_object_new_int(HASH_COUNT(a_token_item->token_emissions))); - return json_obj_datum; -} - -/** - * @brief Compose string list of all tokens with information - * @param a_ledger - * @return - */ -json_object *dap_ledger_token_info(dap_ledger_t *a_ledger, size_t a_limit, size_t a_offset) -{ - json_object * json_obj_datum; - json_object * json_arr_out = json_object_new_array(); - dap_ledger_token_item_t *l_token_item, *l_tmp_item; - pthread_rwlock_rdlock(&PVT(a_ledger)->tokens_rwlock); - size_t l_arr_start = 0; - if (a_offset > 0) { - l_arr_start = a_offset; - json_object* json_obj_tx = json_object_new_object(); - json_object_object_add(json_obj_tx, "offset", json_object_new_int(l_arr_start)); - json_object_array_add(json_arr_out, json_obj_tx); - } - size_t l_arr_end = HASH_COUNT(PVT(a_ledger)->tokens); - if (a_limit) { - json_object* json_obj_tx = json_object_new_object(); - json_object_object_add(json_obj_tx, "limit", json_object_new_int(a_limit)); - json_object_array_add(json_arr_out, json_obj_tx); - l_arr_end = l_arr_start + a_limit; - if (l_arr_end > HASH_COUNT(PVT(a_ledger)->tokens)) { - l_arr_end = HASH_COUNT(PVT(a_ledger)->tokens); - } - } - size_t i = 0; - HASH_ITER(hh, PVT(a_ledger)->tokens, l_token_item, l_tmp_item) { - if (i < l_arr_start || i >= l_arr_end) { - i++; - continue; - } - json_obj_datum = s_token_item_to_json(l_token_item); - json_object_array_add(json_arr_out, json_obj_datum); - i++; - } - pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); - return json_arr_out; -} - -/** - * @breif Forms a JSON object with a token description for the specified ticker. - * @param a_ledger - * @param a_token_ticker - * @return - */ -json_object *dap_ledger_token_info_by_name(dap_ledger_t *a_ledger, const char *a_token_ticker) -{ - dap_ledger_token_item_t *l_token_item = NULL; - HASH_FIND_STR(PVT(a_ledger)->tokens, a_token_ticker, l_token_item); - if (l_token_item) - return s_token_item_to_json(l_token_item); - return json_object_new_null(); -} - -/** - * @brief Get all token declatations - * @param a_ledger - * @return - */ -dap_list_t* dap_ledger_token_decl_all(dap_ledger_t *a_ledger) -{ - dap_list_t * l_ret = NULL; - dap_ledger_token_item_t *l_token_item, *l_tmp_item; - pthread_rwlock_rdlock(&PVT(a_ledger)->tokens_rwlock); - - HASH_ITER(hh, PVT(a_ledger)->tokens, l_token_item, l_tmp_item) { - dap_chain_datum_token_t *l_token = l_token_item->datum_token; - l_ret = dap_list_append(l_ret, l_token); - } - pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); - return l_ret; -} - - -/** - * @brief s_threshold_txs_proc - * @param a_ledger - */ -static void s_threshold_txs_proc( dap_ledger_t *a_ledger) -{ - bool l_success; - dap_ledger_private_t * l_ledger_pvt = PVT(a_ledger); - pthread_rwlock_wrlock(&l_ledger_pvt->threshold_txs_rwlock); - do { - l_success = false; - dap_ledger_tx_item_t *l_tx_item, *l_tx_tmp; - HASH_ITER(hh, l_ledger_pvt->threshold_txs, l_tx_item, l_tx_tmp) { - int l_res = dap_ledger_tx_add(a_ledger, l_tx_item->tx, &l_tx_item->tx_hash_fast, true, NULL); - if (l_res != DAP_CHAIN_CS_VERIFY_CODE_TX_NO_EMISSION && - l_res != DAP_CHAIN_CS_VERIFY_CODE_TX_NO_PREVIOUS) { - HASH_DEL(l_ledger_pvt->threshold_txs, l_tx_item); - if ( !l_ledger_pvt->mapped ) - DAP_DELETE(l_tx_item->tx); - DAP_DELETE(l_tx_item); - l_success = true; - } - } - } while (l_success); - pthread_rwlock_unlock(&l_ledger_pvt->threshold_txs_rwlock); -} - -/** - * @breif s_treshold_txs_free - * @param a_ledger - */ -static void s_threshold_txs_free(dap_ledger_t *a_ledger) -{ - log_it(L_DEBUG, "Start free threshold txs"); - dap_ledger_private_t *l_pvt = PVT(a_ledger); - dap_ledger_tx_item_t *l_current = NULL, *l_tmp = NULL; - dap_nanotime_t l_time_cut_off = dap_nanotime_now() - dap_nanotime_from_sec(7200); //7200 sec = 2 hours. - pthread_rwlock_wrlock(&l_pvt->threshold_txs_rwlock); - HASH_ITER(hh, l_pvt->threshold_txs, l_current, l_tmp) { - if (l_current->ts_added < l_time_cut_off) { - HASH_DEL(l_pvt->threshold_txs, l_current); - char l_tx_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE]; - dap_chain_hash_fast_to_str(&l_current->tx_hash_fast, l_tx_hash_str, sizeof(l_tx_hash_str)); - if ( !l_pvt->mapped ) - DAP_DELETE(l_current->tx); - DAP_DELETE(l_current); - log_it(L_NOTICE, "Removed transaction %s form threshold ledger", l_tx_hash_str); - } - } - pthread_rwlock_unlock(&l_pvt->threshold_txs_rwlock); -} - -/** - * @brief s_load_cache_gdb_loaded_balances_callback - * @param a_global_db_context - * @param a_rc - * @param a_group - * @param a_key - * @param a_values_total - * @param a_values_shift - * @param a_values_count - * @param a_values - * @param a_arg - */ -static bool s_load_cache_gdb_loaded_balances_callback(dap_global_db_instance_t *a_dbi, - int a_rc, const char *a_group, - const size_t a_values_total, const size_t a_values_count, - dap_global_db_obj_t *a_values, void *a_arg) -{ - dap_ledger_t * l_ledger = (dap_ledger_t*) a_arg; - dap_ledger_private_t * l_ledger_pvt = PVT(l_ledger); - for (size_t i = 0; i < a_values_count; i++) { - dap_ledger_wallet_balance_t *l_balance_item = DAP_NEW_Z(dap_ledger_wallet_balance_t); - if (!l_balance_item) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return false; - } - l_balance_item->key = DAP_NEW_Z_SIZE(char, strlen(a_values[i].key) + 1); - if (!l_balance_item->key) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - DAP_DEL_Z(l_balance_item); - return false; - } - strcpy(l_balance_item->key, a_values[i].key); - char *l_ptr = strchr(l_balance_item->key, ' '); - if (l_ptr++) { - strcpy(l_balance_item->token_ticker, l_ptr); - } - l_balance_item->balance = *(uint256_t *)a_values[i].value; - HASH_ADD_KEYPTR(hh, l_ledger_pvt->balance_accounts, l_balance_item->key, - strlen(l_balance_item->key), l_balance_item); - /* Notify the world */ - /*struct json_object *l_json = wallet_info_json_collect(a_ledger, l_balance_item); - dap_notify_server_send_mt(json_object_get_string(l_json)); - json_object_put(l_json);*/ // TODO: unstable and spammy - } - pthread_mutex_lock( &l_ledger_pvt->load_mutex ); - l_ledger_pvt->load_end = true; - pthread_cond_broadcast( &l_ledger_pvt->load_cond ); - pthread_mutex_unlock( &l_ledger_pvt->load_mutex ); - return true; -} - -/** - * @brief s_load_cache_gdb_loaded_txs_callback - * @param a_global_db_context - * @param a_rc - * @param a_group - * @param a_key - * @param a_values_total - * @param a_values_shift - * @param a_values_count - * @param a_values - * @param a_arg - */ -static bool s_load_cache_gdb_loaded_txs_callback(dap_global_db_instance_t *a_dbi, - int a_rc, const char *a_group, - const size_t a_values_total, const size_t a_values_count, - dap_global_db_obj_t *a_values, void *a_arg) -{ - dap_ledger_t * l_ledger = (dap_ledger_t*) a_arg; - dap_ledger_private_t * l_ledger_pvt = PVT(l_ledger); - for (size_t i = 0; i < a_values_count; i++) { - dap_ledger_tx_item_t *l_tx_item = DAP_NEW_Z(dap_ledger_tx_item_t); - if ( !l_tx_item ) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return false; - } - dap_chain_hash_fast_from_str(a_values[i].key, &l_tx_item->tx_hash_fast); - l_tx_item->tx = DAP_NEW_Z_SIZE(dap_chain_datum_tx_t, a_values[i].value_len - sizeof(l_tx_item->cache_data)); - if ( !l_tx_item->tx ) { - DAP_DELETE(l_tx_item); - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return false; - } - memcpy(&l_tx_item->cache_data, a_values[i].value, sizeof(l_tx_item->cache_data)); - memcpy(l_tx_item->tx, a_values[i].value + sizeof(l_tx_item->cache_data), a_values[i].value_len - sizeof(l_tx_item->cache_data)); - l_tx_item->ts_added = dap_nanotime_now(); - HASH_ADD_INORDER(hh, l_ledger_pvt->ledger_items, tx_hash_fast, sizeof(dap_chain_hash_fast_t), l_tx_item, s_sort_ledger_tx_item); - } - return true; -} - -static bool s_load_cache_gdb_loaded_stake_lock_callback(dap_global_db_instance_t *a_dbi, - int a_rc, const char *a_group, - const size_t a_values_total, const size_t a_values_count, - dap_global_db_obj_t *a_values, void *a_arg) -{ - dap_ledger_t *l_ledger = (dap_ledger_t *) a_arg; - dap_ledger_private_t *l_ledger_pvt = PVT(l_ledger); - - for (size_t i = 0; i < a_values_count; i++) { - if (a_values[i].value_len != sizeof(dap_hash_fast_t)) - continue; - dap_ledger_stake_lock_item_t *l_new_stake_lock_emission = DAP_NEW(dap_ledger_stake_lock_item_t); - if (!l_new_stake_lock_emission) { - debug_if(s_debug_more, L_ERROR, "Error: memory allocation when try adding item 'dap_ledger_stake_lock_item_t' to hash-table"); - continue; - } - dap_chain_hash_fast_from_str(a_values[i].key, &l_new_stake_lock_emission->tx_for_stake_lock_hash); - l_new_stake_lock_emission->tx_used_out = *(dap_hash_fast_t *)(a_values[i].value); - HASH_ADD(hh, l_ledger_pvt->emissions_for_stake_lock, tx_for_stake_lock_hash, sizeof(dap_chain_hash_fast_t), l_new_stake_lock_emission); - } - - char* l_gdb_group = dap_ledger_get_gdb_group(l_ledger, DAP_LEDGER_TXS_STR); - dap_global_db_get_all(l_gdb_group, 0, s_load_cache_gdb_loaded_txs_callback, l_ledger); - DAP_DELETE(l_gdb_group); - return true; -} - - -/** - * @brief GDB callback for loaded emissions from cache - * @param a_global_db_context - * @param a_rc - * @param a_group - * @param a_key - * @param a_values_total - * @param a_values_shift - * @param a_values_count - * @param a_values - * @param a_arg - * @return Always true thats means to clear up a_values - */ -static bool s_load_cache_gdb_loaded_emissions_callback(dap_global_db_instance_t *a_dbi, - int a_rc, const char *a_group, - const size_t a_values_total, const size_t a_values_count, - dap_global_db_obj_t *a_values, void *a_arg) -{ - dap_ledger_t * l_ledger = (dap_ledger_t*) a_arg; - dap_ledger_private_t * l_ledger_pvt = PVT(l_ledger); - - for (size_t i = 0; i < a_values_count; i++) { - if (a_values[i].value_len <= sizeof(dap_hash_fast_t)) - continue; - const char *c_token_ticker = ((dap_chain_datum_token_emission_t *) - (a_values[i].value + sizeof(dap_hash_fast_t)))->hdr.ticker; - dap_ledger_token_item_t *l_token_item = NULL; - HASH_FIND_STR(l_ledger_pvt->tokens, c_token_ticker, l_token_item); - if (!l_token_item) { - log_it(L_WARNING, "Not found token with ticker [%s], need to 'ledger reload' to update cache", c_token_ticker); - continue; - } - dap_ledger_token_emission_item_t *l_emission_item = DAP_NEW_Z(dap_ledger_token_emission_item_t); - if ( !l_emission_item ) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return false; - } - dap_chain_hash_fast_from_str(a_values[i].key, &l_emission_item->datum_token_emission_hash); - l_emission_item->tx_used_out = *(dap_hash_fast_t*)a_values[i].value; - l_emission_item->datum_token_emission = DAP_DUP_SIZE(a_values[i].value + sizeof(dap_hash_fast_t), - a_values[i].value_len - sizeof(dap_hash_fast_t)); - l_emission_item->datum_token_emission_size = a_values[i].value_len - sizeof(dap_hash_fast_t); - HASH_ADD(hh, l_token_item->token_emissions, datum_token_emission_hash, - sizeof(dap_chain_hash_fast_t), l_emission_item); - } - - char* l_gdb_group = dap_ledger_get_gdb_group(l_ledger, DAP_LEDGER_STAKE_LOCK_STR); - dap_global_db_get_all(l_gdb_group, 0, s_load_cache_gdb_loaded_stake_lock_callback, l_ledger); - DAP_DELETE(l_gdb_group); - return true; -} - - -/** - * @brief s_load_cache_gdb_loaded_callback - * @param a_global_db_context - * @param a_rc - * @param a_group - * @param a_key - * @param a_values_total - * @param a_values_shift - * @param a_values_count - * @param a_values - * @param a_arg - */ -static bool s_load_cache_gdb_loaded_tokens_callback(dap_global_db_instance_t *a_dbi, - int a_rc, const char *a_group, - const size_t a_values_total, const size_t a_values_count, - dap_global_db_obj_t *a_values, void *a_arg) -{ - dap_ledger_t *l_ledger = (dap_ledger_t *) a_arg; - dap_ledger_private_t *l_ledger_pvt = PVT(l_ledger); - if(a_rc) { - log_it(L_NOTICE, "No ledger cache found"); - pthread_mutex_lock(&l_ledger_pvt->load_mutex); - l_ledger_pvt->load_end = true; - pthread_cond_broadcast(&l_ledger_pvt->load_cond ); - pthread_mutex_unlock(&l_ledger_pvt->load_mutex); - - } - for (size_t i = 0; i < a_values_count; i++) { - if (a_values[i].value_len <= sizeof(uint256_t)) - continue; - dap_chain_datum_token_t *l_token = (dap_chain_datum_token_t *)(a_values[i].value + sizeof(uint256_t)); - size_t l_token_size = a_values[i].value_len - sizeof(uint256_t); - if (strcmp(l_token->ticker, a_values[i].key)) { - log_it(L_WARNING, "Corrupted token with ticker [%s], need to 'ledger reload' to update cache", a_values[i].key); - continue; - } - dap_ledger_token_add(l_ledger, (byte_t *)l_token, l_token_size); - dap_ledger_token_item_t *l_token_item = s_ledger_find_token(l_ledger, l_token->ticker); - if (l_token_item) - l_token_item->current_supply = *(uint256_t*)a_values[i].value; - } - - char *l_gdb_group = dap_ledger_get_gdb_group(l_ledger, DAP_LEDGER_EMISSIONS_STR); - dap_global_db_get_all(l_gdb_group, 0, s_load_cache_gdb_loaded_emissions_callback, l_ledger); - DAP_DELETE(l_gdb_group); - return true; -} - -/** - * @brief Load ledger from cache (stored in GDB) - * @param a_ledger - */ -void dap_ledger_load_cache(dap_ledger_t *a_ledger) -{ - dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); - char *l_gdb_group = dap_ledger_get_gdb_group(a_ledger, DAP_LEDGER_TOKENS_STR); - - pthread_mutex_lock(& l_ledger_pvt->load_mutex); - dap_global_db_get_all(l_gdb_group, 0, s_load_cache_gdb_loaded_tokens_callback, a_ledger); - while (!l_ledger_pvt->load_end) - pthread_cond_wait(& l_ledger_pvt->load_cond, &l_ledger_pvt->load_mutex); - pthread_mutex_unlock(& l_ledger_pvt->load_mutex); - - DAP_DELETE(l_gdb_group); -} - - -/** - * @brief - * create ledger for specific net - * load ledger cache - * @param a_check_flags checking flags - * DAP_LEDGER_CHECK_TOKEN_EMISSION - * DAP_LEDGER_CHECK_CELLS_DS - * DAP_LEDGER_CHECK_CELLS_DS - * @param a_net_name char * network name, for example "kelvin-testnet" - * @return dap_ledger_t* - */ -dap_ledger_t *dap_ledger_create(dap_chain_net_t *a_net, uint16_t a_flags) -{ - dap_ledger_t *l_ledger = dap_ledger_handle_new(); - if (!l_ledger) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return NULL; - } - l_ledger->net = a_net; - dap_ledger_private_t *l_ledger_pvt = PVT(l_ledger); - l_ledger_pvt->check_ds = a_flags & DAP_LEDGER_CHECK_LOCAL_DS; - l_ledger_pvt->check_cells_ds = a_flags & DAP_LEDGER_CHECK_CELLS_DS; - l_ledger_pvt->check_token_emission = a_flags & DAP_LEDGER_CHECK_TOKEN_EMISSION; - l_ledger_pvt->cached = a_flags & DAP_LEDGER_CACHE_ENABLED; - l_ledger_pvt->mapped = a_flags & DAP_LEDGER_MAPPED; - l_ledger_pvt->threshold_enabled = a_flags & DAP_LEDGER_THRESHOLD_ENABLED; - if (l_ledger_pvt->threshold_enabled) - l_ledger_pvt->threshold_txs_free_timer = dap_interval_timer_create(s_threshold_free_timer_tick, - (dap_timer_callback_t)s_threshold_txs_free, l_ledger); - pthread_cond_init(&l_ledger_pvt->load_cond, NULL); - pthread_mutex_init(&l_ledger_pvt->load_mutex, NULL); - -#ifndef DAP_LEDGER_TEST - char * l_chains_path = dap_strdup_printf("%s/network/%s", dap_config_path(), a_net->pub.name); - DIR * l_chains_dir = opendir(l_chains_path); - DAP_DEL_Z(l_chains_path); - - struct dirent * l_dir_entry; - while ( (l_dir_entry = readdir(l_chains_dir) )!= NULL ){ - if (l_dir_entry->d_name[0] == '\0') - continue; - char * l_entry_name = dap_strdup(l_dir_entry->d_name); - if (strlen(l_entry_name) > 4) { - if ( strncmp (l_entry_name + strlen(l_entry_name)-4,".cfg",4) == 0 ) { // its .cfg file - l_entry_name [strlen(l_entry_name)-4] = 0; - log_it(L_DEBUG,"Open chain config \"%s.%s\"...", a_net->pub.name, l_entry_name); - l_chains_path = dap_strdup_printf("network/%s/%s", a_net->pub.name, l_entry_name); - dap_config_t * l_cfg = dap_config_open(l_chains_path); - uint16_t l_whitelist_size, l_blacklist_size, i; - const char **l_whitelist = dap_config_get_array_str(l_cfg, "ledger", "hard_accept_list", &l_whitelist_size), - **l_blacklist = dap_config_get_array_str(l_cfg, "ledger", "hard_reject_list", &l_blacklist_size); - for (i = 0; i < l_blacklist_size; ++i) { - dap_ledger_hal_item_t *l_item = DAP_NEW_Z(dap_ledger_hal_item_t); - dap_chain_hash_fast_from_str(l_blacklist[i], &l_item->hash); - HASH_ADD(hh, l_ledger_pvt->hrl_items, hash, sizeof(dap_hash_fast_t), l_item); - } - for (i = 0; i < l_whitelist_size; ++i) { - dap_ledger_hal_item_t *l_item = DAP_NEW_Z(dap_ledger_hal_item_t); - dap_chain_hash_fast_from_str(l_whitelist[i], &l_item->hash); - HASH_ADD(hh, l_ledger_pvt->hal_items, hash, sizeof(dap_hash_fast_t), l_item); - } - dap_config_close(l_cfg); - log_it(L_DEBUG, "Chain %s.%s has %d datums in HAL and %d datums in HRL", a_net->pub.name, l_entry_name, l_whitelist_size, l_blacklist_size); - } - } - DAP_DELETE (l_entry_name); - } - closedir(l_chains_dir); - - if ( l_ledger_pvt->cached ) - // load ledger cache from GDB - dap_ledger_load_cache(l_ledger); -#endif - - return l_ledger; -} - -enum ledger_permissions { - LEDGER_PERMISSION_RECEIVER_ALLOWED, - LEDGER_PERMISSION_RECEIVER_BLOCKED, - LEDGER_PERMISSION_SENDER_ALLOWED, - LEDGER_PERMISSION_SENDER_BLOCKED -}; - -/** - * @brief dap_ledger_permissions_check - * @param a_token_item - * @param a_permission_id - * @param a_data - * @param a_data_size - * @return - */ -static bool s_ledger_permissions_check(dap_ledger_token_item_t *a_token_item, enum ledger_permissions a_permission_id, dap_chain_addr_t *a_addr) -{ - dap_chain_addr_t *l_addrs = NULL; - size_t l_addrs_count = 0; - switch (a_permission_id) { - case LEDGER_PERMISSION_RECEIVER_ALLOWED: - l_addrs = a_token_item->tx_recv_allow; - l_addrs_count = a_token_item->tx_recv_allow_size; - break; - case LEDGER_PERMISSION_RECEIVER_BLOCKED: - l_addrs = a_token_item->tx_recv_block; - l_addrs_count = a_token_item->tx_recv_block_size; - break; - case LEDGER_PERMISSION_SENDER_ALLOWED: - l_addrs = a_token_item->tx_send_allow; - l_addrs_count = a_token_item->tx_send_allow_size; - break; - case LEDGER_PERMISSION_SENDER_BLOCKED: - l_addrs = a_token_item->tx_send_block; - l_addrs_count = a_token_item->tx_send_block_size; - break; - } - for (size_t n = 0; n < l_addrs_count; n++) - if (dap_chain_addr_compare(l_addrs + n, a_addr)) - return true; - return false; -} - -dap_ledger_check_error_t s_ledger_addr_check(dap_ledger_token_item_t *a_token_item, dap_chain_addr_t *a_addr, bool a_receive) -{ - dap_return_val_if_fail(a_token_item && a_addr, DAP_LEDGER_CHECK_INVALID_ARGS); - if (dap_chain_addr_is_blank(a_addr)) - return DAP_LEDGER_CHECK_OK; - if (a_receive) { - if ((a_token_item->flags & DAP_CHAIN_DATUM_TOKEN_FLAG_ALL_RECEIVER_BLOCKED) || - (a_token_item->flags & DAP_CHAIN_DATUM_TOKEN_FLAG_ALL_RECEIVER_FROZEN)) { - // Check we are in white list - if (!s_ledger_permissions_check(a_token_item, LEDGER_PERMISSION_RECEIVER_ALLOWED, a_addr)) - return DAP_LEDGER_CHECK_ADDR_FORBIDDEN; - } else if ((a_token_item->flags & DAP_CHAIN_DATUM_TOKEN_FLAG_ALL_RECEIVER_ALLOWED) || - (a_token_item->flags & DAP_CHAIN_DATUM_TOKEN_FLAG_ALL_RECEIVER_UNFROZEN)) { - // Check we are in black list - if (s_ledger_permissions_check(a_token_item, LEDGER_PERMISSION_RECEIVER_BLOCKED, a_addr)) - return DAP_LEDGER_CHECK_ADDR_FORBIDDEN; - } - } else { - if ((a_token_item->flags & DAP_CHAIN_DATUM_TOKEN_FLAG_ALL_SENDER_BLOCKED) || - (a_token_item->flags & DAP_CHAIN_DATUM_TOKEN_FLAG_ALL_SENDER_FROZEN)) { - // Check we are in white list - if (!s_ledger_permissions_check(a_token_item, LEDGER_PERMISSION_SENDER_ALLOWED, a_addr)) - return DAP_LEDGER_CHECK_ADDR_FORBIDDEN; - } else if ((a_token_item->flags & DAP_CHAIN_DATUM_TOKEN_FLAG_ALL_SENDER_ALLOWED) || - (a_token_item->flags & DAP_CHAIN_DATUM_TOKEN_FLAG_ALL_SENDER_UNFROZEN)) { - // Check we are in black list - if (s_ledger_permissions_check(a_token_item, LEDGER_PERMISSION_SENDER_BLOCKED, a_addr)) - return DAP_LEDGER_CHECK_ADDR_FORBIDDEN; - } - } - return DAP_LEDGER_CHECK_OK; -} - -int s_emission_add_check(dap_ledger_t *a_ledger, byte_t *a_token_emission, size_t a_token_emission_size, dap_chain_hash_fast_t *a_emission_hash, - dap_chain_datum_token_emission_t **a_emission, dap_ledger_token_item_t **a_token_item) -{ - dap_return_val_if_fail(a_token_emission && a_token_emission_size, DAP_LEDGER_CHECK_INVALID_ARGS); - size_t l_emission_size = a_token_emission_size; - dap_chain_datum_token_emission_t *l_emission = dap_chain_datum_emission_read(a_token_emission, &l_emission_size); - if (!l_emission) - return DAP_LEDGER_CHECK_INVALID_SIZE; - if (l_emission->hdr.version < 3 && !a_token_item) { // It's mempool check - log_it(L_WARNING, "Legacy emission version %hhu isn't supported for a new emissions", l_emission->hdr.version); - DAP_DELETE(l_emission); - return DAP_LEDGER_EMISSION_CHECK_LEGACY_FORBIDDEN; - } - dap_ledger_token_item_t *l_token_item = s_ledger_find_token(a_ledger, l_emission->hdr.ticker); - if (!l_token_item) { - log_it(L_ERROR, "Check emission: token %s was not found", l_emission->hdr.ticker); - DAP_DELETE(l_emission); - return DAP_LEDGER_CHECK_TICKER_NOT_FOUND; - } - dap_ledger_token_emission_item_t *l_token_emission_item = NULL; - // check if such emission is already present in table - pthread_rwlock_rdlock(&l_token_item->token_emissions_rwlock); - HASH_FIND(hh, l_token_item->token_emissions, a_emission_hash, sizeof(*a_emission_hash), l_token_emission_item); - pthread_rwlock_unlock(&l_token_item->token_emissions_rwlock); - if (l_token_emission_item) { - debug_if(s_debug_more, L_WARNING, "Can't add token emission datum of %s %s ( %s ): already present in cache", - dap_uint256_to_char(l_emission->hdr.value, NULL), l_emission->hdr.ticker, - dap_chain_hash_fast_to_str_static(a_emission_hash)); - DAP_DELETE(l_emission); - return DAP_LEDGER_CHECK_ALREADY_CACHED; - } - - if (!PVT(a_ledger)->check_token_emission) - goto ret_success; - - // Check emission correctness - if (IS_ZERO_256((l_emission->hdr.value))) { - log_it(L_ERROR, "Emission check: zero %s emission value", l_token_item->ticker); - DAP_DELETE(l_emission); - return DAP_LEDGER_CHECK_ZERO_VALUE; - } - - if (!s_ledger_token_supply_check(l_token_item, l_emission->hdr.value)) { - DAP_DELETE(l_emission); - return DAP_LEDGER_EMISSION_CHECK_VALUE_EXCEEDS_CURRENT_SUPPLY; - } - - //additional check for private tokens - if((l_token_item->subtype == DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PRIVATE) - || (l_token_item->subtype == DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE)) { - dap_ledger_check_error_t ret = s_ledger_addr_check(l_token_item, &l_emission->hdr.address, true); - if (ret == DAP_LEDGER_CHECK_ADDR_FORBIDDEN) { - log_it(L_WARNING, "Address %s is not in allowed to receive for emission of token %s", - dap_chain_addr_to_str_static(&l_emission->hdr.address), l_token_item->ticker); - DAP_DELETE(l_emission); - return ret; - } - } - switch (l_emission->hdr.type) { - - case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_AUTH: { - size_t l_sign_data_check_size = sizeof(dap_chain_datum_token_emission_t) + l_emission->data.type_auth.tsd_total_size >= sizeof(dap_chain_datum_token_emission_t) - ? sizeof(dap_chain_datum_token_emission_t) + l_emission->data.type_auth.tsd_total_size : 0; - if (l_sign_data_check_size > l_emission_size) { - if (!s_check_hal(a_ledger, a_emission_hash)) { - log_it(L_WARNING, "Incorrect size %zu of datum emission, expected at least %zu", l_emission_size, l_sign_data_check_size); - DAP_DELETE(l_emission); - return DAP_LEDGER_CHECK_INVALID_SIZE; - } - goto ret_success; - } - size_t l_emission_check_size = sizeof(dap_chain_datum_token_emission_t) + l_emission->data.type_auth.tsd_n_signs_size >= sizeof(dap_chain_datum_token_emission_t) - ? sizeof(dap_chain_datum_token_emission_t) + l_emission->data.type_auth.tsd_n_signs_size : 0; - if (l_emission_check_size != l_emission_size) { - log_it(L_WARNING, "Incorrect size %zu of datum emission, must be %zu", l_emission_size, l_emission_check_size); - DAP_DELETE(l_emission); - return DAP_LEDGER_CHECK_INVALID_SIZE; - } - size_t l_signs_unique = l_emission->data.type_auth.signs_count; - dap_sign_t **l_signs = dap_sign_get_unique_signs(l_emission->tsd_n_signs + l_emission->data.type_auth.tsd_total_size, - l_emission->data.type_auth.tsd_n_signs_size, &l_signs_unique); - if (l_signs_unique < l_token_item->auth_signs_valid) { - - DAP_DELETE(l_signs); - - if (!s_check_hal(a_ledger, a_emission_hash)) { - - log_it(L_WARNING, "The number of unique token signs %zu is less than total token signs set to %zu", - l_signs_unique, l_token_item->auth_signs_total); - DAP_DELETE(l_emission); - return DAP_LEDGER_CHECK_NOT_ENOUGH_VALID_SIGNS; - } - - goto ret_success; - } - size_t l_sign_auth_count = l_emission->data.type_auth.signs_count; - size_t l_sign_auth_size = l_emission->data.type_auth.tsd_n_signs_size; - if (l_emission->hdr.version < 3) { - l_sign_data_check_size = sizeof(l_emission->hdr); - } else { - l_emission->data.type_auth.signs_count = 0; - l_emission->data.type_auth.tsd_n_signs_size = 0; - } - size_t l_aproves = 0; - for (uint16_t i = 0; i < l_signs_unique; i++) { - for (uint16_t k = 0; k < l_token_item->auth_signs_total; k++) { - if (dap_pkey_compare_with_sign(l_token_item->auth_pkeys[k], l_signs[i])) { - // Verify if token emission is signed - if (!dap_sign_verify(l_signs[i], l_emission, l_sign_data_check_size)) - l_aproves++; - break; - } - } - } - if (l_emission->hdr.version >= 3) { - l_emission->data.type_auth.signs_count = l_sign_auth_count; - l_emission->data.type_auth.tsd_n_signs_size = l_sign_auth_size; - } - DAP_DELETE(l_signs); - if (l_aproves < l_token_item->auth_signs_valid && - !s_check_hal(a_ledger, a_emission_hash)) { - log_it(L_WARNING, "Emission of %s datoshi of %s:%s is wrong: only %zu valid aproves when %zu need", - dap_uint256_to_char(l_emission->hdr.value, NULL), a_ledger->net->pub.name, l_emission->hdr.ticker, - l_aproves, l_token_item->auth_signs_valid); - debug_if(s_debug_more, L_ATT, "!!! Datum hash for HAL: %s", dap_chain_hash_fast_to_str_static(a_emission_hash)); - DAP_DELETE(l_emission); - return DAP_LEDGER_CHECK_NOT_ENOUGH_VALID_SIGNS; - } - } break; - - default: - log_it(L_ERROR, "Checking emission of type %s not implemented", dap_chain_datum_emission_type_str(l_emission->hdr.type)); - DAP_DELETE(l_emission); - return DAP_LEDGER_CHECK_PARSE_ERROR; - } - -ret_success: - if (a_token_item) - *a_token_item = l_token_item; - if (a_emission) - *a_emission = l_emission; - else - DAP_DELETE(l_emission); - - return DAP_LEDGER_CHECK_OK; -} - -int dap_ledger_token_emission_add_check(dap_ledger_t *a_ledger, byte_t *a_token_emission, size_t a_token_emission_size, dap_chain_hash_fast_t *a_emission_hash) -{ - return s_emission_add_check(a_ledger, a_token_emission, a_token_emission_size, a_emission_hash, NULL, NULL); -} - -static void s_ledger_emission_cache_update(dap_ledger_t *a_ledger, dap_ledger_token_emission_item_t *a_emission_item) -{ - if (!PVT(a_ledger)->cached) - return; - char *l_gdb_group = dap_ledger_get_gdb_group(a_ledger, DAP_LEDGER_EMISSIONS_STR); - size_t l_cache_size = a_emission_item->datum_token_emission_size + sizeof(dap_hash_fast_t); - uint8_t *l_cache = DAP_NEW_STACK_SIZE(uint8_t, l_cache_size); - memcpy(l_cache, &a_emission_item->tx_used_out, sizeof(dap_hash_fast_t)); - memcpy(l_cache + sizeof(dap_hash_fast_t), a_emission_item->datum_token_emission, a_emission_item->datum_token_emission_size); - char l_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE]; - dap_chain_hash_fast_to_str(&a_emission_item->datum_token_emission_hash, l_hash_str, sizeof(l_hash_str)); - if (dap_global_db_set(l_gdb_group, l_hash_str, l_cache, l_cache_size, false, NULL, NULL)) { - log_it(L_WARNING, "Ledger cache mismatch"); - } - DAP_DELETE(l_gdb_group); -} - -/** - * @brief dap_ledger_token_emission_add - * @param a_token_emission - * @param a_token_emision_size - * @return - */ - -int dap_ledger_token_emission_add(dap_ledger_t *a_ledger, byte_t *a_token_emission, size_t a_token_emission_size, dap_hash_fast_t *a_emission_hash) -{ - dap_ledger_token_item_t *l_token_item = NULL; - dap_chain_datum_token_emission_t *l_emission = NULL; - int l_ret = s_emission_add_check(a_ledger, a_token_emission, a_token_emission_size, a_emission_hash, &l_emission, &l_token_item); - if (l_ret != DAP_LEDGER_CHECK_OK) - return l_ret; - dap_ledger_token_emission_item_t *l_token_emission_item = NULL; - // check if such emission is already present in table - pthread_rwlock_wrlock(&l_token_item->token_emissions_rwlock); - HASH_FIND(hh, l_token_item->token_emissions, a_emission_hash, sizeof(*a_emission_hash), l_token_emission_item); - if (l_token_emission_item) { - pthread_rwlock_unlock(&l_token_item->token_emissions_rwlock); - log_it(L_ERROR, "Duplicate token emission datum of %s %s ( %s )", - dap_uint256_to_char(l_emission->hdr.value, NULL), l_emission->hdr.ticker, dap_hash_fast_to_str_static(a_emission_hash)); - DAP_DELETE(l_emission); - return DAP_LEDGER_CHECK_APPLY_ERROR; - } - l_token_emission_item = DAP_NEW_Z(dap_ledger_token_emission_item_t); - if (!l_token_emission_item) { - pthread_rwlock_unlock(&l_token_item->token_emissions_rwlock); - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; - } - l_token_emission_item->datum_token_emission = l_emission; - l_token_emission_item->datum_token_emission_hash = *a_emission_hash; - HASH_ADD(hh, l_token_item->token_emissions, datum_token_emission_hash, sizeof(*a_emission_hash), l_token_emission_item); - //Update value in ledger memory object - if (!s_ledger_token_supply_check_update(a_ledger, l_token_item, l_emission->hdr.value, false)) { - HASH_DEL(l_token_item->token_emissions, l_token_emission_item); - pthread_rwlock_unlock(&l_token_item->token_emissions_rwlock); - DAP_DELETE(l_emission); - DAP_DELETE(l_token_emission_item); - return DAP_LEDGER_CHECK_APPLY_ERROR; - } - pthread_rwlock_unlock(&l_token_item->token_emissions_rwlock); - // Add it to cache - s_ledger_emission_cache_update(a_ledger, l_token_emission_item); - if (s_debug_more) { - const char *l_balance; dap_uint256_to_char(l_token_emission_item->datum_token_emission->hdr.value, &l_balance); - log_it(L_NOTICE, "Added token emission datum to emissions cache: type=%s value=%s token=%s to_addr=%s", - dap_chain_datum_emission_type_str(l_emission->hdr.type), - l_balance, l_emission->hdr.ticker, - dap_chain_addr_to_str_static(&(l_emission->hdr.address))); - } - if (PVT(a_ledger)->threshold_enabled) - s_threshold_txs_proc(a_ledger); - return DAP_LEDGER_CHECK_OK; -} - -void s_ledger_stake_lock_cache_update(dap_ledger_t *a_ledger, dap_ledger_stake_lock_item_t *a_stake_lock_item) -{ - if (!PVT(a_ledger)->cached) - return; - char l_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE]; - dap_chain_hash_fast_to_str(&a_stake_lock_item->tx_for_stake_lock_hash, l_hash_str, sizeof(l_hash_str)); - char *l_group = dap_ledger_get_gdb_group(a_ledger, DAP_LEDGER_STAKE_LOCK_STR); - if (dap_global_db_set(l_group, l_hash_str, &a_stake_lock_item->tx_used_out, sizeof(dap_hash_fast_t), false, NULL, NULL)) - log_it(L_WARNING, "Ledger cache mismatch"); - DAP_DEL_Z(l_group); -} - -int dap_ledger_emission_for_stake_lock_item_add(dap_ledger_t *a_ledger, const dap_chain_hash_fast_t *a_tx_hash) -{ - dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); - dap_ledger_stake_lock_item_t *l_new_stake_lock_emission = NULL; - pthread_rwlock_rdlock(&l_ledger_pvt->stake_lock_rwlock); - HASH_FIND(hh, l_ledger_pvt->emissions_for_stake_lock, a_tx_hash, sizeof(dap_hash_fast_t), - l_new_stake_lock_emission); - pthread_rwlock_unlock(&l_ledger_pvt->stake_lock_rwlock); - if (l_new_stake_lock_emission) { - return -1; - } - l_new_stake_lock_emission = DAP_NEW_Z(dap_ledger_stake_lock_item_t); - if (!l_new_stake_lock_emission) { - if (s_debug_more) { - log_it(L_ERROR, "Error: memory allocation when try adding item 'dap_ledger_stake_lock_item_t' to hash-table"); - } - return -13; - } - l_new_stake_lock_emission->tx_for_stake_lock_hash = *a_tx_hash; - pthread_rwlock_wrlock(&l_ledger_pvt->stake_lock_rwlock); - HASH_ADD(hh, l_ledger_pvt->emissions_for_stake_lock, tx_for_stake_lock_hash, sizeof(dap_chain_hash_fast_t), l_new_stake_lock_emission); - pthread_rwlock_unlock(&l_ledger_pvt->stake_lock_rwlock); - - s_ledger_stake_lock_cache_update(a_ledger, l_new_stake_lock_emission); - - return 0; - -} - -dap_ledger_stake_lock_item_t *s_emissions_for_stake_lock_item_find(dap_ledger_t *a_ledger, const dap_chain_hash_fast_t *a_token_emission_hash) -{ - dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); - dap_ledger_stake_lock_item_t *l_new_stake_lock_emission = NULL; - pthread_rwlock_rdlock(&l_ledger_pvt->stake_lock_rwlock); - HASH_FIND(hh, l_ledger_pvt->emissions_for_stake_lock, a_token_emission_hash, sizeof(dap_chain_hash_fast_t), - l_new_stake_lock_emission); - pthread_rwlock_unlock(&l_ledger_pvt->stake_lock_rwlock); - return l_new_stake_lock_emission; -} - - -int dap_ledger_token_emission_load(dap_ledger_t *a_ledger, byte_t *a_token_emission, - size_t a_token_emission_size, dap_hash_fast_t *a_token_emission_hash) -{ - if (dap_chain_net_get_load_mode(a_ledger->net)) { - dap_ledger_token_emission_item_t *l_token_emission_item = NULL; - dap_ledger_token_item_t *l_token_item, *l_item_tmp; - pthread_rwlock_rdlock(&PVT(a_ledger)->tokens_rwlock); - HASH_ITER(hh, PVT(a_ledger)->tokens, l_token_item, l_item_tmp) { - pthread_rwlock_rdlock(&l_token_item->token_emissions_rwlock); - HASH_FIND(hh, l_token_item->token_emissions, a_token_emission_hash, sizeof(*a_token_emission_hash), - l_token_emission_item); - pthread_rwlock_unlock(&l_token_item->token_emissions_rwlock); - if (l_token_emission_item) { - pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); - return 0; - } - } - pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); - } - return dap_ledger_token_emission_add(a_ledger, a_token_emission, a_token_emission_size, a_token_emission_hash); -} - -dap_ledger_token_emission_item_t *s_emission_item_find(dap_ledger_t *a_ledger, - const char *a_token_ticker, const dap_chain_hash_fast_t *a_token_emission_hash, dap_ledger_token_item_t **a_token_item) -{ - dap_ledger_token_item_t *l_token_item = s_ledger_find_token(a_ledger, a_token_ticker); - if (!l_token_item) - return NULL; - else if (a_token_item) - *a_token_item = l_token_item; - dap_ledger_token_emission_item_t *l_token_emission_item = NULL; - pthread_rwlock_rdlock(&l_token_item->token_emissions_rwlock); - HASH_FIND(hh, l_token_item->token_emissions, a_token_emission_hash, sizeof(*a_token_emission_hash), l_token_emission_item); - pthread_rwlock_unlock(&l_token_item->token_emissions_rwlock); - return l_token_emission_item; -} - -/** - * @brief dap_ledger_token_emission_find - * @param a_token_ticker - * @param a_token_emission_hash - * @return - */ -dap_chain_datum_token_emission_t *dap_ledger_token_emission_find(dap_ledger_t *a_ledger, const dap_chain_hash_fast_t *a_token_emission_hash) -{ - dap_ledger_token_emission_item_t *l_emission_item = NULL; - pthread_rwlock_rdlock(&PVT(a_ledger)->tokens_rwlock); - for (dap_ledger_token_item_t *l_item = PVT(a_ledger)->tokens; l_item; l_item = l_item->hh.next) { - l_emission_item = s_emission_item_find(a_ledger, l_item->ticker, a_token_emission_hash, NULL); - if (l_emission_item) - break; - } - pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); - return l_emission_item ? l_emission_item->datum_token_emission : NULL; -} - -/** - * @brief dap_ledger_set_local_cell_id - * @param a_local_cell_id - */ -void dap_ledger_set_local_cell_id(dap_ledger_t *a_ledger, dap_chain_cell_id_t a_local_cell_id) -{ - PVT(a_ledger)->local_cell_id.uint64 = a_local_cell_id.uint64; -} - -/** - * @brief dap_ledger_tx_get_token_ticker_by_hash - * @param a_ledger - * @param a_tx_hash - * @return - */ -const char* dap_ledger_tx_get_token_ticker_by_hash(dap_ledger_t *a_ledger,dap_chain_hash_fast_t *a_tx_hash) -{ - if(!a_ledger || !a_tx_hash) - return NULL; - dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); - - if ( dap_hash_fast_is_blank(a_tx_hash) ) - return NULL; - - dap_ledger_tx_item_t *l_item = NULL; - unsigned l_hash_value; - HASH_VALUE(a_tx_hash, sizeof(*a_tx_hash), l_hash_value); - pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); - HASH_FIND_BYHASHVALUE(hh, l_ledger_pvt->ledger_items, a_tx_hash, sizeof(*a_tx_hash), l_hash_value, l_item); - pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); - return l_item ? l_item->cache_data.token_ticker : NULL; -} - -/** - * @brief Get list of all tickets for ledger and address. If address is NULL returns all the tockens present in system - * @param a_ledger - * @param a_addr - * @param a_tickers - * @param a_tickers_size - */ -void dap_ledger_addr_get_token_ticker_all(dap_ledger_t *a_ledger, dap_chain_addr_t * a_addr, - char *** a_tickers, size_t * a_tickers_size) -{ - if (a_addr == NULL){ // Get all tockens - pthread_rwlock_rdlock(&PVT(a_ledger)->tokens_rwlock); - size_t l_count = HASH_COUNT(PVT(a_ledger)->tokens); - if (l_count && a_tickers){ - dap_ledger_token_item_t * l_token_item, *l_tmp; - char **l_tickers = DAP_NEW_Z_SIZE(char*, l_count * sizeof(char*)); - if (!l_tickers) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - pthread_rwlock_unlock(&PVT(a_ledger)->balance_accounts_rwlock); - return; - } - l_count = 0; - HASH_ITER(hh, PVT(a_ledger)->tokens, l_token_item, l_tmp) { - l_tickers[l_count] = dap_strdup(l_token_item->ticker); - l_count++; - } - *a_tickers = l_tickers; - } - pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); - if(a_tickers_size) - *a_tickers_size = l_count; - }else{ // Calc only tokens from address balance - dap_ledger_wallet_balance_t *wallet_balance, *tmp; - size_t l_count = HASH_COUNT(PVT(a_ledger)->balance_accounts); - if(l_count && a_tickers){ - char **l_tickers = DAP_NEW_Z_SIZE(char*, l_count * sizeof(char*)); - if (!l_tickers) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - pthread_rwlock_unlock(&PVT(a_ledger)->balance_accounts_rwlock); - return; - } - l_count = 0; - const char *l_addr = dap_chain_addr_to_str_static(a_addr); - pthread_rwlock_rdlock(&PVT(a_ledger)->balance_accounts_rwlock); - HASH_ITER(hh, PVT(a_ledger)->balance_accounts, wallet_balance, tmp) { - char **l_keys = dap_strsplit(wallet_balance->key, " ", -1); - if (!dap_strcmp(l_keys[0], l_addr)) { - l_tickers[l_count] = dap_strdup(wallet_balance->token_ticker); - ++l_count; - } - dap_strfreev(l_keys); - } - pthread_rwlock_unlock(&PVT(a_ledger)->balance_accounts_rwlock); - *a_tickers = l_tickers; - } - if(a_tickers_size) - *a_tickers_size = l_count; - } -} - -const char *dap_ledger_get_description_by_ticker(dap_ledger_t *a_ledger, const char *a_token_ticker){ - if (!a_ledger || !a_token_ticker) - return NULL; - return s_ledger_find_token(a_ledger, a_token_ticker)->description; -} - -/** - * Get transaction in the cache by hash - * - * return transaction, or NULL if transaction not found in the cache - */ -dap_chain_datum_tx_t* dap_ledger_tx_find_datum_by_hash(dap_ledger_t *a_ledger, const dap_chain_hash_fast_t *a_tx_hash, - dap_ledger_tx_item_t **a_item_out, bool a_unspent_only) -{ - if ( !a_tx_hash || dap_hash_fast_is_blank(a_tx_hash) ) - return NULL; - dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); - dap_chain_datum_tx_t *l_tx_ret = NULL; - dap_ledger_tx_item_t *l_tx_item = NULL; - pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); - HASH_FIND(hh, l_ledger_pvt->ledger_items, a_tx_hash, sizeof(dap_chain_hash_fast_t), l_tx_item); - pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); - if(l_tx_item) { - if (!a_unspent_only || !l_tx_item->cache_data.ts_spent) { - l_tx_ret = l_tx_item->tx; - if(a_item_out) - *a_item_out = l_tx_item; - } - } - return l_tx_ret; -} - -dap_hash_fast_t dap_ledger_get_first_chain_tx_hash(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_chain_tx_out_cond_t *a_cond_out) -{ - dap_hash_fast_t l_hash = { }, l_hash_tmp; - if (!a_ledger || !a_cond_out || !a_tx){ - log_it(L_ERROR, "Argument is NULL in dap_ledger_get_first_chain_tx_hash()"); - return l_hash; - } - dap_chain_datum_tx_t *l_prev_tx = a_tx; - byte_t *l_iter = a_tx->tx_items; - while (( l_iter = dap_chain_datum_tx_item_get(l_prev_tx, NULL, l_iter, TX_ITEM_TYPE_IN_COND, NULL) )) { - l_hash_tmp = ((dap_chain_tx_in_cond_t *)l_iter)->header.tx_prev_hash; - if ( dap_hash_fast_is_blank(&l_hash_tmp) ) - return l_hash_tmp; - if (( l_prev_tx = dap_ledger_tx_find_by_hash(a_ledger, &l_hash_tmp) ) && - ( dap_chain_datum_tx_out_cond_get(l_prev_tx, a_cond_out->header.subtype, NULL) )) { - l_hash = l_hash_tmp; - l_iter = l_prev_tx->tx_items; - } - } - return l_hash; -} - -dap_hash_fast_t dap_ledger_get_final_chain_tx_hash(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_cond_type, dap_chain_hash_fast_t *a_tx_hash) -{ - dap_chain_hash_fast_t l_hash = { }, l_hash_tmp; - if (!a_ledger || !a_tx_hash || dap_hash_fast_is_blank(a_tx_hash)) - return l_hash; - - dap_chain_datum_tx_t *l_tx = NULL; - l_hash = *a_tx_hash; - int l_out_num = 0; - dap_ledger_tx_item_t *l_item = NULL; - while (( l_tx = dap_ledger_tx_find_datum_by_hash(a_ledger, &l_hash, &l_item, false) )) { - if ( !dap_chain_datum_tx_out_cond_get(l_tx, a_cond_type, &l_out_num) - || dap_hash_fast_is_blank(&l_item->cache_data.tx_hash_spent_fast[l_out_num])) - break; - - l_hash = l_item->cache_data.tx_hash_spent_fast[l_out_num]; - } - return l_hash; -} - -/** - * Check whether used 'out' items (local function) - */ -static bool s_ledger_tx_hash_is_used_out_item(dap_ledger_tx_item_t *a_item, int a_idx_out, dap_hash_fast_t *a_out_spender_hash) -{ - if (!a_item || !a_item->cache_data.n_outs) { - //log_it(L_DEBUG, "list_cached_item is NULL"); - return true; - } - if(a_idx_out >= MAX_OUT_ITEMS) { - if(s_debug_more) - log_it(L_ERROR, "Too big index(%d) of 'out' items (max=%d)", a_idx_out, MAX_OUT_ITEMS); - } - assert(a_idx_out < MAX_OUT_ITEMS); - // if there are used 'out' items - if ((a_item->cache_data.n_outs_used > 0) && !dap_hash_fast_is_blank(&(a_item->cache_data.tx_hash_spent_fast[a_idx_out]))) { - if (a_out_spender_hash) - *a_out_spender_hash = a_item->cache_data.tx_hash_spent_fast[a_idx_out]; - return true; - } - return false; -} - -static dap_ledger_reward_item_t *s_find_reward(dap_ledger_t *a_ledger, dap_ledger_reward_key_t *a_search_key) -{ - dap_ledger_reward_item_t *l_reward_item = NULL; - pthread_rwlock_rdlock(&PVT(a_ledger)->rewards_rwlock); - HASH_FIND(hh, PVT(a_ledger)->rewards, a_search_key, sizeof(*a_search_key), l_reward_item); - pthread_rwlock_unlock(&PVT(a_ledger)->rewards_rwlock); - return l_reward_item; -} - -bool dap_ledger_is_used_reward(dap_ledger_t *a_ledger, dap_hash_fast_t *a_block_hash, dap_hash_fast_t *a_sign_pkey_hash) -{ - dap_ledger_reward_key_t l_search_key = { .block_hash = *a_block_hash, .sign_pkey_hash = *a_sign_pkey_hash }; - return s_find_reward(a_ledger, &l_search_key); -} - -static int s_callback_sign_compare(dap_list_t *a_list_elem, dap_list_t *a_sign_elem) -{ - dap_pkey_t *l_key = (dap_pkey_t *)a_list_elem->data; - dap_sign_t *l_sign = (dap_sign_t *)a_sign_elem->data; - if (!l_key || !l_sign) { - log_it(L_CRITICAL, "Invalid argument"); - return -1; - } - return !dap_pkey_compare_with_sign(l_key, l_sign); -} - -bool dap_ledger_tx_poa_signed(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx) -{ - dap_chain_tx_sig_t *l_tx_sig = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get(a_tx, NULL, 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); - return dap_list_find(a_ledger->net->pub.keys, l_sign, s_callback_sign_compare); -} - -inline static bool s_ledger_check_token_ticker(const char *a_ticker) -{ - const char *c = a_ticker; - for (int i = 0; i < DAP_CHAIN_TICKER_SIZE_MAX; i++, c++) - if (*c == '\0') - return true; - return false; -} - -/* -services we know now -0x01 - VPN -0x02 - xchange -0x03, 0x13 - pos_delegate -0x04 bridge -0x.05 - custom datum -0x06 voting -0x12 - stake_lock -*/ - -const char *dap_ledger_tx_action_str(dap_chain_tx_tag_action_type_t a_tag) -{ - - if (a_tag == DAP_CHAIN_TX_TAG_ACTION_UNKNOWN) return "unknown"; - if (a_tag == DAP_CHAIN_TX_TAG_ACTION_TRANSFER_REGULAR) return "regular"; - if (a_tag == DAP_CHAIN_TX_TAG_ACTION_TRANSFER_COMISSION) return "comission"; - if (a_tag == DAP_CHAIN_TX_TAG_ACTION_TRANSFER_CROSSCHAIN) return "crosschain"; - if (a_tag == DAP_CHAIN_TX_TAG_ACTION_TRANSFER_REWARD) return "reward"; - if (a_tag == DAP_CHAIN_TX_TAG_ACTION_OPEN) return "open"; - if (a_tag == DAP_CHAIN_TX_TAG_ACTION_USE) return "use"; - if (a_tag == DAP_CHAIN_TX_TAG_ACTION_EXTEND) return "extend"; - if (a_tag == DAP_CHAIN_TX_TAG_ACTION_CLOSE) return "close"; - if (a_tag == DAP_CHAIN_TX_TAG_ACTION_CHANGE) return "change"; - - return "WTFSUBTAG"; - -} - -dap_chain_tx_tag_action_type_t dap_ledger_tx_action_str_to_action_t(const char *a_str) -{ - if (!a_str) - return DAP_CHAIN_TX_TAG_ACTION_UNKNOWN; - - if (strcmp("unknown", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_UNKNOWN; - if (strcmp("regular", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_TRANSFER_REGULAR; - if (strcmp("comission", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_TRANSFER_COMISSION; - if (strcmp("crosschain", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_TRANSFER_CROSSCHAIN; - if (strcmp("reward", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_TRANSFER_REWARD; - if (strcmp("open", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_OPEN; - if (strcmp("use", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_USE; - if (strcmp("extend", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_EXTEND; - if (strcmp("close", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_CLOSE; - if (strcmp("change", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_CHANGE; - - return DAP_CHAIN_TX_TAG_ACTION_UNKNOWN; -} - -bool dap_ledger_tx_service_info(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_hash, - dap_chain_srv_uid_t *a_uid, char **a_service_name, dap_chain_tx_tag_action_type_t *a_action) -{ - //find tx - dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); - dap_ledger_tx_item_t *l_tx_item = NULL; - pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); - HASH_FIND(hh, l_ledger_pvt->ledger_items, a_tx_hash, sizeof(dap_chain_hash_fast_t), l_tx_item); - pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); - - - if(l_tx_item) { - dap_ledger_service_info_t *l_sinfo = NULL; - pthread_rwlock_rdlock(&s_services_rwlock); - HASH_FIND_INT(s_services, &l_tx_item->cache_data.tag, l_sinfo); - pthread_rwlock_unlock(&s_services_rwlock); - if (l_sinfo) - { - if(a_uid) *a_uid = l_sinfo->service_uid; - if (a_service_name) *a_service_name = l_sinfo->tag_str; - if (a_action) *a_action = l_tx_item->cache_data.action; - return true; - } - } - - if (a_action) *a_action = DAP_CHAIN_TX_TAG_ACTION_UNKNOWN; - return false; -} - - -bool dap_ledger_deduct_tx_tag(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, char **a_service_name, dap_chain_srv_uid_t *a_tag, dap_chain_tx_tag_action_type_t *a_action) -{ - dap_ledger_service_info_t *l_sinfo_current, *l_sinfo_tmp; - - - dap_chain_datum_tx_item_groups_t l_items_groups = {0}; - dap_chain_datum_tx_group_items(a_tx, &l_items_groups); - - bool l_res = false; - int l_deductions_ok = 0; - - pthread_rwlock_rdlock(&s_services_rwlock); - HASH_ITER(hh, s_services , l_sinfo_current, l_sinfo_tmp) { - dap_chain_tx_tag_action_type_t action = DAP_CHAIN_TX_TAG_ACTION_UNKNOWN; - if (l_sinfo_current->callback && l_sinfo_current->callback(a_ledger, a_tx, &l_items_groups, &action)){ - if (a_tag) *a_tag = l_sinfo_current->service_uid; - if (a_action) *a_action = action; - if (a_service_name) *a_service_name = l_sinfo_current->tag_str; - l_res = true; - l_deductions_ok ++; - } - } - pthread_rwlock_unlock(&s_services_rwlock); - - if (l_deductions_ok > 1) - { - char l_tx_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE]; - dap_chain_hash_fast_t l_tx_hash = dap_chain_node_datum_tx_calc_hash(a_tx); - dap_chain_hash_fast_to_str(&l_tx_hash, l_tx_hash_str, sizeof(l_tx_hash_str)); - - log_it(L_WARNING, "Transaction %s identyfied by multiple services (%d):", l_tx_hash_str, l_deductions_ok); - - pthread_rwlock_rdlock(&s_services_rwlock); - HASH_ITER(hh, s_services , l_sinfo_current, l_sinfo_tmp) { - dap_chain_tx_tag_action_type_t action = DAP_CHAIN_TX_TAG_ACTION_UNKNOWN; - if (l_sinfo_current->callback && l_sinfo_current->callback(a_ledger, a_tx, &l_items_groups,&action)) { - log_it(L_WARNING, "%s %s", l_sinfo_current->tag_str, dap_ledger_tx_action_str(action)); - } - } - - pthread_rwlock_unlock(&s_services_rwlock); - } - - dap_chain_datum_tx_group_items_free(&l_items_groups); - - return l_res; -} - -/** - * Checking a new transaction before adding to the cache - * - * return 0 OK, otherwise error - */ -// Checking a new transaction before adding to the cache -static int s_tx_cache_check(dap_ledger_t *a_ledger, - dap_chain_datum_tx_t *a_tx, - dap_hash_fast_t *a_tx_hash, - bool a_from_threshold, - dap_list_t **a_list_bound_items, - dap_list_t **a_list_tx_out, - char *a_main_ticker, - dap_chain_srv_uid_t *a_tag, - dap_chain_tx_tag_action_type_t *a_action, - bool a_check_for_removing) -{ - dap_return_val_if_fail(a_ledger && a_tx && a_tx_hash, DAP_LEDGER_CHECK_INVALID_ARGS); - if (!a_from_threshold) { - dap_ledger_tx_item_t *l_ledger_item = NULL; - pthread_rwlock_rdlock(&PVT(a_ledger)->ledger_rwlock); - HASH_FIND(hh, PVT(a_ledger)->ledger_items, a_tx_hash, sizeof(dap_chain_hash_fast_t), l_ledger_item); - pthread_rwlock_unlock(&PVT(a_ledger)->ledger_rwlock); - if (l_ledger_item && !a_check_for_removing ) { // transaction already present in the cache list - if (s_debug_more) { - log_it(L_WARNING, "Transaction %s already present in the cache", dap_chain_hash_fast_to_str_static(a_tx_hash)); - if (a_tag) *a_tag = l_ledger_item->cache_data.tag; - if (a_action) *a_action = l_ledger_item->cache_data.action; - } - return DAP_LEDGER_CHECK_ALREADY_CACHED; - } else if (!l_ledger_item && a_check_for_removing) { // transaction already present in the cache list - debug_if(s_debug_more, L_WARNING, "Transaction %s not present in the cache. Can not delete it. Skip.", dap_chain_hash_fast_to_str_static(a_tx_hash)); - return DAP_LEDGER_TX_CHECK_FOR_REMOVING_CANT_FIND_TX; - } - } -/* - * Steps of checking for current transaction tx2 and every previous transaction tx1: - * 1. valid(tx2.dap_chain_datum_tx_sig.pkey) - * && - * 2. tx2.input != tx2.inputs.used - * && - * 3. !is_used_out(tx1.dap_chain_datum_tx_out) - * && - * 4. tx1.dap_chain_datum_tx_out.addr.data.key == tx2.dap_chain_datum_tx_sig.pkey for unconditional output - * \\ - * 5. tx1.dap_chain_datum_tx_out.condition == verify_svc_type(tx2) for conditional output - * && - * 6. sum( find (tx2.input.tx_prev_hash).output[tx2.input_tx_prev_idx].value ) == sum (tx2.outputs.value) per token - * && - * 7. valid(fee) -*/ - dap_list_t *l_list_bound_items = NULL; - dap_list_t *l_list_tx_out = NULL; - - // sum of values in 'out' items from the previous transactions - dap_ledger_tokenizer_t *l_values_from_prev_tx = NULL, *l_values_from_cur_tx = NULL, - *l_value_cur = NULL, *l_tmp = NULL, *l_res = NULL; - const char *l_token = NULL, *l_main_ticker = NULL; - - int l_err_num = DAP_LEDGER_CHECK_OK; - int l_prev_tx_count = 0; - - // 1. Verify signature in current transaction - if (!a_from_threshold && dap_chain_datum_tx_verify_sign(a_tx)) - return DAP_LEDGER_CHECK_NOT_ENOUGH_VALID_SIGNS; - - // ---------------------------------------------------------------- - // find all 'in' && 'in_cond' && 'in_ems' && 'in_reward' items in current transaction - dap_list_t *l_list_in = dap_chain_datum_tx_items_get(a_tx, TX_ITEM_TYPE_IN_ALL, &l_prev_tx_count); - if (!l_list_in) { - log_it(L_WARNING, "Tx check: no valid inputs found"); - return DAP_LEDGER_TX_CHECK_TX_NO_VALID_INPUTS; - } - dap_chain_hash_fast_t l_tx_first_sign_pkey_hash = {}; - dap_pkey_t *l_tx_first_sign_pkey = NULL; - bool l_girdled_ems_used = false; - uint256_t l_taxed_value = {}; - - if(a_tag) dap_ledger_deduct_tx_tag(a_ledger, a_tx, NULL, a_tag, a_action); - - // find all previous transactions - for (dap_list_t *it = l_list_in; it; it = it->next) { - dap_ledger_tx_bound_t *l_bound_item = DAP_NEW_Z(dap_ledger_tx_bound_t); - if (!l_bound_item) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - l_err_num = DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; - break; - } - l_list_bound_items = dap_list_append(l_list_bound_items, l_bound_item); - - uint8_t l_cond_type = *(uint8_t *)it->data; - l_bound_item->type = l_cond_type; - uint256_t l_value = uint256_0; - void *l_tx_prev_out = NULL; - dap_chain_datum_tx_t *l_tx_prev = NULL; - dap_ledger_token_emission_item_t *l_emission_item = NULL; - dap_ledger_stake_lock_item_t *l_stake_lock_emission = NULL; - bool l_girdled_ems = false; - - switch (l_cond_type) { - case TX_ITEM_TYPE_IN_EMS: { // It's the emission (base) TX - dap_chain_tx_in_ems_t *l_tx_in_ems = it->data; - l_token = l_tx_in_ems->header.ticker; - if (!s_ledger_check_token_ticker(l_token)) { - l_err_num = DAP_LEDGER_CHECK_TICKER_NOT_FOUND; - break; - } - dap_hash_fast_t *l_emission_hash = &l_tx_in_ems->header.token_emission_hash; - // 2. Check current transaction for doubles in input items list - for (dap_list_t *l_iter = it->next; l_iter; l_iter = l_iter->next) { - dap_chain_tx_in_ems_t *l_in_ems_check = l_iter->data; - if (l_in_ems_check->header.type == TX_ITEM_TYPE_IN_EMS && - dap_hash_fast_compare(&l_in_ems_check->header.token_emission_hash, l_emission_hash) && !a_check_for_removing) - { - debug_if(s_debug_more, L_ERROR, "Emission output already used in current tx"); - l_err_num = DAP_LEDGER_TX_CHECK_PREV_OUT_ALREADY_USED_IN_CURRENT_TX; - break; - } - } - if (l_err_num) - break; - if ((l_girdled_ems = dap_hash_fast_is_blank(l_emission_hash)) || - (l_stake_lock_emission = s_emissions_for_stake_lock_item_find(a_ledger, l_emission_hash))) { - dap_chain_datum_tx_t *l_tx_stake_lock = a_tx; - // 3. Check emission for STAKE_LOCK - if (!dap_hash_fast_is_blank(l_emission_hash)) { - dap_hash_fast_t cur_tx_hash; - dap_hash_fast(a_tx, dap_chain_datum_tx_get_size(a_tx), &cur_tx_hash); - if (!dap_hash_fast_is_blank(&l_stake_lock_emission->tx_used_out) && !a_check_for_removing) { - if (!dap_hash_fast_compare(&cur_tx_hash, &l_stake_lock_emission->tx_used_out)) - debug_if(s_debug_more, L_WARNING, "stake_lock_emission already present in cache for IN_EMS [%s]", l_token); - else - debug_if(s_debug_more, L_WARNING, "stake_lock_emission is used out for IN_EMS [%s]", l_token); - l_err_num = DAP_LEDGER_TX_CHECK_STAKE_LOCK_IN_EMS_ALREADY_USED; - break; - } - l_tx_stake_lock = dap_ledger_tx_find_by_hash(a_ledger, l_emission_hash); - } else { - // 2. The only allowed item with girdled emission - if (l_girdled_ems_used && !a_check_for_removing) { - debug_if(s_debug_more, L_WARNING, "stake_lock_emission is used out for IN_EMS [%s]", l_token); - l_err_num = DAP_LEDGER_TX_CHECK_STAKE_LOCK_IN_EMS_ALREADY_USED; - break; - } else - l_girdled_ems_used = true; - } - if (!l_tx_stake_lock) { - debug_if(s_debug_more, L_WARNING, "Not found stake_lock transaction"); - l_err_num = DAP_CHAIN_CS_VERIFY_CODE_TX_NO_EMISSION; - break; - } - - dap_ledger_token_item_t *l_delegated_item = s_ledger_find_token(a_ledger, l_token); - if (!l_delegated_item) { - debug_if(s_debug_more, L_WARNING, "Token [%s] not found", l_token); - l_err_num = DAP_LEDGER_CHECK_TICKER_NOT_FOUND; - break; - } - if (!l_delegated_item->is_delegated) { - debug_if(s_debug_more, L_WARNING, "Token [%s] not valid for stake_lock transaction", l_token); - l_err_num = DAP_LEDGER_TX_CHECK_STAKE_LOCK_INVALID_TOKEN; - break; - } - if (!dap_ledger_token_ticker_check(a_ledger, l_delegated_item->delegated_from)) { - debug_if(s_debug_more, L_WARNING, "Token [%s] not found", l_delegated_item->delegated_from); - l_err_num = DAP_LEDGER_CHECK_TICKER_NOT_FOUND; - break; - } - - if (l_girdled_ems) - l_main_ticker = l_delegated_item->delegated_from; - - dap_chain_tx_out_cond_t *l_tx_stake_lock_out_cond = dap_chain_datum_tx_out_cond_get(l_tx_stake_lock, DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK, NULL); - if (!l_tx_stake_lock_out_cond) { - debug_if(s_debug_more, L_WARNING, "No OUT_COND of stake_lock subtype for IN_EMS [%s]", l_tx_in_ems->header.ticker); - l_err_num = DAP_LEDGER_TX_CHECK_STAKE_LOCK_NO_OUT_COND_FOR_IN_EMS; - break; - } - uint256_t l_value_expected ={}; - if (MULT_256_COIN(l_tx_stake_lock_out_cond->header.value, l_delegated_item->emission_rate, &l_value_expected)) { - if (s_debug_more) { - char *l_emission_rate_str = dap_chain_balance_coins_print(l_delegated_item->emission_rate); - const char *l_locked_value_str; dap_uint256_to_char(l_tx_stake_lock_out_cond->header.value, &l_locked_value_str); - log_it( L_WARNING, "Multiplication overflow for %s emission: locked value %s emission rate %s", - l_tx_in_ems->header.ticker, l_locked_value_str, l_emission_rate_str); - DAP_DEL_Z(l_emission_rate_str); - } - l_err_num = DAP_LEDGER_CHECK_INTEGER_OVERFLOW; - break; - } - dap_chain_tx_out_ext_t *l_tx_out_ext = NULL; - uint256_t l_stake_lock_ems_value = {}; - int l_item_idx = 0; - do { - l_tx_out_ext = (dap_chain_tx_out_ext_t *)dap_chain_datum_tx_item_get(a_tx, &l_item_idx, NULL, TX_ITEM_TYPE_OUT_EXT, NULL); - if (!l_tx_out_ext) { - if (l_girdled_ems) { - debug_if(s_debug_more, L_WARNING, "No OUT_EXT for girdled IN_EMS [%s]", l_tx_in_ems->header.ticker); - l_err_num = DAP_LEDGER_TX_CHECK_NO_OUT_EXT_FOR_GIRDLED_IN_EMS; - } - break; - } - l_item_idx++; - } while (strcmp(l_tx_out_ext->token, l_token)); - if (!l_tx_out_ext) { - dap_chain_tx_out_t *l_tx_out = (dap_chain_tx_out_t *)dap_chain_datum_tx_item_get(a_tx, NULL, NULL, TX_ITEM_TYPE_OUT, NULL); - if (!l_tx_out) { - debug_if(true, L_WARNING, "Can't find OUT nor OUT_EXT item for base TX with IN_EMS [%s]", l_tx_in_ems->header.ticker); - l_err_num = DAP_LEDGER_TX_CHECK_NO_OUT_ITEMS_FOR_BASE_TX; - break; - } else - l_stake_lock_ems_value = l_tx_out->header.value; - } else - l_stake_lock_ems_value = l_tx_out_ext->header.value; - if (!s_ledger_token_supply_check(l_delegated_item, l_stake_lock_ems_value)) { - l_err_num = DAP_LEDGER_EMISSION_CHECK_VALUE_EXCEEDS_CURRENT_SUPPLY; - break; - } - if (!EQUAL_256(l_value_expected, l_stake_lock_ems_value)) { - // !!! A terrible legacy crutch, TODO !!! - SUM_256_256(l_value_expected, GET_256_FROM_64(10), &l_value_expected); - if (!EQUAL_256(l_value_expected, l_stake_lock_ems_value)) { - char *l_value_expected_str = dap_chain_balance_datoshi_print(l_value_expected); - char *l_locked_value_str = dap_chain_balance_datoshi_print(l_stake_lock_ems_value); - - debug_if(s_debug_more, L_WARNING, "Value %s != %s expected for [%s]",l_locked_value_str, l_value_expected_str, - l_tx_in_ems->header.ticker); - - DAP_DEL_Z(l_value_expected_str); - DAP_DEL_Z(l_locked_value_str); - l_err_num = DAP_LEDGER_TX_CHECK_STAKE_LOCK_UNEXPECTED_VALUE; - break; - } - } - if (!l_girdled_ems) { - // check tiker - const char *l_tx_ticker = dap_ledger_tx_get_token_ticker_by_hash(a_ledger, l_emission_hash); - if (!l_tx_ticker) { - debug_if(s_debug_more, L_WARNING, "No ticker found for stake_lock tx [expected '%s']", l_tx_in_ems->header.ticker); - l_err_num = DAP_LEDGER_CHECK_TICKER_NOT_FOUND; - break; - } - if (strcmp(l_tx_ticker, l_delegated_item->delegated_from)) { - debug_if(s_debug_more, L_WARNING, "Ticker '%s' != expected '%s'", l_tx_ticker, l_tx_in_ems->header.ticker); - l_err_num = DAP_LEDGER_TX_CHECK_STAKE_LOCK_OTHER_TICKER_EXPECTED; - break; - } - } - debug_if(s_debug_more, L_NOTICE, "Check emission passed for IN_EMS [%s]", l_tx_in_ems->header.ticker); - if (l_stake_lock_emission) { - l_bound_item->stake_lock_item = l_stake_lock_emission; - l_value = l_stake_lock_ems_value; - } else // girdled emission - l_value = l_tx_out_ext->header.value; - l_bound_item->token_item = l_delegated_item; - l_bound_item->type = TX_ITEM_TYPE_IN_EMS_LOCK; - } else if ( (l_emission_item = s_emission_item_find(a_ledger, l_token, l_emission_hash, &l_bound_item->token_item)) ) { - // 3. Check AUTH token emission - if (!dap_hash_fast_is_blank(&l_emission_item->tx_used_out) && !a_check_for_removing) { - debug_if(s_debug_more, L_WARNING, "Emission for IN_EMS [%s] is already used", l_tx_in_ems->header.ticker); - l_err_num = DAP_LEDGER_TX_CHECK_IN_EMS_ALREADY_USED; - break; - } - l_value = l_emission_item->datum_token_emission->hdr.value; - l_bound_item->emission_item = l_emission_item; - } else { - l_err_num = DAP_CHAIN_CS_VERIFY_CODE_TX_NO_EMISSION; - break; - } - } break; - - case TX_ITEM_TYPE_IN_REWARD: { - dap_chain_tx_in_reward_t *l_tx_in_reward = it->data; - dap_hash_fast_t *l_block_hash = &l_tx_in_reward->block_hash; - // 2. Check current transaction for doubles in input items list - for (dap_list_t *l_iter = l_list_in; l_iter; l_iter = l_iter->next) { - dap_chain_tx_in_reward_t *l_in_reward_check = l_iter->data; - if (l_tx_in_reward != l_in_reward_check && - l_in_reward_check->type == TX_ITEM_TYPE_IN_REWARD && - dap_hash_fast_compare(&l_in_reward_check->block_hash, l_block_hash) && !a_check_for_removing) { - debug_if(s_debug_more, L_ERROR, "Reward for this block sign already used in current tx"); - l_err_num = DAP_LEDGER_TX_CHECK_PREV_OUT_ALREADY_USED_IN_CURRENT_TX; - break; - } - } - if (l_err_num) - break; - if (!l_tx_first_sign_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, NULL, - TX_ITEM_TYPE_SIG, NULL); - assert(l_tx_sig); - // Get sign from sign item - dap_sign_t *l_tx_first_sign = dap_chain_datum_tx_item_sign_get_sig(l_tx_sig); - assert(l_tx_first_sign); - // calculate hash from sign public key - dap_sign_get_pkey_hash(l_tx_first_sign, &l_tx_first_sign_pkey_hash); - l_tx_first_sign_pkey = dap_pkey_get_from_sign(l_tx_first_sign); - } - // 3. Check if already spent reward - dap_ledger_reward_key_t l_search_key = { .block_hash = *l_block_hash, .sign_pkey_hash = l_tx_first_sign_pkey_hash }; - dap_ledger_reward_item_t *l_reward_item = s_find_reward(a_ledger, &l_search_key); - if (l_reward_item && !a_check_for_removing) { - l_err_num = DAP_LEDGER_TX_CHECK_REWARD_ITEM_ALREADY_USED; - char l_block_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE], - l_sign_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE], - l_spender_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE]; - dap_chain_hash_fast_to_str(l_block_hash, l_block_hash_str, sizeof(l_block_hash_str)); - dap_chain_hash_fast_to_str(&l_tx_first_sign_pkey_hash, l_sign_hash_str, sizeof(l_sign_hash_str)); - dap_chain_hash_fast_to_str(&l_reward_item->spender_tx, l_spender_hash_str, sizeof(l_spender_hash_str)); - debug_if(s_debug_more, L_WARNING, "Reward for block %s sign %s already spent by %s", l_block_hash_str, l_sign_hash_str, l_spender_hash_str); - break; - } - // Check reward legitimacy & amount - dap_chain_t *l_chain; - DL_FOREACH(a_ledger->net->pub.chains, l_chain) { - if (l_chain->callback_calc_reward) { - l_value = l_chain->callback_calc_reward(l_chain, l_block_hash, l_tx_first_sign_pkey); - break; - } - } - if (IS_ZERO_256(l_value)) { - l_err_num = DAP_LEDGER_TX_CHECK_REWARD_ITEM_ILLEGAL; - char l_block_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE], - l_sign_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE]; - dap_chain_hash_fast_to_str(l_block_hash, l_block_hash_str, sizeof(l_block_hash_str)); - dap_chain_hash_fast_to_str(&l_tx_first_sign_pkey_hash, l_sign_hash_str, sizeof(l_sign_hash_str)); - debug_if(s_debug_more, L_DEBUG, "Can't find block %s with sign %s", l_block_hash_str, l_sign_hash_str); - break; - } - // Reward nominated in net native ticker only - l_token = l_main_ticker = a_ledger->net->pub.native_ticker; - dap_ledger_token_item_t *l_token_item = s_ledger_find_token(a_ledger, l_token); - if (!l_token_item) { - debug_if(s_debug_more, L_ERROR, "Native token ticker not found"); - l_err_num = DAP_LEDGER_CHECK_TICKER_NOT_FOUND; - break; - } - if (!s_ledger_token_supply_check(l_token_item, l_value) && !a_check_for_removing) { - l_err_num = DAP_LEDGER_EMISSION_CHECK_VALUE_EXCEEDS_CURRENT_SUPPLY; - break; - } - l_bound_item->token_item = l_token_item; - l_bound_item->reward_key = l_search_key; - // Overflow checked later with overall values sum - SUM_256_256(l_taxed_value, l_value, &l_taxed_value); - } break; - - case TX_ITEM_TYPE_IN: - case TX_ITEM_TYPE_IN_COND: { // Not emission types - uint32_t l_tx_prev_out_idx = (uint32_t)-1; - dap_hash_fast_t *l_tx_prev_hash; - if (l_cond_type == TX_ITEM_TYPE_IN) { - dap_chain_tx_in_t *l_tx_in = it->data; - l_tx_prev_hash = &l_tx_in->header.tx_prev_hash; - if (dap_hash_fast_is_blank(l_tx_prev_hash)) { - DAP_DELETE(l_bound_item); - l_list_bound_items = dap_list_delete_link(l_list_bound_items, dap_list_last(l_list_bound_items)); - continue; // old base tx compliance - } - l_tx_prev_out_idx = l_tx_in->header.tx_out_prev_idx; - // 2. Check current transaction for doubles in input items list - for (dap_list_t *l_iter = l_list_in; l_iter; l_iter = l_iter->next) { - dap_chain_tx_in_t *l_in_check = l_iter->data; - if (l_tx_in != l_in_check && - l_in_check->header.type == TX_ITEM_TYPE_IN && - l_in_check->header.tx_out_prev_idx == l_tx_prev_out_idx && - dap_hash_fast_compare(&l_in_check->header.tx_prev_hash, l_tx_prev_hash) && !a_check_for_removing) { - debug_if(s_debug_more, L_ERROR, "This previous tx output already used in current tx"); - l_err_num = DAP_LEDGER_TX_CHECK_PREV_OUT_ALREADY_USED_IN_CURRENT_TX; - break; - } - } - if (l_err_num) - break; - } else { - dap_chain_tx_in_cond_t *l_tx_in_cond = it->data; - l_tx_prev_hash = &l_tx_in_cond->header.tx_prev_hash; - l_tx_prev_out_idx = l_tx_in_cond->header.tx_out_prev_idx; - // 2. Check current transaction for doubles in input items list - for (dap_list_t *l_iter = l_list_in; l_iter; l_iter = l_iter->next) { - dap_chain_tx_in_cond_t *l_in_cond_check = l_iter->data; - if (l_tx_in_cond != l_in_cond_check && - l_in_cond_check->header.type == TX_ITEM_TYPE_IN_COND && - l_in_cond_check->header.tx_out_prev_idx == l_tx_prev_out_idx && - dap_hash_fast_compare(&l_in_cond_check->header.tx_prev_hash, l_tx_prev_hash) && !a_check_for_removing) { - debug_if(s_debug_more, L_ERROR, "This previous tx output already used in current tx"); - l_err_num = DAP_LEDGER_TX_CHECK_PREV_OUT_ALREADY_USED_IN_CURRENT_TX; - break; - } - } - if (l_err_num) - break; - } - // Get previous transaction in the cache by hash - dap_ledger_tx_item_t *l_item_out = NULL; - l_tx_prev = dap_ledger_tx_find_datum_by_hash(a_ledger, l_tx_prev_hash, &l_item_out, false); - char l_tx_prev_hash_str[DAP_HASH_FAST_STR_SIZE]; - dap_hash_fast_to_str(l_tx_prev_hash, l_tx_prev_hash_str, DAP_HASH_FAST_STR_SIZE); - if (!l_tx_prev) { // Unchained transaction or previous TX was already spent and removed from ledger - debug_if(s_debug_more && !a_from_threshold, L_DEBUG, "No previous transaction was found for hash %s", l_tx_prev_hash_str); - l_err_num = DAP_CHAIN_CS_VERIFY_CODE_TX_NO_PREVIOUS; - break; - } else if (l_item_out->cache_data.ts_spent && !a_check_for_removing) { - l_err_num = DAP_LEDGER_TX_CHECK_OUT_ITEM_ALREADY_USED; - debug_if(s_debug_more, L_WARNING, "All 'out' items of previous tx %s were already spent", l_tx_prev_hash_str); - break; - } - l_bound_item->prev_item = l_item_out; - l_bound_item->prev_out_idx = l_tx_prev_out_idx; - l_token = l_item_out->cache_data.token_ticker; - debug_if(s_debug_more && !a_from_threshold, L_INFO, "Previous transaction was found for hash %s",l_tx_prev_hash_str); - - // 2. Check if out in previous transaction has spent - dap_hash_fast_t l_spender = {}; - if (s_ledger_tx_hash_is_used_out_item(l_item_out, l_tx_prev_out_idx, &l_spender) && !a_check_for_removing) { - l_err_num = DAP_LEDGER_TX_CHECK_OUT_ITEM_ALREADY_USED; - char l_hash[DAP_CHAIN_HASH_FAST_STR_SIZE]; - dap_chain_hash_fast_to_str(&l_spender, l_hash, sizeof(l_hash)); - debug_if(s_debug_more, L_INFO, "'Out' item of previous tx %s already spent by %s", l_tx_prev_hash_str, l_hash); - break; - } - - // Get one 'out' item in previous transaction bound with current 'in' item - l_tx_prev_out = dap_chain_datum_tx_item_get_nth(l_tx_prev, TX_ITEM_TYPE_OUT_ALL, l_tx_prev_out_idx); - if(!l_tx_prev_out) { - l_err_num = DAP_LEDGER_TX_CHECK_PREV_OUT_ITEM_NOT_FOUND; - break; - } - if (dap_hash_fast_is_blank(&l_tx_first_sign_pkey_hash)) { - // 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, NULL, - TX_ITEM_TYPE_SIG, NULL); - assert(l_tx_sig); - // Get sign from sign item - dap_sign_t *l_tx_first_sign = dap_chain_datum_tx_item_sign_get_sig(l_tx_sig); - assert(l_tx_first_sign); - // calculate hash from sign public key - dap_sign_get_pkey_hash(l_tx_first_sign, &l_tx_first_sign_pkey_hash); - } - if (l_cond_type == TX_ITEM_TYPE_IN) { - dap_chain_addr_t *l_addr_from = NULL; - dap_chain_tx_item_type_t l_type = *(uint8_t *)l_tx_prev_out; - switch (l_type) { - case TX_ITEM_TYPE_OUT_OLD: // Deprecated - l_addr_from = &((dap_chain_tx_out_old_t *)l_tx_prev_out)->addr; - l_value = dap_chain_uint256_from(((dap_chain_tx_out_old_t *)l_tx_prev_out)->header.value); - break; - case TX_ITEM_TYPE_OUT: - l_addr_from = &((dap_chain_tx_out_t *)l_tx_prev_out)->addr; - l_value = ((dap_chain_tx_out_t *)l_tx_prev_out)->header.value; - break; - case TX_ITEM_TYPE_OUT_EXT: - l_addr_from = &((dap_chain_tx_out_ext_t *)l_tx_prev_out)->addr; - l_value = ((dap_chain_tx_out_ext_t *)l_tx_prev_out)->header.value; - l_token = ((dap_chain_tx_out_ext_t *)l_tx_prev_out)->token; - break; - default: - l_err_num = DAP_LEDGER_TX_CHECK_PREV_OUT_ITEM_MISSTYPED; - break; - } - if (l_err_num) - break; - l_bound_item->in.addr_from = *l_addr_from; - dap_strncpy(l_bound_item->in.token_ticker, l_token, DAP_CHAIN_TICKER_SIZE_MAX); - // 4. compare public key hashes in the signature of the current transaction and in the 'out' item of the previous transaction - if (l_addr_from->net_id.uint64 != a_ledger->net->pub.id.uint64 || - !dap_hash_fast_compare(&l_tx_first_sign_pkey_hash, &l_addr_from->data.hash_fast)) { - l_err_num = DAP_LEDGER_TX_CHECK_PKEY_HASHES_DONT_MATCH; - break; - } - - if ( !l_token || !*l_token ) { - log_it(L_WARNING, "No token ticker found in previous transaction"); - l_err_num = DAP_LEDGER_TX_CHECK_NO_MAIN_TICKER; - break; - } - // Get permissions - dap_ledger_token_item_t *l_token_item = s_ledger_find_token(a_ledger, l_token); - if (!l_token_item) { - debug_if(s_debug_more, L_WARNING, "Token with ticker %s not found", l_token); - l_err_num = DAP_LEDGER_CHECK_TICKER_NOT_FOUND; - break; - } - // Check permissions - if (s_ledger_addr_check(l_token_item, l_addr_from, false) == DAP_LEDGER_CHECK_ADDR_FORBIDDEN) { - debug_if(s_debug_more, L_WARNING, "No permission to send for addr %s", dap_chain_addr_to_str_static(l_addr_from)); - l_err_num = DAP_LEDGER_CHECK_ADDR_FORBIDDEN; - break; - } - } else { // l_cond_type == TX_ITEM_TYPE_IN_COND - if(*(uint8_t *)l_tx_prev_out != TX_ITEM_TYPE_OUT_COND) { - l_err_num = DAP_LEDGER_TX_CHECK_PREV_OUT_ITEM_MISSTYPED; - break; - } - dap_chain_tx_out_cond_t *l_tx_prev_out_cond = NULL; - l_tx_prev_out_cond = (dap_chain_tx_out_cond_t *)l_tx_prev_out; - - // 5a. Check for condition owner - // Get owner tx - dap_hash_fast_t l_owner_tx_hash = dap_ledger_get_first_chain_tx_hash(a_ledger, l_tx_prev, l_tx_prev_out_cond); - dap_chain_datum_tx_t *l_owner_tx = dap_hash_fast_is_blank(&l_owner_tx_hash) - ? l_tx_prev - : dap_ledger_tx_find_by_hash(a_ledger, &l_owner_tx_hash); - dap_chain_tx_sig_t *l_tx_sig = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get(a_tx, NULL, NULL, TX_ITEM_TYPE_SIG, NULL); - dap_sign_t *l_sign = dap_chain_datum_tx_item_sign_get_sig((dap_chain_tx_sig_t *)l_tx_sig); - dap_chain_tx_sig_t *l_owner_tx_sig = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get(l_owner_tx, NULL, NULL, TX_ITEM_TYPE_SIG, NULL); - dap_sign_t *l_owner_sign = dap_chain_datum_tx_item_sign_get_sig((dap_chain_tx_sig_t *)l_owner_tx_sig); - - bool l_owner = false; - l_owner = dap_sign_compare_pkeys(l_owner_sign, l_sign); - - // 5b. Call verificator for conditional output - dap_ledger_verificator_t *l_verificator = NULL; - int l_sub_tmp = l_tx_prev_out_cond->header.subtype; - - pthread_rwlock_rdlock(&s_verificators_rwlock); - HASH_FIND_INT(s_verificators, &l_sub_tmp, l_verificator); - pthread_rwlock_unlock(&s_verificators_rwlock); - if (!l_verificator || !l_verificator->callback) { - debug_if(s_debug_more, L_ERROR, "No verificator set for conditional output subtype %d", l_sub_tmp); - l_err_num = DAP_LEDGER_TX_CHECK_NO_VERIFICATOR_SET; - break; - } - - int l_verificator_error = l_verificator->callback(a_ledger, l_tx_prev_out_cond, a_tx, l_owner); - if (l_verificator_error != DAP_LEDGER_CHECK_OK) { // TODO add string representation for verificator return codes - debug_if(s_debug_more, L_WARNING, "Verificator check error %d for conditional output %s", - l_verificator_error, dap_chain_tx_out_cond_subtype_to_str(l_sub_tmp)); - l_err_num = DAP_LEDGER_TX_CHECK_VERIFICATOR_CHECK_FAILURE; - break; - } - l_bound_item->cond = l_tx_prev_out_cond; - l_value = l_tx_prev_out_cond->header.value; - if (l_tx_prev_out_cond->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE) { - l_token = a_ledger->net->pub.native_ticker; - // Overflow checked later with overall values sum - SUM_256_256(l_taxed_value, l_value, &l_taxed_value); - } - l_main_ticker = l_token; - } - } break; - - default: - break; - } - if (l_err_num) - break; - - l_bound_item->value = l_value; +static bool s_pack_ledger_threshold_info_json (json_object *a_json_arr_out, dap_ledger_tx_item_t *a_tx_item) +{ + json_object *json_obj_tx = json_object_new_object(); + if (!json_obj_tx) + return 1; + char l_tx_prev_hash_str[DAP_HASH_FAST_STR_SIZE]={0}; + char l_time[DAP_TIME_STR_SIZE] = {0}; + dap_chain_hash_fast_to_str(&a_tx_item->tx_hash_fast,l_tx_prev_hash_str,sizeof(l_tx_prev_hash_str)); + dap_time_to_str_rfc822(l_time, sizeof(l_time), a_tx_item->cache_data.ts_created); + json_object_object_add(json_obj_tx, "Ledger thresholded tx_hash_fast", json_object_new_string(l_tx_prev_hash_str)); + json_object_object_add(json_obj_tx, "time_created", json_object_new_string(l_time)); + json_object_object_add(json_obj_tx, "tx_item_size", json_object_new_int(a_tx_item->tx->header.tx_items_size)); + json_object_array_add(a_json_arr_out, json_obj_tx); + return 0; +} +static bool s_pack_ledger_balance_info_json (json_object *a_json_arr_out, dap_ledger_wallet_balance_t *a_balance_item) +{ + json_object* json_obj_tx = json_object_new_object(); + + json_object_object_add(json_obj_tx, "Ledger balance key", json_object_new_string(a_balance_item->key)); + json_object_object_add(json_obj_tx, "token_ticker", json_object_new_string(a_balance_item->token_ticker)); + json_object_object_add(json_obj_tx, "balance", json_object_new_string(dap_uint256_to_char(a_balance_item->balance, NULL))); + json_object_array_add(a_json_arr_out, json_obj_tx); + return 0; +} - if (l_cond_type != TX_ITEM_TYPE_IN) { - // If not checked earlier - if (!l_token || !*l_token) { - log_it(L_WARNING, "No token ticker found in previous transaction"); - l_err_num = DAP_LEDGER_TX_CHECK_NO_MAIN_TICKER; - break; - } - } - HASH_FIND_STR(l_values_from_prev_tx, l_token, l_value_cur); - if (!l_value_cur) { - l_value_cur = DAP_NEW_Z(dap_ledger_tokenizer_t); - if ( !l_value_cur ) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - l_err_num = DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; - break; - } - strcpy(l_value_cur->token_ticker, l_token); - HASH_ADD_STR(l_values_from_prev_tx, token_ticker, l_value_cur); - } - // calculate from previous transactions per each token - if (SUM_256_256(l_value_cur->sum, l_value, &l_value_cur->sum)) { - debug_if(s_debug_more, L_WARNING, "Sum result overflow for tx_add_check with ticker %s", - l_value_cur->token_ticker); - l_err_num = DAP_LEDGER_CHECK_INTEGER_OVERFLOW; - break; - } - } +json_object *dap_ledger_threshold_info(dap_ledger_t *a_ledger, size_t a_limit, size_t a_offset, dap_chain_hash_fast_t *a_threshold_hash, bool a_head) +{ + dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); + dap_ledger_tx_item_t *l_tx_item = NULL, *l_tx_tmp; + json_object *json_arr_out = json_object_new_array(); + if (!json_arr_out) + return NULL; + uint32_t l_counter = 0; + size_t l_arr_start = 0; + size_t l_arr_end = 0; + dap_chain_set_offset_limit_json(json_arr_out, &l_arr_start, &l_arr_end, a_limit, a_offset, HASH_COUNT(l_ledger_pvt->threshold_txs)); - dap_list_free(l_list_in); - DAP_DELETE(l_tx_first_sign_pkey); - if (l_err_num) { - if ( l_list_bound_items ) - dap_list_free_full(l_list_bound_items, NULL); - HASH_ITER(hh, l_values_from_prev_tx, l_value_cur, l_tmp) { - HASH_DEL(l_values_from_prev_tx, l_value_cur); - DAP_DELETE(l_value_cur); + pthread_rwlock_rdlock(&l_ledger_pvt->threshold_txs_rwlock); + if (a_threshold_hash) { + json_object *json_obj_tx = json_object_new_object(); + if (!json_obj_tx) { + pthread_rwlock_unlock(&l_ledger_pvt->threshold_txs_rwlock); + json_object_put(json_arr_out); + return NULL; } - return l_err_num; - } - - // 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 - bool l_multichannel = false; - if (HASH_COUNT(l_values_from_prev_tx) > 1) { - l_multichannel = true; - if (HASH_COUNT(l_values_from_prev_tx) == 2 && !l_main_ticker) { - HASH_FIND_STR(l_values_from_prev_tx, a_ledger->net->pub.native_ticker, l_value_cur); - if (l_value_cur) { - l_value_cur = l_value_cur->hh.next ? l_value_cur->hh.next : l_value_cur->hh.prev; - l_main_ticker = l_value_cur->token_ticker; - } + HASH_FIND(hh, l_ledger_pvt->threshold_txs, a_threshold_hash, sizeof(dap_hash_t), l_tx_item); + if (l_tx_item) { + json_object_object_add(json_obj_tx, "Hash was found in ledger tx threshold", json_object_new_string(dap_hash_fast_to_str_static(a_threshold_hash))); + json_object_array_add(json_arr_out, json_obj_tx); + } else { + json_object_object_add(json_obj_tx, "Hash wasn't found in ledger", json_object_new_string("empty")); + json_object_array_add(json_arr_out, json_obj_tx); } } else { - l_value_cur = DAP_NEW_Z(dap_ledger_tokenizer_t); - if ( !l_value_cur ) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - l_err_num = DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; - if ( l_list_bound_items ) - dap_list_free_full(l_list_bound_items, NULL); - HASH_ITER(hh, l_values_from_prev_tx, l_value_cur, l_tmp) { - HASH_DEL(l_values_from_prev_tx, l_value_cur); - DAP_DELETE(l_value_cur); - } - return l_err_num; - } - dap_stpcpy(l_value_cur->token_ticker, l_token); - if (!l_main_ticker) - l_main_ticker = l_value_cur->token_ticker; - HASH_ADD_STR(l_values_from_cur_tx, token_ticker, l_value_cur); - } - - dap_chain_net_srv_stake_item_t *l_key_item = dap_chain_net_srv_stake_check_pkey_hash(a_ledger->net->pub.id, &l_tx_first_sign_pkey_hash); - bool l_tax_check = l_key_item && !dap_chain_addr_is_blank(&l_key_item->sovereign_addr) && !IS_ZERO_256(l_key_item->sovereign_tax); - - // find 'out' items - uint256_t l_value = {}, l_fee_sum = {}, l_tax_sum = {}; - bool l_fee_check = !IS_ZERO_256(a_ledger->net->pub.fee_value) && !dap_chain_addr_is_blank(&a_ledger->net->pub.fee_addr); - int l_item_idx = 0; - byte_t *it; size_t l_size; - TX_ITEM_ITER_TX(it, l_size, a_tx) { - dap_chain_addr_t l_tx_out_to = { }; - switch ( *it ) { - case TX_ITEM_TYPE_OUT_OLD: { - dap_chain_tx_out_old_t *l_tx_out = (dap_chain_tx_out_old_t*)it; - if (l_multichannel) { // token ticker is mandatory for multichannel transactions - l_err_num = DAP_LEDGER_TX_CHECK_NO_MAIN_TICKER; - break; - } - l_value = dap_chain_uint256_from(l_tx_out->header.value); - l_tx_out_to = l_tx_out->addr; - l_list_tx_out = dap_list_append(l_list_tx_out, l_tx_out); - } break; - case TX_ITEM_TYPE_OUT: { // 256 - dap_chain_tx_out_t *l_tx_out = (dap_chain_tx_out_t *)it; - if (l_multichannel) { // token ticker is mandatory for multichannel transactions - if (l_main_ticker) - l_token = l_main_ticker; - else { - l_err_num = DAP_LEDGER_TX_CHECK_NO_MAIN_TICKER; - break; - } - } - l_value = l_tx_out->header.value; - l_tx_out_to = l_tx_out->addr; - l_list_tx_out = dap_list_append(l_list_tx_out, l_tx_out); - } break; - case TX_ITEM_TYPE_OUT_EXT: { // 256 - dap_chain_tx_out_ext_t *l_tx_out = (dap_chain_tx_out_ext_t *)it; - if (!l_multichannel) { // token ticker is forbiden for single-channel transactions - l_err_num = DAP_LEDGER_TX_CHECK_UNEXPECTED_TOKENIZED_OUT; - break; - } - l_value = l_tx_out->header.value; - l_token = l_tx_out->token; - l_tx_out_to = l_tx_out->addr; - l_list_tx_out = dap_list_append(l_list_tx_out, l_tx_out); - } break; - case TX_ITEM_TYPE_OUT_COND: { - dap_chain_tx_out_cond_t *l_tx_out = (dap_chain_tx_out_cond_t *)it; - if (l_multichannel) { - if (l_tx_out->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE) - l_token = (char *)a_ledger->net->pub.native_ticker; - else if (l_main_ticker) - l_token = l_main_ticker; - else { - log_it(L_WARNING, "No conditional output support for multichannel transaction"); - l_err_num = DAP_LEDGER_TX_CHECK_NO_MAIN_TICKER; - break; - } - } - l_value = l_tx_out->header.value; - l_list_tx_out = dap_list_append(l_list_tx_out, l_tx_out); - if (l_tax_check && l_tx_out->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE && - SUBTRACT_256_256(l_taxed_value, l_value, &l_taxed_value)) { - log_it(L_WARNING, "Fee is greater than sum of inputs"); - l_err_num = DAP_LEDGER_CHECK_INTEGER_OVERFLOW; - break; + size_t i_tmp = 0; + if (a_head) + HASH_ITER(hh, l_ledger_pvt->threshold_txs, l_tx_item, l_tx_tmp) { + if (i_tmp < l_arr_start || i_tmp >= l_arr_end) + { + i_tmp++; + continue; } - } break; - default: - continue; + i_tmp++; + if (s_pack_ledger_threshold_info_json(json_arr_out, l_tx_item)) { + pthread_rwlock_unlock(&l_ledger_pvt->threshold_txs_rwlock); + json_object_put(json_arr_out); + return NULL; + } + l_counter++; } - - if (l_err_num) - break; - 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_ledger_tokenizer_t); - if ( !l_value_cur ) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - l_err_num = DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; - break; + else + { + l_tx_item = HASH_LAST(l_ledger_pvt->threshold_txs); + for(; l_tx_item; l_tx_item = l_tx_item->hh.prev, i_tmp++){ + if (i_tmp < l_arr_start || i_tmp >= l_arr_end) + continue; + if (s_pack_ledger_threshold_info_json(json_arr_out, l_tx_item)) { + pthread_rwlock_unlock(&l_ledger_pvt->threshold_txs_rwlock); + json_object_put(json_arr_out); + return NULL; } - strcpy(l_value_cur->token_ticker, l_token); - HASH_ADD_STR(l_values_from_cur_tx, token_ticker, l_value_cur); + l_counter++; } } - if (SUM_256_256(l_value_cur->sum, l_value, &l_value_cur->sum)) { - debug_if(s_debug_more, L_WARNING, "Sum result overflow for tx_add_check with ticker %s", - l_value_cur->token_ticker); - l_err_num = DAP_LEDGER_CHECK_INTEGER_OVERFLOW; - break; + if (!l_counter) { + json_object* json_obj_tx = json_object_new_object(); + json_object_object_add(json_obj_tx, "status", json_object_new_string("0 items in ledger tx threshold")); + json_object_array_add(json_arr_out, json_obj_tx); } + pthread_rwlock_unlock(&l_ledger_pvt->threshold_txs_rwlock); + } - // Find token item - dap_ledger_token_item_t *l_token_item = s_ledger_find_token(a_ledger, l_token); - if (!l_token_item) { - debug_if(s_debug_more, L_WARNING, "Token with ticker %s not found", l_token); - l_err_num = DAP_LEDGER_CHECK_TICKER_NOT_FOUND; - break; - } - // Check permissions - if (s_ledger_addr_check(l_token_item, &l_tx_out_to, true) == DAP_LEDGER_CHECK_ADDR_FORBIDDEN) { - debug_if(s_debug_more, L_WARNING, "No permission to receive for addr %s", dap_chain_addr_to_str_static(&l_tx_out_to)); - l_err_num = DAP_LEDGER_CHECK_ADDR_FORBIDDEN; - break; - } - if (l_fee_check && dap_chain_addr_compare(&l_tx_out_to, &a_ledger->net->pub.fee_addr) && - !dap_strcmp(l_value_cur->token_ticker, a_ledger->net->pub.native_ticker)) - SUM_256_256(l_fee_sum, l_value, &l_fee_sum); + return json_arr_out; +} - if (l_tax_check && dap_chain_addr_compare(&l_tx_out_to, &l_key_item->sovereign_addr) && - !dap_strcmp(l_value_cur->token_ticker, a_ledger->net->pub.native_ticker)) - SUM_256_256(l_tax_sum, l_value, &l_tax_sum); - } +json_object *dap_ledger_balance_info(dap_ledger_t *a_ledger, size_t a_limit, size_t a_offset, bool a_head) +{ + dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); + json_object * json_arr_out = json_object_new_array(); + pthread_rwlock_rdlock(&l_ledger_pvt->balance_accounts_rwlock); + uint32_t l_counter = 0; + dap_ledger_wallet_balance_t *l_balance_item, *l_balance_tmp; + size_t l_arr_start = 0; + size_t l_arr_end = 0; + dap_chain_set_offset_limit_json(json_arr_out, &l_arr_start, &l_arr_end, a_limit, a_offset, HASH_COUNT(l_ledger_pvt->balance_accounts)); - // Check for transaction consistency (sum(ins) == sum(outs)) - if (!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 || !EQUAL_256(l_res->sum, l_value_cur->sum) ) { - if (s_debug_more) { - char *l_balance = dap_chain_balance_coins_print(l_res ? l_res->sum : uint256_0); - char *l_balance_cur = dap_chain_balance_coins_print(l_value_cur->sum); - log_it(L_ERROR, "Sum of values of out items from current tx (%s) is not equal outs from previous txs (%s) for token %s", - l_balance, l_balance_cur, l_value_cur->token_ticker); - DAP_DELETE(l_balance); - DAP_DELETE(l_balance_cur); - } - l_err_num = DAP_LEDGER_TX_CHECK_SUM_INS_NOT_EQUAL_SUM_OUTS; - break; + size_t i_tmp = 0; + if (a_head) + HASH_ITER(hh, l_ledger_pvt->balance_accounts, l_balance_item, l_balance_tmp) { + if (i_tmp < l_arr_start || i_tmp >= l_arr_end) { + i_tmp++; + continue; } + i_tmp++; + s_pack_ledger_balance_info_json(json_arr_out, l_balance_item); + l_counter +=1; } + else { + l_balance_item = HASH_LAST(l_ledger_pvt->balance_accounts); + for(; l_balance_item; l_balance_item = l_balance_item->hh.prev, i_tmp++){ + if (i_tmp < l_arr_start || i_tmp >= l_arr_end) + continue; + s_pack_ledger_balance_info_json(json_arr_out, l_balance_item); + l_counter++; + } } + if (!l_counter){ + json_object* json_obj_tx = json_object_new_object(); + json_object_object_add(json_obj_tx, "No items in ledger balance_accounts", json_object_new_string("empty")); + json_object_array_add(json_arr_out, json_obj_tx); + } + pthread_rwlock_unlock(&l_ledger_pvt->balance_accounts_rwlock); + return json_arr_out; +} - // 7. Check the network fee - if (!l_err_num && l_fee_check) { - // Check for PoA-cert-signed "service" no-tax tx - if (compare256(l_fee_sum, a_ledger->net->pub.fee_value) == -1 && - !dap_ledger_tx_poa_signed(a_ledger, a_tx)) { - char *l_current_fee = dap_chain_balance_coins_print(l_fee_sum); - char *l_expected_fee = dap_chain_balance_coins_print(a_ledger->net->pub.fee_value); - log_it(L_WARNING, "Fee value is invalid, expected %s pointed %s", l_expected_fee, l_current_fee); - l_err_num = DAP_LEDGER_TX_CHECK_NOT_ENOUGH_FEE; - DAP_DEL_Z(l_current_fee); - DAP_DEL_Z(l_expected_fee); - } - if (l_tax_check && SUBTRACT_256_256(l_taxed_value, l_fee_sum, &l_taxed_value)) { - log_it(L_WARNING, "Fee is greater than sum of inputs"); - l_err_num = DAP_LEDGER_CHECK_INTEGER_OVERFLOW; - } - } - - // 8. Check sovereign tax - if (l_tax_check && !l_err_num) { - uint256_t l_expected_tax = {}; - MULT_256_COIN(l_taxed_value, l_key_item->sovereign_tax, &l_expected_tax); - if (compare256(l_tax_sum, l_expected_tax) == -1) { - char *l_current_tax_str = dap_chain_balance_coins_print(l_tax_sum); - char *l_expected_tax_str = dap_chain_balance_coins_print(l_expected_tax); - log_it(L_WARNING, "Tax value is invalid, expected %s pointed %s", l_expected_tax_str, l_current_tax_str); - l_err_num = DAP_LEDGER_TX_CHECK_NOT_ENOUGH_TAX; - DAP_DEL_Z(l_current_tax_str); - DAP_DEL_Z(l_expected_tax_str); +int dap_ledger_pvt_threshold_txs_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash) +{ + dap_ledger_tx_item_t *l_item_tmp = NULL; + dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); + unsigned l_hash_value = 0; + HASH_VALUE(a_tx_hash, sizeof(*a_tx_hash), l_hash_value); + pthread_rwlock_rdlock(&l_ledger_pvt->threshold_txs_rwlock); + HASH_FIND_BYHASHVALUE(hh, l_ledger_pvt->threshold_txs, a_tx_hash, sizeof(*a_tx_hash), l_hash_value, l_item_tmp); + unsigned long long l_threshold_txs_count = HASH_COUNT(l_ledger_pvt->threshold_txs); + if (!l_item_tmp) { + if (l_threshold_txs_count >= s_threshold_txs_max) { + debug_if(g_debug_ledger, L_WARNING, "Threshold for transactions is overfulled (%zu max), dropping down tx %s, added nothing", + s_threshold_txs_max, dap_hash_fast_to_str_static(a_tx_hash)); + return -2; + } + l_item_tmp = DAP_NEW_Z(dap_ledger_tx_item_t); + if ( !l_item_tmp ) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return -1; + } + l_item_tmp->tx_hash_fast = *a_tx_hash; + l_item_tmp->tx = l_ledger_pvt->mapped ? a_tx : DAP_DUP_SIZE(a_tx, dap_chain_datum_tx_get_size(a_tx)); + if ( !l_item_tmp->tx ) { + DAP_DELETE(l_item_tmp); + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return -1; } + l_item_tmp->ts_added = dap_nanotime_now(); + l_item_tmp->cache_data.ts_created = a_tx->header.ts_created; + HASH_ADD_BYHASHVALUE(hh, l_ledger_pvt->threshold_txs, tx_hash_fast, sizeof(dap_chain_hash_fast_t), l_hash_value, l_item_tmp); + debug_if(g_debug_ledger, L_DEBUG, "Tx %s added to threshold", dap_hash_fast_to_str_static(a_tx_hash)); } + pthread_rwlock_unlock(&l_ledger_pvt->threshold_txs_rwlock); + return 0; +} - if (!l_err_num) { - // TODO move it to service tag deduction - if ( dap_chain_datum_tx_item_get(a_tx, NULL, NULL, TX_ITEM_TYPE_VOTING, NULL ) ) { - if (s_voting_callbacks.voting_callback) { - if ((l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTING, a_tx, a_tx_hash, false))) { - debug_if(s_debug_more, L_WARNING, "Verificator check error %d for voting", l_err_num); - l_err_num = DAP_LEDGER_TX_CHECK_VERIFICATOR_CHECK_FAILURE; - } - } else { - debug_if(s_debug_more, L_WARNING, "Verificator check error for voting item"); - l_err_num = DAP_LEDGER_TX_CHECK_NO_VERIFICATOR_SET; +void dap_ledger_pvt_threshold_txs_proc(dap_ledger_t *a_ledger) +{ + bool l_success; + dap_ledger_private_t * l_ledger_pvt = PVT(a_ledger); + pthread_rwlock_wrlock(&l_ledger_pvt->threshold_txs_rwlock); + do { + l_success = false; + dap_ledger_tx_item_t *l_tx_item, *l_tx_tmp; + HASH_ITER(hh, l_ledger_pvt->threshold_txs, l_tx_item, l_tx_tmp) { + int l_res = dap_ledger_tx_add(a_ledger, l_tx_item->tx, &l_tx_item->tx_hash_fast, true, NULL); + if (l_res != DAP_CHAIN_CS_VERIFY_CODE_TX_NO_EMISSION && + l_res != DAP_CHAIN_CS_VERIFY_CODE_TX_NO_PREVIOUS) { + HASH_DEL(l_ledger_pvt->threshold_txs, l_tx_item); + if ( !l_ledger_pvt->mapped ) + DAP_DELETE(l_tx_item->tx); + DAP_DELETE(l_tx_item); + l_success = true; } - if (a_tag) - a_tag->uint64 = DAP_CHAIN_TX_TAG_ACTION_VOTING; - } else if ( dap_chain_datum_tx_item_get(a_tx, NULL, NULL, TX_ITEM_TYPE_VOTE, NULL) ) { - if (s_voting_callbacks.voting_callback) { - if ((l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTE, a_tx, a_tx_hash, false))) { - debug_if(s_debug_more, L_WARNING, "Verificator check error %d for vote", l_err_num); - l_err_num = DAP_LEDGER_TX_CHECK_VERIFICATOR_CHECK_FAILURE; - } - } else { - debug_if(s_debug_more, L_WARNING, "Verificator check error for vote item"); - l_err_num = DAP_LEDGER_TX_CHECK_NO_VERIFICATOR_SET; - } - if (a_tag) - a_tag->uint64 = DAP_CHAIN_TX_TAG_ACTION_VOTE; } - } - - if (a_main_ticker && !l_err_num) - dap_strncpy(a_main_ticker, l_main_ticker, DAP_CHAIN_TICKER_SIZE_MAX); - - HASH_ITER(hh, l_values_from_prev_tx, l_value_cur, l_tmp) { - HASH_DEL(l_values_from_prev_tx, l_value_cur); - DAP_DELETE(l_value_cur); - } - HASH_ITER(hh, l_values_from_cur_tx, l_value_cur, l_tmp) { - HASH_DEL(l_values_from_cur_tx, l_value_cur); - DAP_DELETE(l_value_cur); - } - if (!a_list_bound_items || l_err_num) { - dap_list_free_full(l_list_bound_items, NULL); - } else { - *a_list_bound_items = l_list_bound_items; - } - - if (!a_list_tx_out || l_err_num) { - dap_list_free(l_list_tx_out); - } else { - *a_list_tx_out = l_list_tx_out; - } - - return l_err_num; + } while (l_success); + pthread_rwlock_unlock(&l_ledger_pvt->threshold_txs_rwlock); } /** - * @brief dap_ledger_tx_check + * @breif s_treshold_txs_free * @param a_ledger - * @param a_tx - * @return */ -int dap_ledger_tx_add_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, size_t a_datum_size, dap_hash_fast_t *a_datum_hash) +static void s_threshold_txs_free(dap_ledger_t *a_ledger) { - dap_return_val_if_fail(a_tx && a_ledger, DAP_LEDGER_CHECK_INVALID_ARGS); - - size_t l_tx_size = dap_chain_datum_tx_get_size(a_tx); - if (l_tx_size != a_datum_size) { - log_it (L_WARNING, "Inconsistent datum TX: datum size %zu != tx size %zu", a_datum_size, l_tx_size); - return DAP_LEDGER_CHECK_INVALID_SIZE; - } - int l_ret_check = s_tx_cache_check(a_ledger, a_tx, a_datum_hash, false, NULL, NULL, NULL, NULL, NULL, false); - if(s_debug_more) { - if (l_ret_check) - log_it(L_NOTICE, "Ledger TX adding check not passed for TX %s: error %s", - dap_chain_hash_fast_to_str_static(a_datum_hash), dap_ledger_check_error_str(l_ret_check)); - else - log_it(L_INFO, "Ledger TX adding check passed for TX %s", dap_chain_hash_fast_to_str_static(a_datum_hash)); + log_it(L_DEBUG, "Start free threshold txs"); + dap_ledger_private_t *l_pvt = PVT(a_ledger); + dap_ledger_tx_item_t *l_current = NULL, *l_tmp = NULL; + dap_nanotime_t l_time_cut_off = dap_nanotime_now() - dap_nanotime_from_sec(7200); //7200 sec = 2 hours. + pthread_rwlock_wrlock(&l_pvt->threshold_txs_rwlock); + HASH_ITER(hh, l_pvt->threshold_txs, l_current, l_tmp) { + if (l_current->ts_added < l_time_cut_off) { + HASH_DEL(l_pvt->threshold_txs, l_current); + char l_tx_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE]; + dap_chain_hash_fast_to_str(&l_current->tx_hash_fast, l_tx_hash_str, sizeof(l_tx_hash_str)); + if ( !l_pvt->mapped ) + DAP_DELETE(l_current->tx); + DAP_DELETE(l_current); + log_it(L_NOTICE, "Removed transaction %s form threshold ledger", l_tx_hash_str); + } } - - return l_ret_check; + pthread_rwlock_unlock(&l_pvt->threshold_txs_rwlock); } /** - * @brief s_balance_cache_update - * @param a_ledger - * @param a_balance - * @return + * @brief s_load_cache_gdb_loaded_balances_callback + * @param a_global_db_context + * @param a_rc + * @param a_group + * @param a_key + * @param a_values_total + * @param a_values_shift + * @param a_values_count + * @param a_values + * @param a_arg */ -static int s_balance_cache_update(dap_ledger_t *a_ledger, dap_ledger_wallet_balance_t *a_balance) +static bool s_load_cache_gdb_loaded_balances_callback(dap_global_db_instance_t *a_dbi, + int a_rc, const char *a_group, + const size_t a_values_total, const size_t a_values_count, + dap_global_db_obj_t *a_values, void *a_arg) { - if (PVT(a_ledger)->cached) { - char *l_gdb_group = dap_ledger_get_gdb_group(a_ledger, DAP_LEDGER_BALANCES_STR); - if (dap_global_db_set(l_gdb_group, a_balance->key, &a_balance->balance, sizeof(uint256_t), false, NULL, NULL)) { - debug_if(s_debug_more, L_WARNING, "Ledger cache mismatch"); - return -1; + dap_ledger_t * l_ledger = (dap_ledger_t*) a_arg; + dap_ledger_private_t * l_ledger_pvt = PVT(l_ledger); + for (size_t i = 0; i < a_values_count; i++) { + dap_ledger_wallet_balance_t *l_balance_item = DAP_NEW_Z(dap_ledger_wallet_balance_t); + if (!l_balance_item) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return false; } - DAP_DELETE(l_gdb_group); - } - /* Notify the world*/ - if ( !dap_chain_net_get_load_mode(a_ledger->net) ) { - struct json_object *l_json = wallet_info_json_collect(a_ledger, a_balance); - dap_notify_server_send_mt(json_object_get_string(l_json)); - json_object_put(l_json); + l_balance_item->key = DAP_NEW_Z_SIZE(char, strlen(a_values[i].key) + 1); + if (!l_balance_item->key) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + DAP_DEL_Z(l_balance_item); + return false; + } + strcpy(l_balance_item->key, a_values[i].key); + char *l_ptr = strchr(l_balance_item->key, ' '); + if (l_ptr++) { + strcpy(l_balance_item->token_ticker, l_ptr); + } + l_balance_item->balance = *(uint256_t *)a_values[i].value; + HASH_ADD_KEYPTR(hh, l_ledger_pvt->balance_accounts, l_balance_item->key, + strlen(l_balance_item->key), l_balance_item); } - return 0; -} - -static int s_sort_ledger_tx_item(dap_ledger_tx_item_t *a, dap_ledger_tx_item_t *b) -{ - if (a->cache_data.ts_created < b->cache_data.ts_created) - return -1; - if (a->cache_data.ts_created == b->cache_data.ts_created) - return 0; - return 1; + pthread_mutex_lock( &l_ledger_pvt->load_mutex ); + l_ledger_pvt->load_end = true; + pthread_cond_broadcast( &l_ledger_pvt->load_cond ); + pthread_mutex_unlock( &l_ledger_pvt->load_mutex ); + return true; } /** - * @brief Add new transaction to the cache list + * @brief Load ledger from cache (stored in GDB) * @param a_ledger - * @param a_tx - * @param a_tx_hash - * @param a_from_threshold - * @return return 1 OK, -1 error */ -int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash, bool a_from_threshold, dap_ledger_datum_iter_data_t *a_datum_index_data) +void dap_ledger_load_cache(dap_ledger_t *a_ledger) { - if(!a_tx) { - debug_if(s_debug_more, L_ERROR, "NULL tx detected"); - return -1; - } - int l_ret = 0; dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); - dap_list_t *l_list_bound_items = NULL; - dap_list_t *l_list_tx_out = NULL; - dap_ledger_tx_item_t *l_item_tmp = NULL; - char l_main_token_ticker[DAP_CHAIN_TICKER_SIZE_MAX] = { '\0' }; - - bool l_from_threshold = a_from_threshold; - char l_tx_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE]; - dap_chain_hash_fast_to_str(a_tx_hash, l_tx_hash_str, sizeof(l_tx_hash_str)); - - int l_ret_check; - dap_chain_srv_uid_t l_tag = { .uint64 = 0 }; - dap_chain_tx_tag_action_type_t l_action = DAP_CHAIN_TX_TAG_ACTION_UNKNOWN; - - if( (l_ret_check = s_tx_cache_check(a_ledger, a_tx, a_tx_hash, a_from_threshold, - &l_list_bound_items, &l_list_tx_out, - l_main_token_ticker, &l_tag, &l_action, false))) { - if ((l_ret_check == DAP_CHAIN_CS_VERIFY_CODE_TX_NO_PREVIOUS || - l_ret_check == DAP_CHAIN_CS_VERIFY_CODE_TX_NO_EMISSION) && - l_ledger_pvt->threshold_enabled && !dap_chain_net_get_load_mode(a_ledger->net)) { - if (!l_from_threshold) { - unsigned l_hash_value = 0; - HASH_VALUE(a_tx_hash, sizeof(*a_tx_hash), l_hash_value); - pthread_rwlock_rdlock(&l_ledger_pvt->threshold_txs_rwlock); - HASH_FIND_BYHASHVALUE(hh, l_ledger_pvt->threshold_txs, a_tx_hash, sizeof(*a_tx_hash), l_hash_value, l_item_tmp); - unsigned long long l_threshold_txs_count = HASH_COUNT(l_ledger_pvt->threshold_txs); - if (!l_item_tmp) { - if (l_threshold_txs_count >= s_threshold_txs_max) { - if(s_debug_more) - log_it(L_WARNING, "Threshold for transactions is overfulled (%zu max), dropping down tx %s, added nothing", - s_threshold_txs_max, l_tx_hash_str); - } else { - l_item_tmp = DAP_NEW_Z(dap_ledger_tx_item_t); - if ( !l_item_tmp ) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return -1; - } - l_item_tmp->tx_hash_fast = *a_tx_hash; - l_item_tmp->tx = l_ledger_pvt->mapped ? a_tx : DAP_DUP_SIZE(a_tx, dap_chain_datum_tx_get_size(a_tx)); - if ( !l_item_tmp->tx ) { - DAP_DELETE(l_item_tmp); - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return -1; - } - l_item_tmp->ts_added = dap_nanotime_now(); - l_item_tmp->cache_data.ts_created = a_tx->header.ts_created; - HASH_ADD_BYHASHVALUE(hh, l_ledger_pvt->threshold_txs, tx_hash_fast, sizeof(dap_chain_hash_fast_t), l_hash_value, l_item_tmp); - if(s_debug_more) - log_it (L_DEBUG, "Tx %s added to threshold", l_tx_hash_str); - } - } - pthread_rwlock_unlock(&l_ledger_pvt->threshold_txs_rwlock); - } - } else { - debug_if(s_debug_more, L_WARNING, "dap_ledger_tx_add() tx %s not passed the check: %s ", l_tx_hash_str, - dap_ledger_check_error_str(l_ret_check)); - } - - if ( l_list_bound_items ) - dap_list_free_full(l_list_bound_items, NULL); - - return l_ret_check; - } - debug_if(s_debug_more, L_DEBUG, "dap_ledger_tx_add() check passed for tx %s", l_tx_hash_str); - if ( a_datum_index_data != NULL){ - dap_strncpy(a_datum_index_data->token_ticker, l_main_token_ticker, DAP_CHAIN_TICKER_SIZE_MAX); - a_datum_index_data->action = l_action; - a_datum_index_data->uid = l_tag; - } - // Mark 'out' items in cache if they were used & delete previous transactions from cache if it need - // find all bound pairs 'in' and 'out' - size_t l_outs_used = dap_list_length(l_list_bound_items); - - dap_store_obj_t *l_cache_used_outs = NULL; - char *l_ledger_cache_group = NULL; - if (PVT(a_ledger)->cached) { - l_cache_used_outs = DAP_NEW_Z_SIZE(dap_store_obj_t, sizeof(dap_store_obj_t) * (l_outs_used + 1)); - if ( !l_cache_used_outs ) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - l_ret = -1; - goto FIN; - } - l_ledger_cache_group = dap_ledger_get_gdb_group(a_ledger, DAP_LEDGER_TXS_STR); - } - const char *l_cur_token_ticker = NULL; - - // Update balance: deducts - int l_spent_idx = 0; - for (dap_list_t *it = l_list_bound_items; it; it = it->next) { - dap_ledger_tx_bound_t *l_bound_item = it->data; - dap_chain_tx_item_type_t l_type = l_bound_item->type; - if (l_type == TX_ITEM_TYPE_IN || l_type == TX_ITEM_TYPE_IN_COND) { - if (l_bound_item->prev_item->cache_data.n_outs <= l_bound_item->prev_item->cache_data.n_outs_used) { - log_it(L_ERROR, "[!] Irrelevant prev tx: out items mismatch %d <= %d", - l_bound_item->prev_item->cache_data.n_outs, l_bound_item->prev_item->cache_data.n_outs_used); - l_outs_used--; - continue; - } - l_spent_idx++; - } - - if ((l_type == TX_ITEM_TYPE_IN_EMS_LOCK || l_type == TX_ITEM_TYPE_IN_REWARD) && - !s_ledger_token_supply_check_update(a_ledger, l_bound_item->token_item, l_bound_item->value, false)) - log_it(L_ERROR, "Insufficient supply for token %s", l_bound_item->token_item->ticker); - - switch (l_type) { - case TX_ITEM_TYPE_IN_EMS: - // Mark it as used with current tx hash - l_bound_item->emission_item->tx_used_out = *a_tx_hash; - s_ledger_emission_cache_update(a_ledger, l_bound_item->emission_item); - l_outs_used--; // Do not calc this output with tx used items - continue; - - case TX_ITEM_TYPE_IN_EMS_LOCK: - if (l_bound_item->stake_lock_item) { // Legacy stake lock emission - // Mark it as used with current tx hash - l_bound_item->stake_lock_item->tx_used_out = *a_tx_hash; - s_ledger_stake_lock_cache_update(a_ledger, l_bound_item->stake_lock_item); - } - l_outs_used--; // Do not calc this output with tx used items - continue; - - case TX_ITEM_TYPE_IN_REWARD: { - dap_ledger_reward_item_t *l_item = DAP_NEW_Z(dap_ledger_reward_item_t); - if (!l_item) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - l_ret = -1; - goto FIN; - } - l_item->key = l_bound_item->reward_key; - l_item->spender_tx = *a_tx_hash; - pthread_rwlock_wrlock(&l_ledger_pvt->rewards_rwlock); - HASH_ADD(hh, l_ledger_pvt->rewards, key, sizeof(l_item->key), l_item); - pthread_rwlock_unlock(&l_ledger_pvt->rewards_rwlock); - } - l_outs_used--; // Do not calc this output with tx used items - continue; + char *l_gdb_group = dap_ledger_get_gdb_group(a_ledger, DAP_LEDGER_TOKENS_STR); - case TX_ITEM_TYPE_IN: { - dap_ledger_wallet_balance_t *wallet_balance = NULL; - l_cur_token_ticker = l_bound_item->in.token_ticker; - const char *l_addr_str = dap_chain_addr_to_str_static(&l_bound_item->in.addr_from); - char *l_wallet_balance_key = dap_strjoin(" ", l_addr_str, l_cur_token_ticker, (char*)NULL); - pthread_rwlock_rdlock(&PVT(a_ledger)->balance_accounts_rwlock); - HASH_FIND_STR(PVT(a_ledger)->balance_accounts, l_wallet_balance_key, wallet_balance); - pthread_rwlock_unlock(&PVT(a_ledger)->balance_accounts_rwlock); - if (wallet_balance) { - debug_if(s_debug_more, L_DEBUG, "SPEND %s from addr: %s", - dap_uint256_to_char(l_bound_item->value, NULL), l_wallet_balance_key); - SUBTRACT_256_256(wallet_balance->balance, l_bound_item->value, &wallet_balance->balance); - // Update the cache - s_balance_cache_update(a_ledger, wallet_balance); - } else { - if(s_debug_more) - log_it(L_ERROR,"!!! Attempt to SPEND from some non-existent balance !!!: %s %s", l_addr_str, l_cur_token_ticker); - } - - DAP_DELETE(l_wallet_balance_key); - } break; + pthread_mutex_lock(& l_ledger_pvt->load_mutex); + dap_global_db_get_all(l_gdb_group, 0, dap_ledger_pvt_cache_gdb_load_tokens_callback, a_ledger); + while (!l_ledger_pvt->load_end) + pthread_cond_wait(& l_ledger_pvt->load_cond, &l_ledger_pvt->load_mutex); + pthread_mutex_unlock(& l_ledger_pvt->load_mutex); - case TX_ITEM_TYPE_IN_COND: { // all balance deducts performed with previous conditional transaction - // Update service items if any - dap_ledger_verificator_t *l_verificator = NULL; - int l_tmp = l_bound_item->cond->header.subtype; - pthread_rwlock_rdlock(&s_verificators_rwlock); - HASH_FIND_INT(s_verificators, &l_tmp, l_verificator); - pthread_rwlock_unlock(&s_verificators_rwlock); - if (l_verificator && l_verificator->callback_added) - l_verificator->callback_added(a_ledger, a_tx, a_tx_hash, l_bound_item->cond); - } break; + DAP_DELETE(l_gdb_group); +} - default: - log_it(L_ERROR, "Unknown item type %d in ledger TX bound for IN part", l_type); - break; - } - // add a used output - dap_ledger_tx_item_t *l_prev_item_out = l_bound_item->prev_item; - l_prev_item_out->cache_data.tx_hash_spent_fast[l_bound_item->prev_out_idx] = *a_tx_hash; - l_prev_item_out->cache_data.n_outs_used++; - if (PVT(a_ledger)->cached) { - // mirror it in the cache - size_t l_tx_size = dap_chain_datum_tx_get_size(l_prev_item_out->tx); - size_t l_tx_cache_sz = l_tx_size + sizeof(l_prev_item_out->cache_data); - byte_t *l_tx_cache = DAP_NEW_Z_SIZE(byte_t, l_tx_cache_sz); - memcpy(l_tx_cache, &l_prev_item_out->cache_data, sizeof(l_prev_item_out->cache_data)); - memcpy(l_tx_cache + sizeof(l_prev_item_out->cache_data), l_prev_item_out->tx, l_tx_size); - char *l_tx_i_hash = dap_chain_hash_fast_to_str_new(&l_prev_item_out->tx_hash_fast); - l_cache_used_outs[l_spent_idx] = (dap_store_obj_t) { - .key = l_tx_i_hash, - .value = l_tx_cache, - .value_len = l_tx_cache_sz, - .group = l_ledger_cache_group, - }; - l_cache_used_outs[l_spent_idx].timestamp = dap_nanotime_now(); - } - // mark previous transactions as used with the extra timestamp - if (l_prev_item_out->cache_data.n_outs_used == l_prev_item_out->cache_data.n_outs) - l_prev_item_out->cache_data.ts_spent = a_tx->header.ts_created; +/** + * @brief + * create ledger for specific net + * load ledger cache + * @param a_check_flags checking flags + * DAP_LEDGER_CHECK_TOKEN_EMISSION + * DAP_LEDGER_CHECK_CELLS_DS + * DAP_LEDGER_CHECK_CELLS_DS + * @param a_net_name char * network name, for example "kelvin-testnet" + * @return dap_ledger_t* + */ +dap_ledger_t *dap_ledger_create(dap_chain_net_t *a_net, uint16_t a_flags) +{ + dap_ledger_t *l_ledger = dap_ledger_handle_new(); + if (!l_ledger) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return NULL; } + l_ledger->net = a_net; + dap_ledger_private_t *l_ledger_pvt = PVT(l_ledger); + l_ledger_pvt->check_ds = a_flags & DAP_LEDGER_CHECK_LOCAL_DS; + l_ledger_pvt->check_cells_ds = a_flags & DAP_LEDGER_CHECK_CELLS_DS; + l_ledger_pvt->check_token_emission = a_flags & DAP_LEDGER_CHECK_TOKEN_EMISSION; + l_ledger_pvt->cached = a_flags & DAP_LEDGER_CACHE_ENABLED; + l_ledger_pvt->mapped = a_flags & DAP_LEDGER_MAPPED; + l_ledger_pvt->threshold_enabled = a_flags & DAP_LEDGER_THRESHOLD_ENABLED; + if (l_ledger_pvt->threshold_enabled) + l_ledger_pvt->threshold_txs_free_timer = dap_interval_timer_create(s_threshold_free_timer_tick, + (dap_timer_callback_t)s_threshold_txs_free, l_ledger); + pthread_cond_init(&l_ledger_pvt->load_cond, NULL); + pthread_mutex_init(&l_ledger_pvt->load_mutex, NULL); +#ifndef DAP_LEDGER_TEST + char * l_chains_path = dap_strdup_printf("%s/network/%s", dap_config_path(), a_net->pub.name); + DIR * l_chains_dir = opendir(l_chains_path); + DAP_DEL_Z(l_chains_path); - //Update balance : raise - bool l_multichannel = false; - bool l_cross_network = false; - uint32_t l_outs_count = 0; - for (dap_list_t *l_tx_out = l_list_tx_out; l_tx_out; l_tx_out = l_tx_out->next, l_outs_count++) { - if (!l_tx_out->data) { - log_it(L_ERROR, "Can't detect tx ticker or matching output, can't append balances cache"); - continue; - } - dap_chain_tx_item_type_t l_type = *(uint8_t *)l_tx_out->data; - if (l_type == TX_ITEM_TYPE_OUT_COND) { - // Update service items if any - dap_chain_tx_out_cond_t *l_cond = (dap_chain_tx_out_cond_t *)l_tx_out->data; - dap_ledger_verificator_t *l_verificator = NULL; - int l_tmp = l_cond->header.subtype; - pthread_rwlock_rdlock(&s_verificators_rwlock); - HASH_FIND_INT(s_verificators, &l_tmp, l_verificator); - pthread_rwlock_unlock(&s_verificators_rwlock); - if (l_verificator && l_verificator->callback_added) - l_verificator->callback_added(a_ledger, a_tx, a_tx_hash, NULL); - continue; // balance raise will be with next conditional transaction - } - - dap_chain_addr_t *l_addr = NULL; - uint256_t l_value = {}; - switch (l_type) { - case TX_ITEM_TYPE_OUT: { - dap_chain_tx_out_t *l_out_item_256 = (dap_chain_tx_out_t *)l_tx_out->data; - l_addr = &l_out_item_256->addr; - l_value = l_out_item_256->header.value; - l_cur_token_ticker = l_main_token_ticker; - } break; - case TX_ITEM_TYPE_OUT_OLD: { - dap_chain_tx_out_old_t *l_out_item = (dap_chain_tx_out_old_t *)l_tx_out->data; - l_addr = &l_out_item->addr; - l_value = GET_256_FROM_64(l_out_item->header.value); - l_cur_token_ticker = l_main_token_ticker; - } break; - case TX_ITEM_TYPE_OUT_EXT: { - dap_chain_tx_out_ext_t *l_out_item_ext_256 = (dap_chain_tx_out_ext_t *)l_tx_out->data; - l_addr = &l_out_item_ext_256->addr; - l_value = l_out_item_ext_256->header.value; - l_cur_token_ticker = l_out_item_ext_256->token; - l_multichannel = true; - } break; - default: - log_it(L_ERROR, "Unknown item type %d", l_type); - break; - } - if (!l_addr) + struct dirent * l_dir_entry; + while ( (l_dir_entry = readdir(l_chains_dir) )!= NULL ){ + if (l_dir_entry->d_name[0] == '\0') continue; - else if (l_addr->net_id.uint64 != a_ledger->net->pub.id.uint64 && - !dap_chain_addr_is_blank(l_addr)) - l_cross_network = true; - const char *l_addr_str = dap_chain_addr_to_str_static(l_addr); - dap_ledger_wallet_balance_t *wallet_balance = NULL; - char *l_wallet_balance_key = dap_strjoin(" ", l_addr_str, l_cur_token_ticker, (char*)NULL); - debug_if(s_debug_more, L_DEBUG, "GOT %s to addr: %s", - dap_uint256_to_char(l_value, NULL), l_wallet_balance_key); - pthread_rwlock_rdlock(&l_ledger_pvt->balance_accounts_rwlock); - HASH_FIND_STR(PVT(a_ledger)->balance_accounts, l_wallet_balance_key, wallet_balance); - pthread_rwlock_unlock(&l_ledger_pvt->balance_accounts_rwlock); - if (wallet_balance) { - //if(s_debug_more) - // log_it(L_DEBUG, "Balance item is present in cache"); - SUM_256_256(wallet_balance->balance, l_value, &wallet_balance->balance); - DAP_DELETE (l_wallet_balance_key); - // Update the cache - s_balance_cache_update(a_ledger, wallet_balance); - } else { - wallet_balance = DAP_NEW_Z(dap_ledger_wallet_balance_t); - if (!wallet_balance) { - log_it(L_CRITICAL, "Memory allocation error in s_load_cache_gdb_loaded_txs_callback"); - l_ret = -1; - goto FIN; - } - wallet_balance->key = l_wallet_balance_key; - strcpy(wallet_balance->token_ticker, l_cur_token_ticker); - SUM_256_256(wallet_balance->balance, l_value, &wallet_balance->balance); - if(s_debug_more) - log_it(L_DEBUG, "Create new balance item: %s %s", l_addr_str, l_cur_token_ticker); - pthread_rwlock_wrlock(&l_ledger_pvt->balance_accounts_rwlock); - HASH_ADD_KEYPTR(hh, PVT(a_ledger)->balance_accounts, wallet_balance->key, - strlen(l_wallet_balance_key), wallet_balance); - pthread_rwlock_unlock(&l_ledger_pvt->balance_accounts_rwlock); - // Add it to cache - s_balance_cache_update(a_ledger, wallet_balance); - } - } - int l_err_num = 0; - if (s_voting_callbacks.voting_callback) { - if (l_tag.uint64 == DAP_CHAIN_TX_TAG_ACTION_VOTING) - l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTING, a_tx, a_tx_hash, true); - else if (l_tag.uint64 == DAP_CHAIN_TX_TAG_ACTION_VOTE) - l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTE, a_tx, a_tx_hash, true); - } - assert(!l_err_num); - - // add transaction to the cache list - dap_ledger_tx_item_t *l_tx_item = DAP_NEW_Z(dap_ledger_tx_item_t); - if ( !l_tx_item ) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - l_ret = -1; - goto FIN; - } - l_tx_item->tx_hash_fast = *a_tx_hash; - size_t l_tx_size = dap_chain_datum_tx_get_size(a_tx); - l_tx_item->tx = l_ledger_pvt->mapped ? a_tx : DAP_DUP_SIZE(a_tx, l_tx_size); - l_tx_item->cache_data.n_outs = l_outs_count; - l_tx_item->cache_data.tag = l_tag; - l_tx_item->cache_data.action = l_action; - dap_stpcpy(l_tx_item->cache_data.token_ticker, l_main_token_ticker); - - l_tx_item->cache_data.multichannel = l_multichannel; - l_tx_item->ts_added = dap_nanotime_now(); - pthread_rwlock_wrlock(&l_ledger_pvt->ledger_rwlock); - if (dap_chain_net_get_load_mode(a_ledger->net) || dap_chain_net_get_state(a_ledger->net) == NET_STATE_SYNC_CHAINS) - HASH_ADD(hh, l_ledger_pvt->ledger_items, tx_hash_fast, sizeof(dap_chain_hash_fast_t), l_tx_item); - else - HASH_ADD_INORDER(hh, l_ledger_pvt->ledger_items, tx_hash_fast, sizeof(dap_chain_hash_fast_t), - l_tx_item, s_sort_ledger_tx_item); // tx_hash_fast: name of key field - pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); - // Callable callback - dap_list_t *l_notifier; - DL_FOREACH(PVT(a_ledger)->tx_add_notifiers, l_notifier) { - dap_ledger_tx_notifier_t *l_notify = (dap_ledger_tx_notifier_t*)l_notifier->data; - l_notify->callback(l_notify->arg, a_ledger, l_tx_item->tx, DAP_LEDGER_NOTIFY_OPCODE_ADDED); - } - if (l_cross_network) { - dap_list_t *l_notifier; - DL_FOREACH(PVT(a_ledger)->bridged_tx_notifiers, l_notifier) { - dap_ledger_bridged_tx_notifier_t *l_notify = l_notifier->data; - l_notify->callback(a_ledger, a_tx, a_tx_hash, l_notify->arg, DAP_LEDGER_NOTIFY_OPCODE_ADDED); - } - } - if (PVT(a_ledger)->cached) { - // Add it to cache - size_t l_tx_cache_sz = l_tx_size + sizeof(l_tx_item->cache_data); - uint8_t *l_tx_cache = DAP_NEW_STACK_SIZE(uint8_t, l_tx_cache_sz); - memcpy(l_tx_cache, &l_tx_item->cache_data, sizeof(l_tx_item->cache_data)); - memcpy(l_tx_cache + sizeof(l_tx_item->cache_data), a_tx, l_tx_size); - l_cache_used_outs[0] = (dap_store_obj_t) { - .key = l_tx_hash_str, - .value = l_tx_cache, - .value_len = l_tx_cache_sz, - .group = l_ledger_cache_group, - }; - l_cache_used_outs[0].timestamp = dap_nanotime_now(); - // Apply it with single DB transaction - if (dap_global_db_set_raw(l_cache_used_outs, l_outs_used + 1, NULL, NULL)) - debug_if(s_debug_more, L_WARNING, "Ledger cache mismatch"); - } - if (!a_from_threshold && l_ledger_pvt->threshold_enabled) - s_threshold_txs_proc(a_ledger); -FIN: - if (l_list_bound_items) - dap_list_free_full(l_list_bound_items, NULL); - if (l_list_tx_out) - dap_list_free(l_list_tx_out); - if (PVT(a_ledger)->cached) { - if (l_cache_used_outs) { - for (size_t i = 1; i <= l_outs_used; i++) { - DAP_DEL_Z(l_cache_used_outs[i].key); - DAP_DEL_Z(l_cache_used_outs[i].value); + char * l_entry_name = dap_strdup(l_dir_entry->d_name); + if (strlen(l_entry_name) > 4) { + if ( strncmp (l_entry_name + strlen(l_entry_name)-4,".cfg",4) == 0 ) { // its .cfg file + l_entry_name [strlen(l_entry_name)-4] = 0; + log_it(L_DEBUG,"Open chain config \"%s.%s\"...", a_net->pub.name, l_entry_name); + l_chains_path = dap_strdup_printf("network/%s/%s", a_net->pub.name, l_entry_name); + dap_config_t * l_cfg = dap_config_open(l_chains_path); + uint16_t l_whitelist_size, l_blacklist_size, i; + const char **l_whitelist = dap_config_get_array_str(l_cfg, "ledger", "hard_accept_list", &l_whitelist_size), + **l_blacklist = dap_config_get_array_str(l_cfg, "ledger", "hard_reject_list", &l_blacklist_size); + for (i = 0; i < l_blacklist_size; ++i) { + dap_ledger_hal_item_t *l_item = DAP_NEW_Z(dap_ledger_hal_item_t); + dap_chain_hash_fast_from_str(l_blacklist[i], &l_item->hash); + HASH_ADD(hh, l_ledger_pvt->hrl_items, hash, sizeof(dap_hash_fast_t), l_item); + } + for (i = 0; i < l_whitelist_size; ++i) { + dap_ledger_hal_item_t *l_item = DAP_NEW_Z(dap_ledger_hal_item_t); + dap_chain_hash_fast_from_str(l_whitelist[i], &l_item->hash); + HASH_ADD(hh, l_ledger_pvt->hal_items, hash, sizeof(dap_hash_fast_t), l_item); + } + dap_config_close(l_cfg); + log_it(L_DEBUG, "Chain %s.%s has %d datums in HAL and %d datums in HRL", a_net->pub.name, l_entry_name, l_whitelist_size, l_blacklist_size); } } - DAP_DEL_Z(l_cache_used_outs); - DAP_DEL_Z(l_ledger_cache_group); + DAP_DELETE (l_entry_name); } - return l_ret; + closedir(l_chains_dir); + + if ( l_ledger_pvt->cached ) + // load ledger cache from GDB + dap_ledger_load_cache(l_ledger); +#endif + // Decrees initializing + dap_ledger_decree_create(a_net); + + return l_ledger; } -void dap_ledger_load_end(dap_ledger_t *a_ledger) +static int s_callback_sign_compare(dap_list_t *a_list_elem, dap_list_t *a_sign_elem) { - pthread_rwlock_wrlock(&PVT(a_ledger)->ledger_rwlock); - HASH_SORT(PVT(a_ledger)->ledger_items, s_sort_ledger_tx_item); - pthread_rwlock_unlock(&PVT(a_ledger)->ledger_rwlock); + dap_pkey_t *l_key = (dap_pkey_t *)a_list_elem->data; + dap_sign_t *l_sign = (dap_sign_t *)a_sign_elem->data; + if (!l_key || !l_sign) { + log_it(L_CRITICAL, "Invalid argument"); + return -1; + } + return !dap_pkey_compare_with_sign(l_key, l_sign); } -/** - * @brief Remove transaction from the cache list - * @param a_ledger - * @param a_tx - * @param a_tx_hash - * @param a_from_threshold - * @return return 1 OK, -1 error - */ -int dap_ledger_tx_remove(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash) +bool dap_ledger_tx_poa_signed(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx) { - int l_ret = 0; - dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); - dap_list_t *l_list_bound_items = NULL; - dap_list_t *l_list_tx_out = NULL; - dap_chain_srv_uid_t l_tag = { .uint64 = 0 }; - char l_main_token_ticker[DAP_CHAIN_TICKER_SIZE_MAX] = { '\0' }; - - char l_tx_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE]; - dap_chain_hash_fast_to_str(a_tx_hash, l_tx_hash_str, sizeof(l_tx_hash_str)); - - // Get boundary items list into l_list_bound_items - // Get tx outs list into l_list_tx_out - int l_ret_check; - if( (l_ret_check = s_tx_cache_check(a_ledger, a_tx, a_tx_hash, false, - &l_list_bound_items, &l_list_tx_out, - l_main_token_ticker, &l_tag, NULL, true))) { - debug_if(s_debug_more, L_WARNING, "dap_ledger_tx_remove() tx %s not passed the check: %s ", l_tx_hash_str, - dap_ledger_check_error_str(l_ret_check)); - return l_ret_check; - } + dap_chain_tx_sig_t *l_tx_sig = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get(a_tx, NULL, 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); + return dap_list_find(a_ledger->net->pub.keys, l_sign, s_callback_sign_compare); +} - dap_ledger_tx_item_t *l_ledger_item = NULL; - pthread_rwlock_rdlock(&PVT(a_ledger)->ledger_rwlock); - HASH_FIND(hh, PVT(a_ledger)->ledger_items, a_tx_hash, sizeof(dap_chain_hash_fast_t), l_ledger_item); - pthread_rwlock_unlock(&PVT(a_ledger)->ledger_rwlock); - if (l_ledger_item && l_ledger_item->cache_data.n_outs_used != 0) { // transaction already present in the cache list - return DAP_LEDGER_TX_CHECK_OUT_ITEM_ALREADY_USED; - } - - // find all bound pairs 'in' and 'out' - size_t l_outs_used = dap_list_length(l_list_bound_items); +/* +services we know now +0x01 - VPN +0x02 - xchange +0x03, 0x13 - pos_delegate +0x04 bridge +0x.05 - custom datum +0x06 voting +0x12 - stake_lock +*/ - dap_store_obj_t *l_cache_used_outs = NULL; - char *l_ledger_cache_group = NULL; - if (PVT(a_ledger)->cached) { - l_cache_used_outs = DAP_NEW_Z_SIZE(dap_store_obj_t, sizeof(dap_store_obj_t) * (l_outs_used)); - if ( !l_cache_used_outs ) { - log_it(L_CRITICAL, "Memory allocation error"); - l_ret = -1; - goto FIN; - } - l_ledger_cache_group = dap_ledger_get_gdb_group(a_ledger, DAP_LEDGER_TXS_STR); - } - const char *l_cur_token_ticker = NULL; +const char *dap_ledger_tx_action_str(dap_chain_tx_tag_action_type_t a_tag) +{ - // Update balance : raise all bound items to balances - int l_spent_idx = 0; - for (dap_list_t *it = l_list_bound_items; it; it = it->next) { - dap_ledger_tx_bound_t *l_bound_item = it->data; - dap_chain_tx_item_type_t l_type = l_bound_item->type; - if ((l_type == TX_ITEM_TYPE_IN_EMS_LOCK || l_type == TX_ITEM_TYPE_IN_REWARD) && - !s_ledger_token_supply_check_update(a_ledger, l_bound_item->token_item, l_bound_item->value, true)) - log_it(L_ERROR, "Insufficient supply for token %s", l_bound_item->token_item->ticker); + if (a_tag == DAP_CHAIN_TX_TAG_ACTION_UNKNOWN) return "unknown"; + if (a_tag == DAP_CHAIN_TX_TAG_ACTION_TRANSFER_REGULAR) return "regular"; + if (a_tag == DAP_CHAIN_TX_TAG_ACTION_TRANSFER_COMISSION) return "comission"; + if (a_tag == DAP_CHAIN_TX_TAG_ACTION_TRANSFER_CROSSCHAIN) return "crosschain"; + if (a_tag == DAP_CHAIN_TX_TAG_ACTION_TRANSFER_REWARD) return "reward"; + if (a_tag == DAP_CHAIN_TX_TAG_ACTION_OPEN) return "open"; + if (a_tag == DAP_CHAIN_TX_TAG_ACTION_USE) return "use"; + if (a_tag == DAP_CHAIN_TX_TAG_ACTION_EXTEND) return "extend"; + if (a_tag == DAP_CHAIN_TX_TAG_ACTION_CLOSE) return "close"; + if (a_tag == DAP_CHAIN_TX_TAG_ACTION_CHANGE) return "change"; - switch (l_type) { - case TX_ITEM_TYPE_IN_EMS: - // Mark it as unused - memset(&(l_bound_item->emission_item->tx_used_out), 0, sizeof(dap_hash_fast_t)); - s_ledger_emission_cache_update(a_ledger, l_bound_item->emission_item); - l_outs_used--; // Do not calc this output with tx used items - continue; + return "WTFSUBTAG"; - case TX_ITEM_TYPE_IN_EMS_LOCK: - if (l_bound_item->stake_lock_item) { // Legacy stake lock emission - // Mark it as used with current tx hash - memset(&(l_bound_item->stake_lock_item->tx_used_out), 0, sizeof(dap_hash_fast_t)); - s_ledger_stake_lock_cache_update(a_ledger, l_bound_item->stake_lock_item); - } - l_outs_used--; // Do not calc this output with tx used items - continue; +} - case TX_ITEM_TYPE_IN_REWARD: { - dap_ledger_reward_item_t *l_item = NULL; - pthread_rwlock_wrlock(&l_ledger_pvt->rewards_rwlock); - HASH_FIND(hh, l_ledger_pvt->rewards, &l_bound_item->reward_key, sizeof(l_bound_item->reward_key), l_item); - if(l_item){ - HASH_DEL(l_ledger_pvt->rewards, l_item); - DAP_DEL_Z(l_item); - } - pthread_rwlock_unlock(&l_ledger_pvt->rewards_rwlock); - } - l_outs_used--; // Do not calc this output with tx used items - continue; +dap_chain_tx_tag_action_type_t dap_ledger_tx_action_str_to_action_t(const char *a_str) +{ + if (!a_str) + return DAP_CHAIN_TX_TAG_ACTION_UNKNOWN; + + if (strcmp("unknown", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_UNKNOWN; + if (strcmp("regular", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_TRANSFER_REGULAR; + if (strcmp("comission", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_TRANSFER_COMISSION; + if (strcmp("crosschain", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_TRANSFER_CROSSCHAIN; + if (strcmp("reward", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_TRANSFER_REWARD; + if (strcmp("open", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_OPEN; + if (strcmp("use", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_USE; + if (strcmp("extend", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_EXTEND; + if (strcmp("close", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_CLOSE; + if (strcmp("change", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_CHANGE; - case TX_ITEM_TYPE_IN: { - dap_ledger_wallet_balance_t *wallet_balance = NULL; - l_cur_token_ticker = l_bound_item->in.token_ticker; - const char *l_addr_str = dap_chain_addr_to_str_static(&l_bound_item->in.addr_from); - char *l_wallet_balance_key = dap_strjoin(" ", l_addr_str, l_cur_token_ticker, (char*)NULL); - pthread_rwlock_rdlock(&PVT(a_ledger)->balance_accounts_rwlock); - HASH_FIND_STR(PVT(a_ledger)->balance_accounts, l_wallet_balance_key, wallet_balance); - pthread_rwlock_unlock(&PVT(a_ledger)->balance_accounts_rwlock); - if (wallet_balance) { - if(s_debug_more) { - char *l_balance = dap_chain_balance_datoshi_print(l_bound_item->value); - log_it(L_DEBUG,"REFUND %s from addr: %s because tx was removed.", l_balance, l_wallet_balance_key); - DAP_DELETE(l_balance); - } - SUM_256_256(wallet_balance->balance, l_bound_item->value, &wallet_balance->balance); - // Update the cache - s_balance_cache_update(a_ledger, wallet_balance); - } else { - if(s_debug_more) - log_it(L_ERROR,"!!! Attempt to SPEND from some non-existent balance !!!: %s %s", l_addr_str, l_cur_token_ticker); - } - DAP_DELETE(l_wallet_balance_key); - } break; + return DAP_CHAIN_TX_TAG_ACTION_UNKNOWN; +} - case TX_ITEM_TYPE_IN_COND: { // all balance deducts performed with previous conditional transaction - // Update service items if any - dap_ledger_verificator_t *l_verificator = NULL; - int l_tmp = l_bound_item->cond->header.subtype; - pthread_rwlock_rdlock(&s_verificators_rwlock); - HASH_FIND_INT(s_verificators, &l_tmp, l_verificator); - pthread_rwlock_unlock(&s_verificators_rwlock); - if (l_verificator && l_verificator->callback_deleted) - l_verificator->callback_deleted(a_ledger, a_tx, l_bound_item->cond); - } break; +bool dap_ledger_tx_service_info(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_hash, + dap_chain_srv_uid_t *a_uid, char **a_service_name, dap_chain_tx_tag_action_type_t *a_action) +{ + //find tx + dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); + dap_ledger_tx_item_t *l_tx_item = NULL; + pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); + HASH_FIND(hh, l_ledger_pvt->ledger_items, a_tx_hash, sizeof(dap_chain_hash_fast_t), l_tx_item); + pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); + + + if(l_tx_item) { + dap_ledger_service_info_t *l_sinfo = NULL; + pthread_rwlock_rdlock(&s_services_rwlock); + HASH_FIND_INT(s_services, &l_tx_item->cache_data.tag, l_sinfo); + pthread_rwlock_unlock(&s_services_rwlock); + if (l_sinfo) + { + if(a_uid) *a_uid = l_sinfo->service_uid; + if (a_service_name) *a_service_name = l_sinfo->tag_str; + if (a_action) *a_action = l_tx_item->cache_data.action; + return true; + } + } - default: - log_it(L_ERROR, "Unknown item type %d in ledger TX bound for IN part", l_type); - break; - } + if (a_action) *a_action = DAP_CHAIN_TX_TAG_ACTION_UNKNOWN; + return false; +} - // add a used output - dap_ledger_tx_item_t *l_prev_item_out = l_bound_item->prev_item; - memset(&(l_prev_item_out->cache_data.tx_hash_spent_fast[l_bound_item->prev_out_idx]), 0, sizeof(dap_hash_fast_t)); - l_prev_item_out->cache_data.n_outs_used--; - if (PVT(a_ledger)->cached) { - // mirror it in the cache - size_t l_tx_size = dap_chain_datum_tx_get_size(l_prev_item_out->tx); - size_t l_tx_cache_sz = l_tx_size + sizeof(l_prev_item_out->cache_data); - byte_t *l_tx_cache = DAP_NEW_Z_SIZE(byte_t, l_tx_cache_sz); - memcpy(l_tx_cache, &l_prev_item_out->cache_data, sizeof(l_prev_item_out->cache_data)); - memcpy(l_tx_cache + sizeof(l_prev_item_out->cache_data), l_prev_item_out->tx, l_tx_size); - char *l_tx_i_hash = dap_chain_hash_fast_to_str_new(&l_prev_item_out->tx_hash_fast); - l_cache_used_outs[l_spent_idx] = (dap_store_obj_t) { - .key = l_tx_i_hash, - .value = l_tx_cache, - .value_len = l_tx_cache_sz, - .group = l_ledger_cache_group - }; - l_cache_used_outs[l_spent_idx].timestamp = 0; - } - // mark previous transactions as used with the extra timestamp - if(l_prev_item_out->cache_data.n_outs_used != l_prev_item_out->cache_data.n_outs) - l_prev_item_out->cache_data.ts_spent = 0; +bool dap_ledger_deduct_tx_tag(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, char **a_service_name, dap_chain_srv_uid_t *a_tag, dap_chain_tx_tag_action_type_t *a_action) +{ + dap_ledger_service_info_t *l_sinfo_current, *l_sinfo_tmp; - if (l_type == TX_ITEM_TYPE_IN || l_type == TX_ITEM_TYPE_IN_COND) { - l_spent_idx++; - } - } + + dap_chain_datum_tx_item_groups_t l_items_groups = {0}; + dap_chain_datum_tx_group_items(a_tx, &l_items_groups); - // Update balance: deducts all outs from balances - bool l_cross_network = false; - for (dap_list_t *l_tx_out = l_list_tx_out; l_tx_out; l_tx_out = dap_list_next(l_tx_out)) { - if (!l_tx_out->data) { - debug_if(s_debug_more, L_WARNING, "Can't detect tx ticker or matching output, can't append balances cache"); - continue; - } - dap_chain_tx_item_type_t l_type = *(uint8_t *)l_tx_out->data; - if (l_type == TX_ITEM_TYPE_OUT_COND) { - // Update service items if any - dap_chain_tx_out_cond_t *l_cond = (dap_chain_tx_out_cond_t *)l_tx_out->data; - dap_ledger_verificator_t *l_verificator = NULL; - int l_tmp = l_cond->header.subtype; - pthread_rwlock_rdlock(&s_verificators_rwlock); - HASH_FIND_INT(s_verificators, &l_tmp, l_verificator); - pthread_rwlock_unlock(&s_verificators_rwlock); - if (l_verificator && l_verificator->callback_deleted) - l_verificator->callback_deleted(a_ledger, a_tx, NULL); - continue; // balance raise will be with next conditional transaction - } + bool l_res = false; + int l_deductions_ok = 0; - dap_chain_addr_t *l_addr = NULL; - uint256_t l_value = {}; - switch (l_type) { - case TX_ITEM_TYPE_OUT: { - dap_chain_tx_out_t *l_out_item_256 = (dap_chain_tx_out_t *)l_tx_out->data; - l_addr = &l_out_item_256->addr; - l_value = l_out_item_256->header.value; - l_cur_token_ticker = l_main_token_ticker; - } break; - case TX_ITEM_TYPE_OUT_OLD: { - dap_chain_tx_out_old_t *l_out_item = (dap_chain_tx_out_old_t *)l_tx_out->data; - l_addr = &l_out_item->addr; - l_value = GET_256_FROM_64(l_out_item->header.value); - l_cur_token_ticker = l_main_token_ticker; - } break; - case TX_ITEM_TYPE_OUT_EXT: { - dap_chain_tx_out_ext_t *l_out_item_ext_256 = (dap_chain_tx_out_ext_t *)l_tx_out->data; - l_addr = &l_out_item_ext_256->addr; - l_value = l_out_item_ext_256->header.value; - l_cur_token_ticker = l_out_item_ext_256->token; - } break; - default: - log_it(L_DEBUG, "Unknown item type %d", l_type); - break; - } - if (!l_addr) - continue; - else if (l_addr->net_id.uint64 != a_ledger->net->pub.id.uint64 && - !dap_chain_addr_is_blank(l_addr)) - l_cross_network = true; - const char *l_addr_str = dap_chain_addr_to_str_static(l_addr); - dap_ledger_wallet_balance_t *wallet_balance = NULL; - char *l_wallet_balance_key = dap_strjoin(" ", l_addr_str, l_cur_token_ticker, (char*)NULL); - if(s_debug_more) { - char *l_balance = dap_chain_balance_datoshi_print(l_value); - log_it(L_DEBUG, "UNDO %s from addr: %s", l_balance, l_wallet_balance_key); - DAP_DELETE(l_balance); - } - pthread_rwlock_rdlock(&l_ledger_pvt->balance_accounts_rwlock); - HASH_FIND_STR(PVT(a_ledger)->balance_accounts, l_wallet_balance_key, wallet_balance); - pthread_rwlock_unlock(&l_ledger_pvt->balance_accounts_rwlock); - if (wallet_balance) { - //if(s_debug_more) - // log_it(L_DEBUG, "Balance item is present in cache"); - SUBTRACT_256_256(wallet_balance->balance, l_value, &wallet_balance->balance); - DAP_DELETE (l_wallet_balance_key); - // Update the cache - s_balance_cache_update(a_ledger, wallet_balance); - } else { - log_it(L_CRITICAL, "Wallet is not presented in cache. Can't substract out value from balance."); + pthread_rwlock_rdlock(&s_services_rwlock); + HASH_ITER(hh, s_services , l_sinfo_current, l_sinfo_tmp) { + dap_chain_tx_tag_action_type_t action = DAP_CHAIN_TX_TAG_ACTION_UNKNOWN; + if (l_sinfo_current->callback && l_sinfo_current->callback(a_ledger, a_tx, &l_items_groups, &action)){ + if (a_tag) *a_tag = l_sinfo_current->service_uid; + if (a_action) *a_action = action; + if (a_service_name) *a_service_name = l_sinfo_current->tag_str; + l_res = true; + l_deductions_ok ++; } - } + } + pthread_rwlock_unlock(&s_services_rwlock); - if (s_voting_callbacks.voting_delete_callback) { - if (l_tag.uint64 == DAP_CHAIN_TX_TAG_ACTION_VOTING) - s_voting_callbacks.voting_delete_callback(a_ledger, TX_ITEM_TYPE_VOTING, a_tx); - else if (l_tag.uint64 == DAP_CHAIN_TX_TAG_ACTION_VOTE) - s_voting_callbacks.voting_delete_callback(a_ledger, TX_ITEM_TYPE_VOTE, a_tx); - } + if (l_deductions_ok > 1) + { + char l_tx_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE]; + dap_chain_hash_fast_t l_tx_hash = dap_chain_node_datum_tx_calc_hash(a_tx); + dap_chain_hash_fast_to_str(&l_tx_hash, l_tx_hash_str, sizeof(l_tx_hash_str)); - // remove transaction from ledger - dap_ledger_tx_item_t *l_tx_item = NULL; - pthread_rwlock_wrlock(&l_ledger_pvt->ledger_rwlock); - HASH_FIND(hh, l_ledger_pvt->ledger_items, a_tx_hash, sizeof(dap_chain_hash_fast_t), l_tx_item); - if (l_tx_item) - HASH_DEL(l_ledger_pvt->ledger_items, l_tx_item); - pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); + log_it(L_WARNING, "Transaction %s identyfied by multiple services (%d):", l_tx_hash_str, l_deductions_ok); - // Callable callback - dap_list_t *l_notifier; - DL_FOREACH(PVT(a_ledger)->tx_add_notifiers, l_notifier) { - dap_ledger_tx_notifier_t *l_notify = (dap_ledger_tx_notifier_t*)l_notifier->data; - l_notify->callback(l_notify->arg, a_ledger, l_tx_item->tx, DAP_LEDGER_NOTIFY_OPCODE_DELETED); - } - if (l_cross_network) { - dap_list_t *l_notifier; - DL_FOREACH(PVT(a_ledger)->bridged_tx_notifiers, l_notifier) { - dap_ledger_bridged_tx_notifier_t *l_notify = l_notifier->data; - l_notify->callback(a_ledger, a_tx, a_tx_hash, l_notify->arg, DAP_LEDGER_NOTIFY_OPCODE_DELETED); - } - } - - if (PVT(a_ledger)->cached) { - // Add it to cache - dap_global_db_del_sync(l_ledger_cache_group, l_tx_hash_str); - // Apply it with single DB transaction - if (dap_global_db_set_raw(l_cache_used_outs, l_outs_used, NULL, NULL)) - debug_if(s_debug_more, L_WARNING, "Ledger cache mismatch"); - } -FIN: - if (l_list_bound_items) - dap_list_free_full(l_list_bound_items, NULL); - if (l_list_tx_out) - dap_list_free(l_list_tx_out); - if (PVT(a_ledger)->cached) { - if (l_cache_used_outs) { - for (size_t i = 1; i < l_outs_used; i++) { - DAP_DEL_Z(l_cache_used_outs[i].key); - DAP_DEL_Z(l_cache_used_outs[i].value); + pthread_rwlock_rdlock(&s_services_rwlock); + HASH_ITER(hh, s_services , l_sinfo_current, l_sinfo_tmp) { + dap_chain_tx_tag_action_type_t action = DAP_CHAIN_TX_TAG_ACTION_UNKNOWN; + if (l_sinfo_current->callback && l_sinfo_current->callback(a_ledger, a_tx, &l_items_groups,&action)) { + log_it(L_WARNING, "%s %s", l_sinfo_current->tag_str, dap_ledger_tx_action_str(action)); } - } - DAP_DEL_Z(l_cache_used_outs); - DAP_DEL_Z(l_ledger_cache_group); - } - return l_ret; -} + } -int dap_ledger_tx_load(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_chain_hash_fast_t *a_tx_hash, dap_ledger_datum_iter_data_t *a_datum_index_data) -{ -#ifndef DAP_LEDGER_TEST - if (dap_chain_net_get_load_mode(a_ledger->net)) { - if (PVT(a_ledger)->cache_tx_check_callback) - PVT(a_ledger)->cache_tx_check_callback(a_ledger, a_tx_hash); - dap_ledger_tx_item_t *l_tx_item = NULL; - unsigned l_hash_value; - HASH_VALUE(a_tx_hash, sizeof(dap_chain_hash_fast_t), l_hash_value); - pthread_rwlock_rdlock(&PVT(a_ledger)->ledger_rwlock); - HASH_FIND_BYHASHVALUE(hh, PVT(a_ledger)->ledger_items, a_tx_hash, sizeof(dap_chain_hash_fast_t), l_hash_value, l_tx_item); - pthread_rwlock_unlock(&PVT(a_ledger)->ledger_rwlock); - if (l_tx_item) - return DAP_LEDGER_CHECK_ALREADY_CACHED; + pthread_rwlock_unlock(&s_services_rwlock); } -#endif - return dap_ledger_tx_add(a_ledger, a_tx, a_tx_hash, false, a_datum_index_data); + + dap_chain_datum_tx_group_items_free(&l_items_groups); + + return l_res; } /** @@ -5119,7 +1086,7 @@ void dap_ledger_purge(dap_ledger_t *a_ledger, bool a_preserve_db) l_ledger_pvt->load_end = false; - dap_chain_net_decree_purge(a_ledger->net); + dap_ledger_decree_purge(a_ledger->net); } /** @@ -5169,17 +1136,6 @@ uint64_t dap_ledger_count_from_to(dap_ledger_t * a_ledger, dap_nanotime_t a_ts_f return l_ret; } - -/** - * Check whether used 'out' items - */ -bool dap_ledger_tx_hash_is_used_out_item(dap_ledger_t *a_ledger, dap_chain_hash_fast_t *a_tx_hash, int a_idx_out, dap_hash_fast_t *a_out_spender) -{ - dap_ledger_tx_item_t *l_item_out = NULL; - /*dap_chain_datum_tx_t *l_tx =*/ dap_ledger_tx_find_datum_by_hash(a_ledger, a_tx_hash, &l_item_out, false); - return l_item_out ? s_ledger_tx_hash_is_used_out_item(l_item_out, a_idx_out, a_out_spender) : true; -} - /** * Calculate balance of addr * @@ -5196,71 +1152,16 @@ uint256_t dap_ledger_calc_balance(dap_ledger_t *a_ledger, const dap_chain_addr_t HASH_FIND_STR(PVT(a_ledger)->balance_accounts, l_wallet_balance_key, l_balance_item); pthread_rwlock_unlock(&PVT(a_ledger)->balance_accounts_rwlock); if (l_balance_item) { - debug_if(s_debug_more, L_INFO, "Found address in cache with balance %s", + debug_if(g_debug_ledger, L_INFO, "Found address in cache with balance %s", dap_uint256_to_char(l_balance_item->balance, NULL)); l_ret = l_balance_item->balance; } else { - debug_if(s_debug_more, L_WARNING, "Balance item %s not found", l_wallet_balance_key); + debug_if(g_debug_ledger, L_WARNING, "Balance item %s not found", l_wallet_balance_key); } DAP_DELETE(l_wallet_balance_key); return l_ret; } -uint256_t dap_ledger_calc_balance_full(dap_ledger_t *a_ledger, const dap_chain_addr_t *a_addr, - const char *a_token_ticker) -{ - uint256_t balance = uint256_0; - - if(!a_addr || dap_chain_addr_check_sum(a_addr)) - return balance; - - dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); - dap_ledger_tx_item_t *l_iter_current, *l_item_tmp; - pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); - HASH_ITER(hh, l_ledger_pvt->ledger_items , l_iter_current, l_item_tmp) - { - dap_chain_datum_tx_t *l_cur_tx = l_iter_current->tx; - // Get 'out' items from transaction - int l_out_idx = 0; - byte_t *it; size_t l_size; - TX_ITEM_ITER_TX(it, l_size, l_cur_tx) { - if ( l_out_idx > MAX_OUT_ITEMS ) - return log_it(L_ERROR, "Number of 'out' items exeeds max number %d", MAX_OUT_ITEMS), uint256_0; - uint256_t l_add = { }; - dap_chain_addr_t l_out_addr = { }; - switch (*it) { - case TX_ITEM_TYPE_OUT_OLD: { - dap_chain_tx_out_old_t *l_tx_out = (dap_chain_tx_out_old_t*)it; - l_add = dap_chain_uint256_from(l_tx_out->header.value); - l_out_addr = l_tx_out->addr; - } break; - case TX_ITEM_TYPE_OUT: { - dap_chain_tx_out_t *l_tx_out = (dap_chain_tx_out_t*)it; - l_add = l_tx_out->header.value; - l_out_addr = l_tx_out->addr; - } break; - case TX_ITEM_TYPE_OUT_EXT: { - dap_chain_tx_out_ext_t *l_tx_out = (dap_chain_tx_out_ext_t*)it; - l_add = l_tx_out->header.value; - l_out_addr = l_tx_out->addr; - } break; - case TX_ITEM_TYPE_OUT_COND: - ++l_out_idx; - default: - continue; - } - ++l_out_idx; - if ( !dap_strcmp( a_token_ticker, l_iter_current->cache_data.token_ticker ) // Tokens match - && !dap_chain_addr_compare( a_addr, &l_out_addr ) // Addresses match - && !s_ledger_tx_hash_is_used_out_item( l_iter_current, l_out_idx, NULL ) // Output is unused - && !dap_chain_datum_tx_verify_sign(l_cur_tx) // Signs are valid - ) SUM_256_256(balance, l_add, &balance); - } - } - pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); - return balance; -} - /** * Get the transaction in the cache by the addr in out item * @@ -5435,7 +1336,6 @@ dap_list_t* dap_ledger_tx_cache_find_out_cond_all(dap_ledger_t *a_ledger, dap_ch return l_ret; } - /** * Get the transaction in the cache with the out_cond item * @@ -5650,50 +1550,6 @@ dap_list_t *dap_ledger_get_list_tx_outs(dap_ledger_t *a_ledger, const char *a_to return l_list_used_out; } - -// Add new verificator callback with associated subtype. Returns 1 if callback replaced, -1 error, overwise returns 0 -int dap_ledger_verificator_add(dap_chain_tx_out_cond_subtype_t a_subtype, dap_ledger_verificator_callback_t a_callback, dap_ledger_updater_callback_t a_callback_added, dap_ledger_delete_callback_t a_callback_deleted) -{ - dap_ledger_verificator_t *l_new_verificator = NULL; - int l_tmp = (int)a_subtype; - pthread_rwlock_rdlock(&s_verificators_rwlock); - HASH_FIND_INT(s_verificators, &l_tmp, l_new_verificator); - pthread_rwlock_unlock(&s_verificators_rwlock); - if (l_new_verificator) { - l_new_verificator->callback = a_callback; - return 1; - } - l_new_verificator = DAP_NEW(dap_ledger_verificator_t); - if (!l_new_verificator) { - log_it(L_CRITICAL, "%s", c_error_memory_alloc); - return -1; - } - l_new_verificator->subtype = (int)a_subtype; - l_new_verificator->callback = a_callback; - l_new_verificator->callback_added = a_callback_added; - l_new_verificator->callback_deleted = a_callback_deleted; - pthread_rwlock_wrlock(&s_verificators_rwlock); - HASH_ADD_INT(s_verificators, subtype, l_new_verificator); - pthread_rwlock_unlock(&s_verificators_rwlock); - return 0; -} - -int dap_chain_ledger_voting_verificator_add(dap_chain_ledger_voting_callback_t a_callback, dap_chain_ledger_voting_delete_callback_t a_callback_delete) -{ - if (!a_callback) - return -1; - - if (!s_voting_callbacks.voting_callback || !s_voting_callbacks.voting_delete_callback){ - s_voting_callbacks.voting_callback = a_callback; - s_voting_callbacks.voting_delete_callback = a_callback_delete; - return 1; - } - - s_voting_callbacks.voting_callback = a_callback; - s_voting_callbacks.voting_delete_callback = a_callback_delete; - return 0; -} - dap_list_t *dap_ledger_get_txs(dap_ledger_t *a_ledger, size_t a_count, size_t a_page, bool a_reverse, bool a_unspent_only) { dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); @@ -5853,24 +1709,6 @@ dap_list_t *dap_ledger_get_list_tx_cond_outs(dap_ledger_t *a_ledger, const char : ( dap_list_free_full(l_list_used_out, NULL), NULL ); } -void dap_ledger_tx_add_notify(dap_ledger_t *a_ledger, dap_ledger_tx_add_notify_t a_callback, void *a_arg) -{ - dap_return_if_fail(a_ledger && a_callback); - dap_ledger_tx_notifier_t *l_notifier; - DAP_NEW_Z_RET(l_notifier, dap_ledger_tx_notifier_t, NULL); - *l_notifier = (dap_ledger_tx_notifier_t) { .callback = a_callback, .arg = a_arg }; - PVT(a_ledger)->tx_add_notifiers = dap_list_append(PVT(a_ledger)->tx_add_notifiers, l_notifier); -} - -void dap_ledger_bridged_tx_notify_add(dap_ledger_t *a_ledger, dap_ledger_bridged_tx_notify_t a_callback, void *a_arg) -{ - dap_return_if_fail(a_ledger && a_callback); - dap_ledger_bridged_tx_notifier_t *l_notifier; - DAP_NEW_Z_RET(l_notifier, dap_ledger_bridged_tx_notifier_t, NULL); - *l_notifier = (dap_ledger_bridged_tx_notifier_t) { .callback = a_callback, .arg = a_arg }; - PVT(a_ledger)->bridged_tx_notifiers = dap_list_append(PVT(a_ledger)->bridged_tx_notifiers , l_notifier); -} - bool dap_ledger_cache_enabled(dap_ledger_t *a_ledger) { return PVT(a_ledger)->cached; @@ -5881,18 +1719,6 @@ void dap_ledger_set_cache_tx_check_callback(dap_ledger_t *a_ledger, dap_ledger_c PVT(a_ledger)->cache_tx_check_callback = a_callback; } -const char *dap_ledger_tx_calculate_main_ticker(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, int *a_ledger_rc) -{ - static _Thread_local char s_main_ticker[DAP_CHAIN_TICKER_SIZE_MAX] = { '\0' }; - dap_hash_fast_t l_tx_hash = dap_chain_node_datum_tx_calc_hash(a_tx); - int l_rc = s_tx_cache_check(a_ledger, a_tx, &l_tx_hash, false, NULL, NULL, s_main_ticker, NULL, NULL, false); - if (l_rc == DAP_LEDGER_CHECK_ALREADY_CACHED) - dap_strncpy( s_main_ticker, dap_ledger_tx_get_token_ticker_by_hash(a_ledger, &l_tx_hash), DAP_CHAIN_TICKER_SIZE_MAX ); - if (a_ledger_rc) - *a_ledger_rc = l_rc; - return s_main_ticker; -} - dap_list_t *dap_ledger_states_aggregate(dap_ledger_t *a_ledger) { dap_list_t *ret = NULL; diff --git a/modules/ledger/dap_chain_net_anchor.c b/modules/ledger/dap_chain_ledger_anchor.c similarity index 79% rename from modules/ledger/dap_chain_net_anchor.c rename to modules/ledger/dap_chain_ledger_anchor.c index 863a363fd6d18a97f21fbf68c98fc64ce2dba34a..8a517fa28e3372cdcfaef995776fff60b050029e 100644 --- a/modules/ledger/dap_chain_net_anchor.c +++ b/modules/ledger/dap_chain_ledger_anchor.c @@ -27,37 +27,22 @@ #include "dap_sign.h" #include "dap_pkey.h" #include "dap_chain.h" -#include "dap_chain_cell.h" #include "dap_chain_common.h" #include "dap_chain_ledger.h" #include "dap_chain_datum_decree.h" #include "dap_chain_net_srv_stake_pos_delegate.h" #include "dap_chain_net.h" #include "dap_chain_net_tx.h" -#include "dap_chain_net_decree.h" +#include "dap_chain_ledger_pvt.h" #include "dap_chain_datum_anchor.h" #include "dap_chain_cs_esbocs.h" -#define LOG_TAG "chain_net_anchor" - -typedef struct anchor_table{ - dap_hash_fast_t anchor_hash; - dap_chain_datum_anchor_t *anchor; - UT_hash_handle hh; -} anchor_table_t; +#define LOG_TAG "dap_ledger_anchor" // private function prototypes static bool s_verify_pubkeys(dap_sign_t *a_sign, dap_sign_t **a_decree_signs, size_t a_num_of_decree_sign); static inline dap_sign_t *s_concate_all_signs_in_array(dap_sign_t *a_in_signs, size_t a_signs_size, size_t *a_sings_count, size_t *a_signs_arr_size); -static bool s_debug_more = false; - -int dap_chain_net_anchor_init() -{ - s_debug_more = dap_config_get_item_bool_default(g_config, "chain_net", "debug_more", s_debug_more); - return 0; -} - static int s_anchor_verify(dap_chain_net_t *a_net, dap_chain_datum_anchor_t *a_anchor, size_t a_data_size, bool a_load_mode) { if (a_data_size < sizeof(dap_chain_datum_anchor_t)) @@ -83,9 +68,9 @@ static int s_anchor_verify(dap_chain_net_t *a_net, dap_chain_datum_anchor_t *a_a bool l_sign_authorized = false; size_t l_signs_size_original = a_anchor->header.signs_size; a_anchor->header.signs_size = 0; + dap_ledger_private_t *l_ledger_pvt = PVT(a_net->pub.ledger); for (size_t i = 0; i < l_num_of_unique_signs; i++) { - dap_chain_net_decree_t *l_net_decree = dap_chain_net_get_net_decree(a_net); - for (dap_list_t *it = l_net_decree->pkeys; it; it = it->next) { + for (dap_list_t *it = l_ledger_pvt->decree_owners_pkeys; it; it = it->next) { if (dap_pkey_compare_with_sign(it->data, l_unique_signs[i])) { // TODO make signs verification in s_concate_all_signs_in_array to correctly header.signs_size calculation size_t l_verify_data_size = a_anchor->header.data_size + sizeof(dap_chain_datum_anchor_t); @@ -118,7 +103,7 @@ static int s_anchor_verify(dap_chain_net_t *a_net, dap_chain_datum_anchor_t *a_a return 0; bool l_is_applied = false; - l_decree = dap_chain_net_decree_get_by_hash(a_net, &l_decree_hash, &l_is_applied); + l_decree = dap_ledger_decree_get_by_hash(a_net, &l_decree_hash, &l_is_applied); if (!l_decree) { log_it(L_WARNING, "Can't get decree by hash %s", dap_hash_fast_to_str_static(&l_decree_hash)); return DAP_CHAIN_CS_VERIFY_CODE_NO_DECREE; @@ -132,12 +117,12 @@ static int s_anchor_verify(dap_chain_net_t *a_net, dap_chain_datum_anchor_t *a_a } // Public functions -int dap_chain_net_anchor_verify(dap_chain_net_t *a_net, dap_chain_datum_anchor_t *a_anchor, size_t a_data_size) +int dap_ledger_anchor_verify(dap_chain_net_t *a_net, dap_chain_datum_anchor_t *a_anchor, size_t a_data_size) { return s_anchor_verify(a_net, a_anchor, a_data_size, false); } -int dap_chain_net_anchor_load(dap_chain_datum_anchor_t * a_anchor, dap_chain_t *a_chain, dap_hash_fast_t *a_anchor_hash) +int dap_ledger_anchor_load(dap_chain_datum_anchor_t *a_anchor, dap_chain_t *a_chain, dap_hash_fast_t *a_anchor_hash) { int ret_val = 0; @@ -148,12 +133,6 @@ int dap_chain_net_anchor_load(dap_chain_datum_anchor_t * a_anchor, dap_chain_t * } dap_chain_net_t *l_net = dap_chain_net_by_id(a_chain->net_id); - dap_chain_net_decree_t *l_net_decree = dap_chain_net_get_net_decree(l_net); - if (!l_net_decree) - { - log_it(L_WARNING, "Decree is not inited!"); - return -108; - } if ((ret_val = s_anchor_verify(l_net, a_anchor, dap_chain_datum_anchor_get_size(a_anchor), true)) != 0) { @@ -168,50 +147,56 @@ int dap_chain_net_anchor_load(dap_chain_datum_anchor_t * a_anchor, dap_chain_t * return -109; } - if ((ret_val = dap_chain_net_decree_apply(&l_hash, NULL, a_chain, true)) != 0){ - debug_if(s_debug_more, L_WARNING, "Decree applying failed"); + if ((ret_val = dap_ledger_decree_apply(&l_hash, NULL, a_chain, true)) != 0){ + debug_if(g_debug_ledger, L_WARNING, "Decree applying failed"); return ret_val; } - - - anchor_table_t **l_anchors = dap_chain_net_get_anchors(l_net); - anchor_table_t *l_new_anchor = DAP_NEW_Z(anchor_table_t); - l_new_anchor->anchor_hash = *a_anchor_hash; - l_new_anchor->anchor = a_anchor; - HASH_ADD(hh, *l_anchors, anchor_hash, sizeof(l_new_anchor->anchor_hash), l_new_anchor); + dap_ledger_private_t *l_ledger_pvt = PVT(l_net->pub.ledger); + dap_ledger_anchor_item_t *l_new_anchor = NULL; + unsigned l_hash_value; + HASH_VALUE(a_anchor_hash, sizeof(dap_hash_fast_t), l_hash_value); + pthread_rwlock_wrlock(&l_ledger_pvt->decrees_rwlock); + HASH_FIND_BYHASHVALUE(hh, l_ledger_pvt->anchors, a_anchor_hash, sizeof(dap_hash_fast_t), l_hash_value, l_new_anchor); + if (!l_new_anchor) { + l_new_anchor = DAP_NEW_Z(dap_ledger_anchor_item_t); + if (!l_new_anchor) { + log_it(L_CRITICAL, "Memory allocation error"); + pthread_rwlock_unlock(&l_ledger_pvt->decrees_rwlock); + return -1; + } + l_new_anchor->anchor_hash = *a_anchor_hash; + l_new_anchor->anchor = a_anchor; + HASH_ADD_BYHASHVALUE(hh, l_ledger_pvt->anchors, anchor_hash, sizeof(dap_hash_fast_t), l_hash_value, l_new_anchor); + } + pthread_rwlock_unlock(&l_ledger_pvt->decrees_rwlock); return ret_val; } dap_chain_datum_anchor_t * s_find_previous_anchor(dap_hash_fast_t *a_old_anchor_hash, dap_chain_net_t *a_net) { - if (!a_old_anchor_hash || !a_net){ - log_it(L_ERROR,"Params are NULL"); - return NULL; - } - - dap_chain_net_t *l_net = a_net; dap_chain_datum_anchor_t * l_ret_anchor = NULL; - dap_chain_datum_anchor_t *l_old_anchor = NULL; - - anchor_table_t **l_anchors_ptr = dap_chain_net_get_anchors(l_net); - anchor_table_t *l_anchor = NULL; - HASH_FIND(hh, *l_anchors_ptr, a_old_anchor_hash, sizeof(*a_old_anchor_hash), l_anchor); - if (!l_old_anchor){ - log_it(L_WARNING,"Can not find anchor"); + dap_return_val_if_fail(a_old_anchor_hash && a_net, NULL); + + dap_ledger_private_t *l_ledger_pvt = PVT(a_net->pub.ledger); + dap_ledger_anchor_item_t *l_anchor = NULL; + pthread_rwlock_rdlock(&l_ledger_pvt->decrees_rwlock); + HASH_FIND(hh, l_ledger_pvt->anchors, a_old_anchor_hash, sizeof(dap_hash_fast_t), l_anchor); + pthread_rwlock_unlock(&l_ledger_pvt->decrees_rwlock); + if (!l_anchor) { + log_it(L_WARNING, "Can not find anchor"); return NULL; } - l_old_anchor = l_anchor->anchor; - + dap_chain_datum_anchor_t *l_old_anchor = l_anchor->anchor; dap_hash_fast_t l_old_decrere_hash = {}; if (dap_chain_datum_anchor_get_hash_from_data(l_old_anchor, &l_old_decrere_hash) != 0) return NULL; - dap_chain_datum_decree_t *l_old_decree = dap_chain_net_decree_get_by_hash(l_net, &l_old_decrere_hash, NULL); + dap_chain_datum_decree_t *l_old_decree = dap_ledger_decree_get_by_hash(a_net, &l_old_decrere_hash, NULL); uint16_t l_old_decree_type = l_old_decree->header.type; uint16_t l_old_decree_subtype = l_old_decree->header.sub_type; - anchor_table_t *l_anchors = HASH_LAST(*l_anchors_ptr); + dap_ledger_anchor_item_t *l_anchors = HASH_LAST(l_ledger_pvt->anchors); for(; l_anchors; l_anchors = l_anchors->hh.prev){ size_t l_datums_count = 0; @@ -221,7 +206,7 @@ dap_chain_datum_anchor_t * s_find_previous_anchor(dap_hash_fast_t *a_old_anchor_ continue; bool l_is_applied = false; - dap_chain_datum_decree_t *l_decree = dap_chain_net_decree_get_by_hash(l_net, &l_hash, &l_is_applied); + dap_chain_datum_decree_t *l_decree = dap_ledger_decree_get_by_hash(a_net, &l_hash, &l_is_applied); if (!l_decree) continue; @@ -240,13 +225,13 @@ dap_chain_datum_anchor_t * s_find_previous_anchor(dap_hash_fast_t *a_old_anchor_ if(dap_chain_addr_compare(&l_addr_old, &l_addr_new)){ l_ret_anchor = l_curr_anchor; - dap_chain_net_decree_reset_applied(l_net, &l_hash); + dap_ledger_decree_reset_applied(a_net, &l_hash); break; } } else if (l_decree->header.type == l_old_decree_type && l_decree->header.sub_type == l_old_decree_subtype){ // check addr if l_decree type is stake approve l_ret_anchor = l_curr_anchor; - dap_chain_net_decree_reset_applied(l_net, &l_hash); + dap_ledger_decree_reset_applied(a_net, &l_hash); break; } if (l_ret_anchor) @@ -258,16 +243,18 @@ dap_chain_datum_anchor_t * s_find_previous_anchor(dap_hash_fast_t *a_old_anchor_ void s_delete_anchor(dap_chain_net_t *a_net, dap_hash_fast_t *a_anchor_hash) { - anchor_table_t **l_anchors_ptr = dap_chain_net_get_anchors(a_net); - anchor_table_t *l_anchor = NULL; - HASH_FIND(hh, *l_anchors_ptr, a_anchor_hash, sizeof(*a_anchor_hash), l_anchor); - if(l_anchor){ - HASH_DEL(*l_anchors_ptr, l_anchor); + dap_ledger_private_t *l_ledger_pvt = PVT(a_net->pub.ledger); + dap_ledger_anchor_item_t *l_anchor = NULL; + pthread_rwlock_wrlock(&l_ledger_pvt->decrees_rwlock); + HASH_FIND(hh, l_ledger_pvt->anchors, a_anchor_hash, sizeof(dap_hash_fast_t), l_anchor); + if (l_anchor) { + HASH_DEL(l_ledger_pvt->anchors, l_anchor); DAP_DEL_Z(l_anchor); } + pthread_rwlock_unlock(&l_ledger_pvt->decrees_rwlock); } -int dap_chain_net_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t *a_chain, dap_hash_fast_t *a_anchor_hash) +int dap_ledger_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t *a_chain, dap_hash_fast_t *a_anchor_hash) { int ret_val = 0; @@ -279,12 +266,6 @@ int dap_chain_net_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t dap_chain_net_t *l_net = dap_chain_net_by_id(a_chain->net_id); - if (!dap_chain_net_get_net_decree(l_net)) - { - log_it(L_WARNING,"Decree is not inited!"); - return -108; - } - ret_val = s_anchor_verify(l_net, a_anchor, dap_chain_datum_anchor_get_size(a_anchor), true); if (ret_val != 0) @@ -297,7 +278,7 @@ int dap_chain_net_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t if (dap_chain_datum_anchor_get_hash_from_data(a_anchor, &l_hash) != 0) return -110; - dap_chain_datum_decree_t *l_decree = dap_chain_net_decree_get_by_hash(l_net, &l_hash, NULL); + dap_chain_datum_decree_t *l_decree = dap_ledger_decree_get_by_hash(l_net, &l_hash, NULL); if (!l_decree) return -111; @@ -305,7 +286,7 @@ int dap_chain_net_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t switch (l_decree->header.sub_type) { case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_FEE:{ - dap_chain_net_decree_reset_applied(l_net, &l_hash); + dap_ledger_decree_reset_applied(l_net, &l_hash); dap_chain_datum_anchor_t * l_new_anchor = s_find_previous_anchor(a_anchor_hash, l_net); s_delete_anchor(l_net, a_anchor_hash); if (l_new_anchor){// if previous anchor is founded apply it @@ -315,7 +296,7 @@ int dap_chain_net_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t return -109; } - if((ret_val = dap_chain_net_decree_apply(&l_hash, NULL, a_chain, true))!=0){ + if((ret_val = dap_ledger_decree_apply(&l_hash, NULL, a_chain, true))!=0){ log_it(L_WARNING,"Decree applying failed"); return ret_val; } @@ -336,13 +317,13 @@ int dap_chain_net_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t return -105; } dap_chain_net_srv_stake_key_invalidate(&l_signing_addr); - dap_chain_net_decree_reset_applied(l_net, &l_hash); + dap_ledger_decree_reset_applied(l_net, &l_hash); s_delete_anchor(l_net, a_anchor_hash); } break; case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_STAKE_INVALIDATE:{ // Find previous anchor with this stake approve and apply it - dap_chain_net_decree_reset_applied(l_net, &l_hash); + dap_ledger_decree_reset_applied(l_net, &l_hash); dap_chain_datum_anchor_t * l_new_anchor = s_find_previous_anchor(a_anchor_hash, l_net); s_delete_anchor(l_net, a_anchor_hash); if (l_new_anchor){// if previous anchor is founded apply it @@ -351,7 +332,7 @@ int dap_chain_net_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t log_it(L_WARNING,"Can not find datum hash in anchor data"); return -109; } - if((ret_val = dap_chain_net_decree_apply(&l_hash, NULL, a_chain, true))!=0){ + if((ret_val = dap_ledger_decree_apply(&l_hash, NULL, a_chain, true))!=0){ log_it(L_WARNING,"Decree applying failed"); return ret_val; } @@ -359,7 +340,7 @@ int dap_chain_net_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t } break; case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_STAKE_MIN_VALUE:{ - dap_chain_net_decree_reset_applied(l_net, &l_hash); + dap_ledger_decree_reset_applied(l_net, &l_hash); dap_chain_datum_anchor_t * l_new_anchor = s_find_previous_anchor(a_anchor_hash, l_net); s_delete_anchor(l_net, a_anchor_hash); if (l_new_anchor){// if previous anchor is founded apply it @@ -368,7 +349,7 @@ int dap_chain_net_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t log_it(L_WARNING,"Can not find datum hash in anchor data"); return -109; } - if((ret_val = dap_chain_net_decree_apply(&l_hash, NULL, a_chain, true))!=0){ + if((ret_val = dap_ledger_decree_apply(&l_hash, NULL, a_chain, true))!=0){ log_it(L_WARNING,"Decree applying failed"); return ret_val; } @@ -379,7 +360,7 @@ int dap_chain_net_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t } break; case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_STAKE_MIN_VALIDATORS_COUNT:{ - dap_chain_net_decree_reset_applied(l_net, &l_hash); + dap_ledger_decree_reset_applied(l_net, &l_hash); dap_chain_datum_anchor_t * l_new_anchor = s_find_previous_anchor(a_anchor_hash, l_net); s_delete_anchor(l_net, a_anchor_hash); if (l_new_anchor){// if previous anchor is founded apply it @@ -389,7 +370,7 @@ int dap_chain_net_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t return -109; } - if((ret_val = dap_chain_net_decree_apply(&l_hash, NULL, a_chain, true))!=0){ + if((ret_val = dap_ledger_decree_apply(&l_hash, NULL, a_chain, true))!=0){ log_it(L_WARNING,"Decree applying failed"); return ret_val; } @@ -400,7 +381,7 @@ int dap_chain_net_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t break; case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_REWARD:{ // find previous anchor with rewarrd and apply it - dap_chain_net_decree_reset_applied(l_net, &l_hash); + dap_ledger_decree_reset_applied(l_net, &l_hash); dap_chain_net_remove_last_reward(dap_chain_net_by_id(a_chain->net_id)); s_delete_anchor(l_net, a_anchor_hash); } diff --git a/modules/ledger/dap_chain_net_decree.c b/modules/ledger/dap_chain_ledger_decree.c similarity index 80% rename from modules/ledger/dap_chain_net_decree.c rename to modules/ledger/dap_chain_ledger_decree.c index 18529155c6521a5a4ec2a19656e511080086298a..6e662fcfac054a4c8466224a68f43686a7c672a2 100644 --- a/modules/ledger/dap_chain_net_decree.c +++ b/modules/ledger/dap_chain_ledger_decree.c @@ -28,37 +28,21 @@ #include "dap_pkey.h" #include "dap_chain_common.h" #include "dap_chain_net.h" -#include "dap_chain_net_decree.h" +#include "dap_chain_ledger_pvt.h" #include "dap_chain_cs_esbocs.h" #include "dap_chain_net_tx.h" #include "dap_chain_net_srv_stake_pos_delegate.h" #include "dap_http_ban_list_client.h" - - -#define LOG_TAG "chain_net_decree" - - -// private types definition -typedef struct decree_table { - dap_hash_fast_t key; - bool wait_for_apply, is_applied; - dap_chain_datum_decree_t *decree; - UT_hash_handle hh; -} decree_table_t; - -// Private variable - +#define LOG_TAG "dap_ledger_decree" // Private fuctions prototype static bool s_verify_pkey (dap_sign_t *a_sign, dap_chain_net_t *a_net); static int s_common_decree_handler(dap_chain_datum_decree_t *a_decree, dap_chain_net_t *a_net, bool a_apply, bool a_anchored); static int s_service_decree_handler(dap_chain_datum_decree_t *a_decree, dap_chain_net_t *a_net, bool a_apply); -static bool s_debug_more = false; - // Public functions -int dap_chain_net_decree_init(dap_chain_net_t *a_net) +int dap_ledger_decree_create(dap_chain_net_t *a_net) { size_t l_auth_certs_count = 0; @@ -67,8 +51,6 @@ int dap_chain_net_decree_init(dap_chain_net_t *a_net) return -106; } - s_debug_more = dap_config_get_item_bool_default(g_config,"chain_net","debug_more", s_debug_more); - dap_list_t *l_net_keys = NULL; uint16_t l_count_verify = 0; for (dap_chain_t *l_chain = a_net->pub.chains; l_chain; l_chain = l_chain->next) { @@ -83,40 +65,34 @@ int dap_chain_net_decree_init(dap_chain_net_t *a_net) log_it(L_WARNING,"Certificates for net %s not found.", a_net->pub.name); return -1; } + dap_ledger_private_t *l_ledger_pvt = PVT(a_net->pub.ledger); + l_ledger_pvt->decree_min_num_of_signers = l_count_verify; + l_ledger_pvt->decree_num_of_owners = l_auth_certs_count; + l_ledger_pvt->decree_owners_pkeys = l_net_keys; - dap_chain_net_decree_t *l_decree = NULL; - l_decree = DAP_NEW_Z(dap_chain_net_decree_t); - if (!l_decree) { - log_it(L_CRITICAL, "Out of memory"); - return -2; - } - - l_decree->min_num_of_owners = l_count_verify; - l_decree->num_of_owners = l_auth_certs_count; - l_decree->pkeys = l_net_keys; - dap_chain_net_set_net_decree(a_net, l_decree); return 0; } -int dap_chain_net_decree_deinit(dap_chain_net_t *a_net) +static int s_decree_clear(dap_chain_net_t *a_net) { - dap_chain_net_decree_t *l_decree = dap_chain_net_get_net_decree(a_net); - dap_list_free_full(l_decree->pkeys, NULL); - DAP_DELETE(l_decree); - decree_table_t **l_decrees = dap_chain_net_get_decrees(a_net), *l_cur_decree, *l_tmp; - HASH_ITER(hh, *l_decrees, l_cur_decree, l_tmp) { - HASH_DEL(*l_decrees, l_cur_decree); + dap_ledger_private_t *l_ledger_pvt = PVT(a_net->pub.ledger); + dap_list_free_full(l_ledger_pvt->decree_owners_pkeys, NULL); + dap_ledger_decree_item_t *l_cur_decree, *l_tmp; + pthread_rwlock_wrlock(&l_ledger_pvt->decrees_rwlock); + HASH_ITER(hh, l_ledger_pvt->decrees, l_cur_decree, l_tmp) { + HASH_DEL(l_ledger_pvt->decrees, l_cur_decree); if ( l_cur_decree->decree && !dap_chain_find_by_id(l_cur_decree->decree->header.common_decree_params.net_id, l_cur_decree->decree->header.common_decree_params.chain_id)->is_mapped ) DAP_DELETE(l_cur_decree->decree); DAP_DELETE(l_cur_decree); } + pthread_rwlock_unlock(&l_ledger_pvt->decrees_rwlock); return 0; } -void dap_chain_net_decree_purge(dap_chain_net_t *a_net) +void dap_ledger_decree_purge(dap_chain_net_t *a_net) { - dap_chain_net_decree_deinit(a_net); - dap_chain_net_decree_init(a_net); + s_decree_clear(a_net); + dap_ledger_decree_create(a_net); } static int s_decree_verify(dap_chain_net_t *a_net, dap_chain_datum_decree_t *a_decree, size_t a_data_size, dap_chain_hash_fast_t *a_decree_hash, bool a_anchored) @@ -134,10 +110,13 @@ static int s_decree_verify(dap_chain_net_t *a_net, dap_chain_datum_decree_t *a_d return -122; } - decree_table_t **l_decrees = dap_chain_net_get_decrees(a_net), *l_sought_decree = NULL; - HASH_FIND(hh, *l_decrees, a_decree_hash, sizeof(dap_hash_fast_t), l_sought_decree); + dap_ledger_private_t *l_ledger_pvt = PVT(a_net->pub.ledger); + dap_ledger_decree_item_t *l_sought_decree = NULL; + pthread_rwlock_rdlock(&l_ledger_pvt->decrees_rwlock); + HASH_FIND(hh, l_ledger_pvt->decrees, a_decree_hash, sizeof(dap_hash_fast_t), l_sought_decree); + pthread_rwlock_unlock(&l_ledger_pvt->decrees_rwlock); if (l_sought_decree && l_sought_decree->decree) { - debug_if(s_debug_more, L_WARNING, "Decree with hash %s is already present", dap_hash_fast_to_str_static(a_decree_hash)); + debug_if(g_debug_ledger, L_WARNING, "Decree with hash %s is already present", dap_hash_fast_to_str_static(a_decree_hash)); return -123; } @@ -154,13 +133,7 @@ static int s_decree_verify(dap_chain_net_t *a_net, dap_chain_datum_decree_t *a_d // Find unique pkeys in pkeys set from previous step and check that number of signs > min size_t l_num_of_unique_signs = 0; dap_sign_t **l_unique_signs = dap_sign_get_unique_signs(l_signs_block, l_signs_size, &l_num_of_unique_signs); - dap_chain_net_decree_t *l_decree = dap_chain_net_get_net_decree(a_net); - if (!l_decree) { - log_it(L_ERROR, "Decree module hasn't been initialized yet"); - return -404; - } - - uint16_t l_min_signs = l_decree->min_num_of_owners; + uint16_t l_min_signs = l_ledger_pvt->decree_min_num_of_signers; if (l_num_of_unique_signs < l_min_signs) { log_it(L_WARNING, "Not enough unique signatures, get %zu from %hu", l_num_of_unique_signs, l_min_signs); return -106; @@ -222,41 +195,38 @@ static int s_decree_verify(dap_chain_net_t *a_net, dap_chain_datum_decree_t *a_d return 0; } -int dap_chain_net_decree_verify(dap_chain_net_t *a_net, dap_chain_datum_decree_t *a_decree, size_t a_data_size, dap_chain_hash_fast_t *a_decree_hash) +int dap_ledger_decree_verify(dap_chain_net_t *a_net, dap_chain_datum_decree_t *a_decree, size_t a_data_size, dap_chain_hash_fast_t *a_decree_hash) { return s_decree_verify(a_net, a_decree, a_data_size, a_decree_hash, false); } -int dap_chain_net_decree_apply(dap_hash_fast_t *a_decree_hash, dap_chain_datum_decree_t *a_decree, dap_chain_t *a_chain, bool a_anchored) +int dap_ledger_decree_apply(dap_hash_fast_t *a_decree_hash, dap_chain_datum_decree_t *a_decree, dap_chain_t *a_chain, bool a_anchored) { + dap_return_val_if_fail(a_decree_hash && a_chain, -107); int ret_val = 0; - dap_chain_net_t *l_net = NULL; - - if (!a_decree_hash || !a_chain) - { - log_it(L_ERROR,"Invalid arguments."); - return -107; - } - - l_net = dap_chain_net_by_id(a_chain->net_id); + dap_chain_net_t *l_net = dap_chain_net_by_id(a_chain->net_id); - if (!l_net || !dap_chain_net_get_net_decree(l_net)) - { - log_it(L_WARNING,"Decree is not inited!"); + if (!l_net) { + log_it(L_WARNING, "Invalid net ID 0x%016" DAP_UINT64_FORMAT_x, a_chain->net_id.uint64); return -108; } - - decree_table_t **l_decrees = dap_chain_net_get_decrees(l_net), *l_new_decree = NULL; - HASH_FIND(hh, *l_decrees, a_decree_hash, sizeof(dap_hash_fast_t), l_new_decree); + dap_ledger_private_t *l_ledger_pvt = PVT(l_net->pub.ledger); + dap_ledger_decree_item_t *l_new_decree = NULL; + unsigned l_hash_value; + HASH_VALUE(a_decree_hash, sizeof(dap_hash_fast_t), l_hash_value); + pthread_rwlock_wrlock(&l_ledger_pvt->decrees_rwlock); + HASH_FIND_BYHASHVALUE(hh, l_ledger_pvt->decrees, a_decree_hash, sizeof(dap_hash_fast_t), l_hash_value, l_new_decree); if (!l_new_decree) { - l_new_decree = DAP_NEW_Z(decree_table_t); + l_new_decree = DAP_NEW_Z(dap_ledger_decree_item_t); if (!l_new_decree) { log_it(L_CRITICAL, "Memory allocation error"); + pthread_rwlock_unlock(&l_ledger_pvt->decrees_rwlock); return -1; } l_new_decree->key = *a_decree_hash; - HASH_ADD(hh, *l_decrees, key, sizeof(dap_hash_fast_t), l_new_decree); + HASH_ADD_BYHASHVALUE(hh, l_ledger_pvt->decrees, key, sizeof(dap_hash_fast_t), l_hash_value, l_new_decree); } + pthread_rwlock_unlock(&l_ledger_pvt->decrees_rwlock); if (!a_decree) { // Processing anchor for decree if (!l_new_decree->decree) { @@ -265,12 +235,12 @@ int dap_chain_net_decree_apply(dap_hash_fast_t *a_decree_hash, dap_chain_datum_d return -110; } if (l_new_decree->is_applied) { - debug_if(s_debug_more, L_WARNING, "Decree already applied"); + debug_if(g_debug_ledger, L_WARNING, "Decree already applied"); return -111; } } else { // Process decree itself if (l_new_decree->decree) { - debug_if(s_debug_more, L_WARNING, "Decree with hash %s is already present", dap_hash_fast_to_str_static(a_decree_hash)); + debug_if(g_debug_ledger, L_WARNING, "Decree with hash %s is already present", dap_hash_fast_to_str_static(a_decree_hash)); return -123; } l_new_decree->decree = a_chain->is_mapped ? a_decree : DAP_DUP_SIZE(a_decree, dap_chain_datum_decree_get_size(a_decree)); @@ -296,12 +266,12 @@ int dap_chain_net_decree_apply(dap_hash_fast_t *a_decree_hash, dap_chain_datum_d l_new_decree->is_applied = true; l_new_decree->wait_for_apply = false; } else - debug_if(s_debug_more, L_ERROR,"Decree applying failed!"); + debug_if(g_debug_ledger, L_ERROR,"Decree applying failed!"); return ret_val; } -int dap_chain_net_decree_load(dap_chain_datum_decree_t * a_decree, dap_chain_t *a_chain, dap_chain_hash_fast_t *a_decree_hash) +int dap_ledger_decree_load(dap_chain_datum_decree_t * a_decree, dap_chain_t *a_chain, dap_chain_hash_fast_t *a_decree_hash) { int ret_val = 0; if (!a_chain || !a_decree) { @@ -311,11 +281,6 @@ int dap_chain_net_decree_load(dap_chain_datum_decree_t * a_decree, dap_chain_t * dap_chain_net_t *l_net = dap_chain_net_by_id(a_chain->net_id); - if ( !dap_chain_net_get_net_decree(l_net) ) { - log_it(L_WARNING, "Decree is not inited!"); - return -108; - } - size_t l_data_size = dap_chain_datum_decree_get_size(a_decree); if ((ret_val = s_decree_verify(l_net, a_decree, l_data_size, a_decree_hash, false)) != 0) { @@ -323,25 +288,31 @@ int dap_chain_net_decree_load(dap_chain_datum_decree_t * a_decree, dap_chain_t * return ret_val; } - return dap_chain_net_decree_apply(a_decree_hash, a_decree, a_chain, false); + return dap_ledger_decree_apply(a_decree_hash, a_decree, a_chain, false); } -int dap_chain_net_decree_reset_applied(dap_chain_net_t *a_net, dap_chain_hash_fast_t *a_decree_hash) +int dap_ledger_decree_reset_applied(dap_chain_net_t *a_net, dap_chain_hash_fast_t *a_decree_hash) { - if (!a_net || !a_decree_hash) - return -1; - decree_table_t **l_decrees = dap_chain_net_get_decrees(a_net), *l_sought_decree = NULL; - HASH_FIND(hh, *l_decrees, a_decree_hash, sizeof(dap_hash_fast_t), l_sought_decree); + dap_return_val_if_fail(a_net && a_decree_hash, -1); + dap_ledger_private_t *l_ledger_pvt = PVT(a_net->pub.ledger); + dap_ledger_decree_item_t *l_sought_decree = NULL; + pthread_rwlock_rdlock(&l_ledger_pvt->decrees_rwlock); + HASH_FIND(hh, l_ledger_pvt->decrees, a_decree_hash, sizeof(dap_hash_fast_t), l_sought_decree); + pthread_rwlock_unlock(&l_ledger_pvt->decrees_rwlock); if (!l_sought_decree) return -2; l_sought_decree->is_applied = false; return 0; } -dap_chain_datum_decree_t *dap_chain_net_decree_get_by_hash(dap_chain_net_t *a_net, dap_hash_fast_t *a_hash, bool *is_applied) +dap_chain_datum_decree_t *dap_ledger_decree_get_by_hash(dap_chain_net_t *a_net, dap_hash_fast_t *a_decree_hash, bool *is_applied) { - decree_table_t **l_decrees = dap_chain_net_get_decrees(a_net), *l_sought_decree = NULL; - HASH_FIND(hh, *l_decrees, a_hash, sizeof(dap_hash_fast_t), l_sought_decree); + dap_return_val_if_fail(a_net && a_decree_hash, NULL); + dap_ledger_decree_item_t *l_sought_decree = NULL; + dap_ledger_private_t *l_ledger_pvt = PVT(a_net->pub.ledger); + pthread_rwlock_rdlock(&l_ledger_pvt->decrees_rwlock); + HASH_FIND(hh, l_ledger_pvt->decrees, a_decree_hash, sizeof(dap_hash_fast_t), l_sought_decree); + pthread_rwlock_unlock(&l_ledger_pvt->decrees_rwlock); return ( !l_sought_decree || !l_sought_decree->decree ) ? NULL : ({ if (is_applied) { *is_applied = l_sought_decree->is_applied; } l_sought_decree->decree; }); @@ -350,7 +321,8 @@ dap_chain_datum_decree_t *dap_chain_net_decree_get_by_hash(dap_chain_net_t *a_ne // Private functions static bool s_verify_pkey (dap_sign_t *a_sign, dap_chain_net_t *a_net) { - for (dap_list_t *it = dap_chain_net_get_net_decree(a_net)->pkeys; it; it = it->next) + dap_ledger_private_t *l_ledger_pvt = PVT(a_net->pub.ledger); + for (dap_list_t *it = l_ledger_pvt->decree_owners_pkeys; it; it = it->next) if (dap_pkey_compare_with_sign(it->data, a_sign)) return true; return false; @@ -397,11 +369,10 @@ static int s_common_decree_handler(dap_chain_datum_decree_t *a_decree, dap_chain } if (!a_apply) break; - dap_chain_net_decree_t *l_net_decree = dap_chain_net_get_net_decree(a_net); - l_net_decree->num_of_owners = l_owners_num; - dap_list_free_full(l_net_decree->pkeys, NULL); - - l_net_decree->pkeys = l_owners_list; + dap_ledger_private_t *l_ledger_pvt = PVT(a_net->pub.ledger); + l_ledger_pvt->decree_num_of_owners = l_owners_num; + dap_list_free_full(l_ledger_pvt->decree_owners_pkeys, NULL); + l_ledger_pvt->decree_owners_pkeys = l_owners_list; break; case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_OWNERS_MIN: if (dap_chain_datum_decree_get_min_owners(a_decree, &l_value)) { @@ -414,7 +385,7 @@ static int s_common_decree_handler(dap_chain_datum_decree_t *a_decree, dap_chain } if (!a_apply) break; - dap_chain_net_get_net_decree(a_net)->min_num_of_owners = dap_uint256_to_uint64(l_value); + PVT(a_net->pub.ledger)->decree_min_num_of_signers = dap_uint256_to_uint64(l_value); break; case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_STAKE_APPROVE: if (dap_chain_datum_decree_get_hash(a_decree, &l_hash)){ @@ -436,7 +407,7 @@ static int s_common_decree_handler(dap_chain_datum_decree_t *a_decree, dap_chain if (!a_anchored) break; if (dap_chain_net_srv_stake_verify_key_and_node(&l_addr, &l_node_addr)) { - debug_if(s_debug_more, L_WARNING, "Key and node verification error"); + debug_if(g_debug_ledger, L_WARNING, "Key and node verification error"); return -109; } if (!a_apply) @@ -621,7 +592,7 @@ static int s_common_decree_handler(dap_chain_datum_decree_t *a_decree, dap_chain static int s_service_decree_handler(dap_chain_datum_decree_t * a_decree, dap_chain_net_t *a_net, bool a_apply) { // size_t l_datum_data_size = ; - // dap_chain_net_srv_t * l_srv = dap_chain_net_srv_get(l_decree->header.srv_id); + // dap_chain_srv_t * l_srv = dap_chain_srv_get(l_decree->header.srv_id); // if(l_srv){ // if(l_srv->callbacks.decree){ // dap_chain_net_t * l_net = dap_chain_net_by_id(a_chain->net_id); @@ -634,3 +605,18 @@ static int s_service_decree_handler(dap_chain_datum_decree_t * a_decree, dap_cha return 0; } + +uint16_t dap_ledger_decree_get_min_num_of_signers(dap_ledger_t *a_ledger) +{ + return PVT(a_ledger)->decree_min_num_of_signers; +} + +uint16_t dap_ledger_decree_get_num_of_owners(dap_ledger_t *a_ledger) +{ + return PVT(a_ledger)->decree_num_of_owners; +} + +const dap_list_t *dap_ledger_decree_get_owners_pkeys(dap_ledger_t *a_ledger) +{ + return PVT(a_ledger)->decree_owners_pkeys; +} diff --git a/modules/ledger/dap_chain_ledger_token.c b/modules/ledger/dap_chain_ledger_token.c new file mode 100644 index 0000000000000000000000000000000000000000..a53204dda4a7792883499d46efd261932e7e30e0 --- /dev/null +++ b/modules/ledger/dap_chain_ledger_token.c @@ -0,0 +1,1946 @@ +/* + * Authors: + * Dmitriy A. Gearasimov <gerasimov.dmitriy@demlabs.net> + * Alexander Lysikov <alexander.lysikov@demlabs.net> + * Roman Khlopkov <roman.khlopkov@demlabs.net> + * DeM Labs Inc. https://demlabs.net + * DeM Labs Open source community https://github.com/demlabsinc + * Copyright (c) 2017-2024 + * All rights reserved. + + This file is part of CellFrame SDK the open source project + + CellFrame SDK 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. + + CellFrame SDK 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 CellFrame SDK based project. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "dap_chain_ledger_pvt.h" + +#define LOG_TAG "dap_ledger_token" + +dap_ledger_token_item_t *dap_ledger_pvt_find_token(dap_ledger_t *a_ledger, const char *a_token_ticker) +{ + dap_return_val_if_fail(a_ledger && a_token_ticker, NULL); + dap_ledger_token_item_t *l_token_item = NULL; + pthread_rwlock_rdlock(&PVT(a_ledger)->tokens_rwlock); + HASH_FIND_STR(PVT(a_ledger)->tokens, a_token_ticker, l_token_item); + pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); + return l_token_item; +} + +inline static dap_ledger_hal_item_t *s_check_hal(dap_ledger_t *a_ledger, dap_hash_fast_t *a_hal_hash) +{ + dap_ledger_hal_item_t *ret = NULL; + HASH_FIND(hh, PVT(a_ledger)->hal_items, a_hal_hash, sizeof(dap_hash_fast_t), ret); + debug_if(g_debug_ledger && ret, L_MSG, "Datum %s is whitelisted", dap_hash_fast_to_str_static(a_hal_hash)); + return ret; +} + +/** + * @brief GDB callback for loaded emissions from cache + * @param a_global_db_context + * @param a_rc + * @param a_group + * @param a_key + * @param a_values_total + * @param a_values_shift + * @param a_values_count + * @param a_values + * @param a_arg + * @return Always true thats means to clear up a_values + */ +static bool s_load_cache_gdb_loaded_emissions_callback(dap_global_db_instance_t *a_dbi, + int a_rc, const char *a_group, + const size_t a_values_total, const size_t a_values_count, + dap_global_db_obj_t *a_values, void *a_arg) +{ + dap_ledger_t * l_ledger = (dap_ledger_t*) a_arg; + dap_ledger_private_t * l_ledger_pvt = PVT(l_ledger); + + for (size_t i = 0; i < a_values_count; i++) { + if (a_values[i].value_len <= sizeof(dap_hash_fast_t)) + continue; + const char *c_token_ticker = ((dap_chain_datum_token_emission_t *) + (a_values[i].value + sizeof(dap_hash_fast_t)))->hdr.ticker; + dap_ledger_token_item_t *l_token_item = NULL; + HASH_FIND_STR(l_ledger_pvt->tokens, c_token_ticker, l_token_item); + if (!l_token_item) { + log_it(L_WARNING, "Not found token with ticker [%s], need to 'ledger reload' to update cache", c_token_ticker); + continue; + } + dap_ledger_token_emission_item_t *l_emission_item = DAP_NEW_Z(dap_ledger_token_emission_item_t); + if ( !l_emission_item ) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return false; + } + dap_chain_hash_fast_from_str(a_values[i].key, &l_emission_item->datum_token_emission_hash); + l_emission_item->tx_used_out = *(dap_hash_fast_t*)a_values[i].value; + l_emission_item->datum_token_emission = DAP_DUP_SIZE(a_values[i].value + sizeof(dap_hash_fast_t), + a_values[i].value_len - sizeof(dap_hash_fast_t)); + l_emission_item->datum_token_emission_size = a_values[i].value_len - sizeof(dap_hash_fast_t); + HASH_ADD(hh, l_token_item->token_emissions, datum_token_emission_hash, + sizeof(dap_chain_hash_fast_t), l_emission_item); + } + + char* l_gdb_group = dap_ledger_get_gdb_group(l_ledger, DAP_LEDGER_STAKE_LOCK_STR); + dap_global_db_get_all(l_gdb_group, 0, dap_ledger_pvt_cache_gdb_load_stake_lock_callback, l_ledger); + DAP_DELETE(l_gdb_group); + return true; +} + + +/** + * @brief s_load_cache_gdb_loaded_callback + * @param a_global_db_context + * @param a_rc + * @param a_group + * @param a_key + * @param a_values_total + * @param a_values_shift + * @param a_values_count + * @param a_values + * @param a_arg + */ +bool dap_ledger_pvt_cache_gdb_load_tokens_callback(dap_global_db_instance_t *a_dbi, + int a_rc, const char *a_group, + const size_t a_values_total, const size_t a_values_count, + dap_global_db_obj_t *a_values, void *a_arg) +{ + dap_ledger_t *l_ledger = (dap_ledger_t *) a_arg; + dap_ledger_private_t *l_ledger_pvt = PVT(l_ledger); + if(a_rc) { + log_it(L_NOTICE, "No ledger cache found"); + pthread_mutex_lock(&l_ledger_pvt->load_mutex); + l_ledger_pvt->load_end = true; + pthread_cond_broadcast(&l_ledger_pvt->load_cond ); + pthread_mutex_unlock(&l_ledger_pvt->load_mutex); + + } + for (size_t i = 0; i < a_values_count; i++) { + if (a_values[i].value_len <= sizeof(uint256_t)) + continue; + dap_chain_datum_token_t *l_token = (dap_chain_datum_token_t *)(a_values[i].value + sizeof(uint256_t)); + size_t l_token_size = a_values[i].value_len - sizeof(uint256_t); + if (strcmp(l_token->ticker, a_values[i].key)) { + log_it(L_WARNING, "Corrupted token with ticker [%s], need to 'ledger reload' to update cache", a_values[i].key); + continue; + } + dap_ledger_token_add(l_ledger, (byte_t *)l_token, l_token_size); + dap_ledger_token_item_t *l_token_item = dap_ledger_pvt_find_token(l_ledger, l_token->ticker); + if (l_token_item) + l_token_item->current_supply = *(uint256_t*)a_values[i].value; + } + + char *l_gdb_group = dap_ledger_get_gdb_group(l_ledger, DAP_LEDGER_EMISSIONS_STR); + dap_global_db_get_all(l_gdb_group, 0, s_load_cache_gdb_loaded_emissions_callback, l_ledger); + DAP_DELETE(l_gdb_group); + return true; +} + +/** + * @brief s_token_tsd_parse + * + * @param a_ledger + * @param a_item_apply_to + * @param a_token + * @param a_token_size + * @return int + */ +static int s_token_tsd_parse(dap_ledger_token_item_t *a_item_apply_to, dap_chain_datum_token_t *a_current_datum, + dap_ledger_t *a_ledger, byte_t *a_tsd, size_t a_tsd_total_size, bool a_apply) +{ + if (!a_tsd_total_size) { + debug_if(a_item_apply_to, L_NOTICE, "No TSD sections in datum token"); + return DAP_LEDGER_CHECK_OK; + } + dap_return_val_if_pass(a_apply && !a_item_apply_to, DAP_LEDGER_CHECK_INVALID_ARGS); + size_t l_new_signs_valid = a_item_apply_to ? a_item_apply_to->auth_signs_valid : 0; + size_t l_new_signs_total = a_item_apply_to ? a_item_apply_to->auth_signs_total : 0; + dap_pkey_t **l_new_pkeys = NULL; + dap_hash_fast_t *l_new_pkey_hashes = NULL; + bool l_was_pkeys_copied = false; + size_t l_new_tx_recv_allow_size = a_item_apply_to ? a_item_apply_to->tx_recv_allow_size : 0; + size_t l_new_tx_recv_block_size = a_item_apply_to ? a_item_apply_to->tx_recv_block_size : 0; + size_t l_new_tx_send_allow_size = a_item_apply_to ? a_item_apply_to->tx_send_allow_size : 0; + size_t l_new_tx_send_block_size = a_item_apply_to ? a_item_apply_to->tx_send_block_size : 0; + dap_chain_addr_t *l_new_tx_recv_allow = NULL, *l_new_tx_recv_block = NULL, + *l_new_tx_send_allow = NULL, *l_new_tx_send_block = NULL; + bool l_was_tx_recv_allow_copied = false, l_was_tx_recv_block_copied = false, + l_was_tx_send_allow_copied = false, l_was_tx_send_block_copied = false; + +#define m_ret_cleanup(ret_code) ({ \ + DAP_DELETE_COUNT(l_new_pkeys, l_new_signs_total); \ + DAP_DEL_MULTY(l_new_tx_recv_allow, l_new_tx_recv_block, \ + l_new_tx_send_allow, l_new_tx_send_block, \ + l_new_pkeys, l_new_pkey_hashes); \ + ret_code; }) + uint64_t l_tsd_size = 0; + dap_tsd_t *l_tsd = (dap_tsd_t *)a_tsd; + for (uint64_t l_offset = 0; l_offset < a_tsd_total_size; l_offset += l_tsd_size) { + if (l_offset + sizeof(dap_tsd_t) > a_tsd_total_size || l_offset + sizeof(dap_tsd_t) < l_offset) { + log_it(L_WARNING, "Incorrect TSD section size, less than header"); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + l_tsd = (dap_tsd_t *)((byte_t *)l_tsd + l_tsd_size); + l_tsd_size = dap_tsd_size(l_tsd); + if (l_offset + l_tsd_size > a_tsd_total_size || l_offset + l_tsd_size < l_offset) { + log_it(L_WARNING, "Wrong TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + switch (l_tsd->type) { + // set flags + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_SET_FLAGS: { + if (l_tsd->size != sizeof(uint16_t)) { + log_it(L_WARNING, "Wrong SET_FLAGS TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + if (!a_apply) + break; + a_item_apply_to->flags |= dap_tsd_get_scalar(l_tsd, uint16_t); + } break; + + // unset flags + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_UNSET_FLAGS: { + if (l_tsd->size != sizeof(uint16_t)) { + log_it(L_WARNING, "Wrong UNSET_FLAGS TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + if (!a_apply) + break; + a_item_apply_to->flags &= ~dap_tsd_get_scalar(l_tsd, uint16_t); + } break; + + // set total supply + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SUPPLY: { // 256 + if (l_tsd->size != sizeof(uint256_t)) { + log_it(L_WARNING, "Wrong TOTAL_SUPPLY TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + if (!a_item_apply_to) { + log_it(L_WARNING, "Unexpected TOTAL_SUPPLY TSD section in datum token declaration"); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_FORBIDDEN); + } + uint256_t l_new_supply = dap_tsd_get_scalar(l_tsd, uint256_t); + if (IS_ZERO_256(a_item_apply_to->total_supply)){ + log_it(L_WARNING, "Cannot update total_supply for token %s because the current value is set to infinity.", a_item_apply_to->ticker); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_SUPPLY); + } + if (!IS_ZERO_256(l_new_supply) && compare256(a_item_apply_to->total_supply, l_new_supply) > -1) { + log_it(L_WARNING, "Can't update token with ticker '%s' because the new 'total_supply' can't be smaller than the old one", a_item_apply_to->ticker); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_SUPPLY); + } + if (!a_apply) + break; + a_item_apply_to->total_supply = l_new_supply; + } break; + + // Allowed tx receiver addres list add, remove or clear + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_ALLOWED_ADD: { + if (l_tsd->size != sizeof(dap_chain_addr_t)) { + log_it(L_WARNING, "Wrong TX_RECEIVER_ALLOWED_ADD TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + // Check if its correct + dap_chain_addr_t *l_add_addr = dap_tsd_get_object(l_tsd, dap_chain_addr_t); + if (dap_chain_addr_check_sum(l_add_addr)) { + log_it(L_WARNING, "Wrong address checksum in TSD param TX_RECEIVER_ALLOWED_ADD"); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_ADDR); + } + if (!l_new_tx_recv_allow && l_new_tx_recv_allow_size && !l_was_tx_recv_allow_copied) { + assert(a_item_apply_to->tx_recv_allow); + // Deep copy addrs to sandbox + l_new_tx_recv_allow = DAP_DUP_SIZE(a_item_apply_to->tx_recv_allow, l_new_tx_recv_allow_size * sizeof(dap_chain_addr_t)); + if (!l_new_tx_recv_allow) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + } + l_was_tx_recv_allow_copied = true; + // Check if its already present + for (size_t i = 0; i < l_new_tx_recv_allow_size; i++) { // Check for all the list + if (dap_chain_addr_compare(l_new_tx_recv_allow + i, l_add_addr)) { // Found + log_it(L_WARNING, "TSD param TX_RECEIVER_ALLOWED_ADD has address %s thats already present in list", + dap_chain_addr_to_str_static(l_add_addr)); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_ADDR_MISMATCH); + } + } + l_new_tx_recv_allow = l_new_tx_recv_allow + ? DAP_REALLOC(l_new_tx_recv_allow, (l_new_tx_recv_allow_size + 1) * sizeof(dap_chain_addr_t)) + : DAP_NEW_Z(dap_chain_addr_t); + if (!l_new_tx_recv_allow) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + l_new_tx_recv_allow[l_new_tx_recv_allow_size++] = *l_add_addr; + } break; + + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_ALLOWED_REMOVE: { + if (l_tsd->size != sizeof(dap_chain_addr_t)) { + log_it(L_WARNING, "Wrong TX_RECEIVER_ALLOWED_REMOVE TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + // Check if its correct + dap_chain_addr_t *l_add_addr = dap_tsd_get_object(l_tsd, dap_chain_addr_t); + if (dap_chain_addr_check_sum(l_add_addr)) { + log_it(L_WARNING, "Wrong address checksum in TSD param TX_RECEIVER_ALLOWED_REMOVE"); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_ADDR); + } + if (!l_new_tx_recv_allow && l_new_tx_recv_allow_size && !l_was_tx_recv_allow_copied) { + assert(a_item_apply_to->tx_recv_allow); + // Deep copy addrs to sandbox + l_new_tx_recv_allow = DAP_DUP_SIZE(a_item_apply_to->tx_recv_allow, l_new_tx_recv_allow_size * sizeof(dap_chain_addr_t)); + if (!l_new_tx_recv_allow) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + } + l_was_tx_recv_allow_copied = true; + // Check if its already present + size_t i = 0; + for ( ; i < l_new_tx_recv_allow_size; i++) // Check for all the list + if (dap_chain_addr_compare(l_new_tx_recv_allow + i, l_add_addr)) + break; + if (i == l_new_tx_recv_allow_size) { + log_it(L_WARNING, "TSD param TX_RECEIVER_ALLOWED_REMOVE has address %s thats not present in list", + dap_chain_addr_to_str_static(l_add_addr)); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_ADDR_MISMATCH); + } + // Addr removing + if (--l_new_tx_recv_allow_size > i) + memmove(l_new_tx_recv_allow + i, l_new_tx_recv_allow + i + 1, + (l_new_tx_recv_allow_size - i - 1) * sizeof(dap_chain_addr_t)); + // Memory clearing + if (l_new_tx_recv_allow_size) + l_new_tx_recv_allow = DAP_REALLOC(l_new_tx_recv_allow, + l_new_tx_recv_allow_size * sizeof(dap_chain_addr_t)); + else + DAP_DEL_Z(l_new_tx_recv_allow); + } break; + + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_ALLOWED_CLEAR: { + if (l_tsd->size != 0) { + log_it(L_WARNING, "Wrong TX_RECEIVER_ALLOWED_CLEAR TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + DAP_DEL_Z(l_new_tx_recv_allow); + l_new_tx_recv_allow_size = 0; + l_was_tx_recv_block_copied = true; + } break; + + // Blocked tx receiver addres list add, remove or clear + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_BLOCKED_ADD: { + if (l_tsd->size != sizeof(dap_chain_addr_t)) { + log_it(L_WARNING, "Wrong TX_RECEIVER_BLOCKED_ADD TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + // Check if its correct + dap_chain_addr_t *l_add_addr = dap_tsd_get_object(l_tsd, dap_chain_addr_t); + if (dap_chain_addr_check_sum(l_add_addr)) { + log_it(L_WARNING, "Wrong address checksum in TSD param TX_RECEIVER_BLOCKED_ADD"); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_ADDR); + } + if (!l_new_tx_recv_block && l_new_tx_recv_block_size && !l_was_tx_recv_block_copied) { + assert(a_item_apply_to->tx_recv_block); + // Deep copy addrs to sandbox + l_new_tx_recv_block = DAP_DUP_SIZE(a_item_apply_to->tx_recv_block, l_new_tx_recv_block_size * sizeof(dap_chain_addr_t)); + if (!l_new_tx_recv_block) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + } + l_was_tx_recv_block_copied = true; + // Check if its already present + for (size_t i = 0; i < l_new_tx_recv_block_size; i++) { // Check for all the list + if (dap_chain_addr_compare(l_new_tx_recv_block + i, l_add_addr)) { // Found + log_it(L_WARNING, "TSD param TX_RECEIVER_BLOCKED_ADD has address %s thats already present in list", + dap_chain_addr_to_str_static(l_add_addr)); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_ADDR_MISMATCH); + } + } + l_new_tx_recv_block = l_new_tx_recv_block + ? DAP_REALLOC(l_new_tx_recv_block, (l_new_tx_recv_block_size + 1) * sizeof(dap_chain_addr_t)) + : DAP_NEW_Z(dap_chain_addr_t); + if (!l_new_tx_recv_block) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + l_new_tx_recv_block[l_new_tx_recv_block_size++] = *l_add_addr; + } break; + + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_BLOCKED_REMOVE: { + if (l_tsd->size != sizeof(dap_chain_addr_t)) { + log_it(L_WARNING, "Wrong TX_RECEIVER_BLOCKED_REMOVE TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + // Check if its correct + dap_chain_addr_t *l_add_addr = dap_tsd_get_object(l_tsd, dap_chain_addr_t); + if (dap_chain_addr_check_sum(l_add_addr)) { + log_it(L_WARNING, "Wrong address checksum in TSD param TX_RECEIVER_BLOCKED_REMOVE"); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_ADDR); + } + if (!l_new_tx_recv_block && l_new_tx_recv_block_size && !l_was_tx_recv_block_copied) { + assert(a_item_apply_to->tx_recv_block); + // Deep copy addrs to sandbox + l_new_tx_recv_block = DAP_DUP_SIZE(a_item_apply_to->tx_recv_block, l_new_tx_recv_block_size * sizeof(dap_chain_addr_t)); + if (!l_new_tx_recv_block) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + } + l_was_tx_recv_block_copied = true; + // Check if its already present + size_t i = 0; + for ( ; i < l_new_tx_recv_block_size; i++) // Check for all the list + if (dap_chain_addr_compare(l_new_tx_recv_block + i, l_add_addr)) + break; + if (i == l_new_tx_recv_block_size) { + log_it(L_WARNING, "TSD param TX_RECEIVER_BLOCKED_REMOVE has address %s thats not present in list", + dap_chain_addr_to_str_static(l_add_addr)); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_ADDR_MISMATCH); + } + // Addr removing + if (--l_new_tx_recv_block_size > i) + memmove(l_new_tx_recv_block + i, l_new_tx_recv_block + i + 1, + (l_new_tx_recv_block_size - i - 1) * sizeof(dap_chain_addr_t)); + // Memory clearing + if (l_new_tx_recv_block_size) + l_new_tx_recv_block = DAP_REALLOC(l_new_tx_recv_block, + l_new_tx_recv_block_size * sizeof(dap_chain_addr_t)); + else + DAP_DEL_Z(l_new_tx_recv_block); + } break; + + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_BLOCKED_CLEAR: { + if (l_tsd->size != 0) { + log_it(L_WARNING, "Wrong TX_RECEIVER_BLOCKED_CLEAR TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + DAP_DEL_Z(l_new_tx_recv_block); + l_new_tx_recv_block_size = 0; + l_was_tx_recv_block_copied = true; + } break; + + // Blocked tx sender addres list add, remove or clear + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_ALLOWED_ADD: { + if (l_tsd->size != sizeof(dap_chain_addr_t)) { + log_it(L_WARNING, "Wrong TX_SENDER_ALLOWED_ADD TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + // Check if its correct + dap_chain_addr_t *l_add_addr = dap_tsd_get_object(l_tsd, dap_chain_addr_t); + if (dap_chain_addr_check_sum(l_add_addr)) { + log_it(L_WARNING, "Wrong address checksum in TSD param TX_SENDER_ALLOWED_ADD"); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_ADDR); + } + if (!l_new_tx_send_allow && l_new_tx_send_allow_size && !l_was_tx_send_allow_copied) { + assert(a_item_apply_to->tx_send_allow); + // Deep copy addrs to sandbox + l_new_tx_send_allow = DAP_DUP_SIZE(a_item_apply_to->tx_send_allow, l_new_tx_send_allow_size * sizeof(dap_chain_addr_t)); + if (!l_new_tx_send_allow) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + } + l_was_tx_send_allow_copied = true; + // Check if its already present + for (size_t i = 0; i < l_new_tx_send_allow_size; i++) { // Check for all the list + if (dap_chain_addr_compare(l_new_tx_send_allow + i, l_add_addr)) { // Found + log_it(L_WARNING, "TSD param TX_SENDER_ALLOWED_ADD has address %s thats already present in list", + dap_chain_addr_to_str_static(l_add_addr)); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_ADDR_MISMATCH); + } + } + l_new_tx_send_allow = l_new_tx_send_allow + ? DAP_REALLOC(l_new_tx_send_allow, (l_new_tx_send_allow_size + 1) * sizeof(dap_chain_addr_t)) + : DAP_NEW_Z(dap_chain_addr_t); + if (!l_new_tx_send_allow) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + l_new_tx_send_allow[l_new_tx_send_allow_size++] = *l_add_addr; + } break; + + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_ALLOWED_REMOVE: { + if (l_tsd->size != sizeof(dap_chain_addr_t)) { + log_it(L_WARNING, "Wrong TX_SENDER_ALLOWED_REMOVE TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + // Check if its correct + dap_chain_addr_t *l_add_addr = dap_tsd_get_object(l_tsd, dap_chain_addr_t); + if (dap_chain_addr_check_sum(l_add_addr)) { + log_it(L_WARNING, "Wrong address checksum in TSD param TX_SENDER_ALLOWED_REMOVE"); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_ADDR); + + } + if (!l_new_tx_send_allow && l_new_tx_send_allow_size && !l_was_tx_send_allow_copied) { + assert(a_item_apply_to->tx_send_allow); + // Deep copy addrs to sandbox + l_new_tx_send_allow = DAP_DUP_SIZE(a_item_apply_to->tx_send_allow, l_new_tx_send_allow_size * sizeof(dap_chain_addr_t)); + if (!l_new_tx_send_allow) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + } + l_was_tx_send_allow_copied = true; + // Check if its already present + size_t i = 0; + for ( ; i < l_new_tx_send_allow_size; i++) // Check for all the list + if (dap_chain_addr_compare(l_new_tx_send_allow + i, l_add_addr)) + break; + if (i == l_new_tx_send_allow_size) { + log_it(L_WARNING, "TSD param TX_SENDER_ALLOWED_REMOVE has address %s thats not present in list", + dap_chain_addr_to_str_static(l_add_addr)); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_ADDR_MISMATCH); + } + // Addr removing + if (--l_new_tx_send_allow_size > i) + memmove(l_new_tx_send_allow + i, l_new_tx_send_allow + i + 1, + (l_new_tx_send_allow_size - i - 1) * sizeof(dap_chain_addr_t)); + // Memory clearing + if (l_new_tx_send_allow_size) + l_new_tx_send_allow = DAP_REALLOC(l_new_tx_send_allow, + l_new_tx_send_allow_size * sizeof(dap_chain_addr_t)); + else + DAP_DEL_Z(l_new_tx_send_allow); + } break; + + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_ALLOWED_CLEAR: { + if (l_tsd->size != 0) { + log_it(L_WARNING, "Wrong TX_SENDER_ALLOWED_CLEAR TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + DAP_DEL_Z(l_new_tx_send_allow); + l_new_tx_send_allow_size = 0; + l_was_tx_send_allow_copied = true; + } break; + + // Blocked tx sender addres list add, remove or clear + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_BLOCKED_ADD: { + if (l_tsd->size != sizeof(dap_chain_addr_t)) { + log_it(L_WARNING, "Wrong TX_SENDER_BLOCKED_ADD TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + // Check if its correct + dap_chain_addr_t *l_add_addr = dap_tsd_get_object(l_tsd, dap_chain_addr_t); + if (dap_chain_addr_check_sum(l_add_addr)) { + log_it(L_WARNING, "Wrong address checksum in TSD param TX_SENDER_BLOCKED_ADD"); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_ADDR); + } + if (!l_new_tx_send_block && l_new_tx_send_block_size && !l_was_tx_send_block_copied) { + assert(a_item_apply_to->tx_send_block); + // Deep copy addrs to sandbox + l_new_tx_send_block = DAP_DUP_SIZE(a_item_apply_to->tx_send_block, l_new_tx_send_block_size * sizeof(dap_chain_addr_t)); + if (!l_new_tx_send_block) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + } + l_was_tx_send_block_copied = true; + // Check if its already present + for (size_t i = 0; i < l_new_tx_send_block_size; i++) { // Check for all the list + if (dap_chain_addr_compare(l_new_tx_send_block + i, l_add_addr)) { // Found + log_it(L_WARNING, "TSD param TX_SENDER_BLOCKED_ADD has address %s thats already present in list", + dap_chain_addr_to_str_static(l_add_addr)); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_ADDR_MISMATCH); + } + } + if (!a_apply) + break; + l_new_tx_send_block = l_new_tx_send_block + ? DAP_REALLOC(l_new_tx_send_block, (l_new_tx_send_block_size + 1) * sizeof(dap_chain_addr_t)) + : DAP_NEW_Z(dap_chain_addr_t); + if (!l_new_tx_send_block) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + l_new_tx_send_block[l_new_tx_send_block_size++] = *l_add_addr; + } break; + + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_BLOCKED_REMOVE: { + if (l_tsd->size != sizeof(dap_chain_addr_t)) { + log_it(L_WARNING, "Wrong TX_SENDER_BLOCKED_REMOVE TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + // Check if its correct + dap_chain_addr_t *l_add_addr = dap_tsd_get_object(l_tsd, dap_chain_addr_t); + if (dap_chain_addr_check_sum(l_add_addr)) { + log_it(L_WARNING, "Wrong address checksum in TSD param TX_SENDER_BLOCKED_REMOVE"); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_INVALID_ADDR); + } + if (!l_new_tx_send_block && l_new_tx_send_block_size && !l_was_tx_send_block_copied) { + assert(a_item_apply_to->tx_send_block); + // Deep copy addrs to sandbox + l_new_tx_send_block = DAP_DUP_SIZE(a_item_apply_to->tx_send_block, l_new_tx_send_block_size * sizeof(dap_chain_addr_t)); + if (!l_new_tx_send_block) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + } + l_was_tx_send_block_copied = true; + // Check if its already present + size_t i = 0; + for ( ; i < l_new_tx_send_block_size; i++) // Check for all the list + if (dap_chain_addr_compare(l_new_tx_send_block + i, l_add_addr)) + break; + if (i == l_new_tx_send_block_size) { + log_it(L_WARNING, "TSD param TX_SENDER_BLOCKED_REMOVE has address %s thats not present in list", + dap_chain_addr_to_str_static(l_add_addr)); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_ADDR_MISMATCH); + } + // Addr removing + if (--l_new_tx_send_block_size > i) + memmove(l_new_tx_send_block + i, l_new_tx_send_block + i + 1, + (l_new_tx_send_block_size - i - 1) * sizeof(dap_chain_addr_t)); + // Memory clearing + if (l_new_tx_send_block_size) + l_new_tx_send_block = DAP_REALLOC(l_new_tx_send_block, + l_new_tx_send_block_size * sizeof(dap_chain_addr_t)); + else + DAP_DEL_Z(l_new_tx_send_block); + } break; + + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_BLOCKED_CLEAR: { + if (l_tsd->size != 0) { + log_it(L_WARNING, "Wrong TX_SENDER_BLOCKED_CLEAR TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + DAP_DEL_Z(l_new_tx_send_block); + l_new_tx_send_block_size = 0; + l_was_tx_send_block_copied = true; + } break; + + case DAP_CHAIN_DATUM_TOKEN_TSD_TOKEN_DESCRIPTION: { + if (l_tsd->size == 0 || l_tsd->data[l_tsd->size - 1] != 0) { + log_it(L_ERROR, "Wrong TOKEN_DESCRIPTION TSD format or size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + if (!a_apply) + break; + DAP_DEL_Z(a_item_apply_to->description); + a_item_apply_to->description = strdup((char *)l_tsd->data); + } break; + + // Set signs count value need to emission be valid + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_VALID: { + if (l_tsd->size != sizeof(uint16_t)) { + log_it(L_WARNING, "Wrong SIGNS_VALID TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + l_new_signs_valid = dap_tsd_get_scalar(l_tsd, uint16_t); + } break; + + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_ADD: { + if (l_tsd->size < sizeof(dap_pkey_t) || l_tsd->size != dap_pkey_get_size((dap_pkey_t *)l_tsd->data)) { + log_it(L_WARNING, "Wrong TOTAL_PKEYS_ADD TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + if (!l_new_pkeys && l_new_signs_total && !l_was_pkeys_copied) { + assert(a_item_apply_to->auth_pkeys); + assert(a_item_apply_to->auth_pkey_hashes); + // Deep copy pkeys & its hashes to sandbox + l_new_pkeys = DAP_NEW_SIZE(dap_pkey_t *, l_new_signs_total * sizeof(dap_pkey_t *)); + if (!l_new_pkeys) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + for (size_t i = 0; i < l_new_signs_total; i++) { + l_new_pkeys[i] = DAP_DUP_SIZE(a_item_apply_to->auth_pkeys[i], dap_pkey_get_size(a_item_apply_to->auth_pkeys[i])); + if (!l_new_pkeys[i]) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + } + assert(!l_new_pkey_hashes); + l_new_pkey_hashes = DAP_DUP_SIZE(a_item_apply_to->auth_pkey_hashes, l_new_signs_total * sizeof(dap_hash_t)); + if (!l_new_pkey_hashes) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + } + l_was_pkeys_copied = true; + dap_pkey_t *l_new_auth_pkey = dap_tsd_get_object(l_tsd, dap_pkey_t); + dap_pkey_type_t l_pkey_type_correction = { .type = DAP_PKEY_TYPE_NULL }; + if (dap_pkey_type_to_enc_key_type(l_new_auth_pkey->header.type) == DAP_ENC_KEY_TYPE_INVALID) { + dap_sign_type_t l_sign_type = { .type = l_new_auth_pkey->header.type.type }; // Legacy cratch + l_pkey_type_correction = dap_pkey_type_from_sign_type(l_sign_type); + if (l_pkey_type_correction.type == DAP_PKEY_TYPE_NULL) { + log_it(L_WARNING, "Unknonw public key type %hu", l_new_auth_pkey->header.type.type); + return m_ret_cleanup(DAP_LEDGER_CHECK_PARSE_ERROR); + } + } + // Check if its already present + dap_hash_t l_new_auth_pkey_hash; + dap_pkey_get_hash(l_new_auth_pkey, &l_new_auth_pkey_hash); + for (size_t i = 0; i < l_new_signs_total; i++) { + if (dap_pkey_compare(l_new_auth_pkey, l_new_pkeys[i])) { + log_it(L_WARNING, "TSD param TOTAL_PKEYS_ADD has pkey %s thats already present in list", + dap_hash_fast_to_str_static(&l_new_auth_pkey_hash)); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_PKEY_MISMATCH); + } + } + l_new_pkeys = l_new_pkeys ? DAP_REALLOC(l_new_pkeys, (l_new_signs_total + 1) * sizeof(dap_pkey_t *)) + : DAP_NEW_Z(dap_pkey_t *); + if (!l_new_pkeys) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + // Pkey adding + l_new_pkeys[l_new_signs_total] = DAP_DUP_SIZE(l_new_auth_pkey, dap_pkey_get_size(l_new_auth_pkey)); + if (!l_new_pkeys[l_new_signs_total]) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + if (l_pkey_type_correction.type != DAP_PKEY_TYPE_NULL) + l_new_pkeys[l_new_signs_total]->header.type = l_pkey_type_correction; + + l_new_pkey_hashes = l_new_pkey_hashes ? DAP_REALLOC(l_new_pkey_hashes, (l_new_signs_total + 1) * sizeof(dap_hash_t)) + : DAP_NEW_Z(dap_hash_t); + if (!l_new_pkey_hashes) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + l_new_pkey_hashes[l_new_signs_total++] = l_new_auth_pkey_hash; + } break; + + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_REMOVE: { + if (l_tsd->size != sizeof(dap_hash_t)) { + log_it(L_WARNING, "Wrong TOTAL_PKEYS_REMOVE TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + if (!l_new_pkeys && l_new_signs_total && !l_was_pkeys_copied) { + assert(a_item_apply_to->auth_pkeys); + assert(a_item_apply_to->auth_pkey_hashes); + // Deep copy pkeys & its hashes to sandbox + l_new_pkeys = DAP_NEW_SIZE(dap_pkey_t *, l_new_signs_total * sizeof(dap_pkey_t *)); + if (!l_new_pkeys) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + for (size_t i = 0; i < l_new_signs_total; i++) { + l_new_pkeys[i] = DAP_DUP_SIZE(a_item_apply_to->auth_pkeys[i], dap_pkey_get_size(a_item_apply_to->auth_pkeys[i])); + if (!l_new_pkeys[i]) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + } + assert(!l_new_pkey_hashes); + l_new_pkey_hashes = DAP_DUP_SIZE(a_item_apply_to->auth_pkey_hashes, l_new_signs_total * sizeof(dap_hash_t)); + if (!l_new_pkey_hashes) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + } + } + l_was_pkeys_copied = true; + dap_hash_t l_new_auth_pkey_hash = dap_tsd_get_scalar(l_tsd, dap_hash_t); + // Check if its already present + size_t i = 0; + for ( ; i < l_new_signs_total; i++) // Check for all the list + if (dap_hash_fast_compare(l_new_pkey_hashes + i, &l_new_auth_pkey_hash)) + break; + if (i == l_new_signs_total) { + log_it(L_WARNING, "TSD param TOTAL_PKEYS_REMOVE has public key hash %s thats not present in list", + dap_hash_fast_to_str_static(&l_new_auth_pkey_hash)); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_PKEY_MISMATCH); + } + // Pkey removing + DAP_DELETE(l_new_pkeys[i]); + if (--l_new_signs_total > i) { + memmove(l_new_pkeys + i, l_new_pkeys + i + 1, (l_new_signs_total - i - 1) * sizeof(dap_pkey_t *)); + memmove(l_new_pkey_hashes + i, l_new_pkey_hashes + i + 1, (l_new_signs_total - i - 1) * sizeof(dap_hash_t)); + } + // Memory clearing + if (l_new_signs_total) { + l_new_pkeys = DAP_REALLOC(l_new_pkeys, l_new_signs_total * sizeof(dap_pkey_t *)); + l_new_pkey_hashes = DAP_REALLOC(l_new_pkey_hashes, l_new_signs_total * sizeof(dap_hash_t)); + } else + DAP_DEL_MULTY(l_new_pkeys, l_new_pkey_hashes); + } break; + + case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DELEGATE_EMISSION_FROM_STAKE_LOCK: { + if (a_current_datum->subtype != DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE) { + log_it(L_WARNING, "TSD section DELEGATE_EMISSION_FROM_STAKE_LOCK allowed for NATIVE subtype only"); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_FORBIDDEN); + } + if (l_tsd->size != sizeof(dap_chain_datum_token_tsd_delegate_from_stake_lock_t) && + l_tsd->size != sizeof(dap_chain_datum_token_tsd_delegate_from_stake_lock_t) + 256 /* Legacy size */) { + log_it(L_WARNING, "Wrong DELEGATE_EMISSION_FROM_STAKE_LOCK TSD size %zu, exiting TSD parse", l_tsd_size); + return m_ret_cleanup(DAP_LEDGER_CHECK_INVALID_SIZE); + } + dap_chain_datum_token_tsd_delegate_from_stake_lock_t *l_delegate = dap_tsd_get_object(l_tsd, dap_chain_datum_token_tsd_delegate_from_stake_lock_t); + const char *l_basic_token_ticker = (const char *)l_delegate->ticker_token_from; + char l_delegated_ticker[DAP_CHAIN_TICKER_SIZE_MAX]; + dap_chain_datum_token_get_delegated_ticker(l_delegated_ticker, l_basic_token_ticker); + if (dap_strcmp(l_delegated_ticker, a_current_datum->ticker)) { + log_it(L_WARNING, "Unexpected delegated token ticker %s (expected %s)", a_current_datum->ticker, l_delegated_ticker); + return m_ret_cleanup(DAP_LEDGER_TOKEN_ADD_CHECK_TSD_OTHER_TICKER_EXPECTED); + } + dap_ledger_token_item_t *l_basic_token = NULL; + HASH_FIND_STR(PVT(a_ledger)->tokens, l_basic_token_ticker, l_basic_token); + if (!l_basic_token) { + log_it(L_WARNING, "Basic token ticker %s for delegated token isn't found", l_basic_token_ticker); + return m_ret_cleanup(DAP_LEDGER_CHECK_TICKER_NOT_FOUND); + } + if (IS_ZERO_256(l_delegate->emission_rate)) { + log_it(L_WARNING, "Emission rate for delegated toke should not be a zero"); + return m_ret_cleanup(DAP_LEDGER_CHECK_ZERO_VALUE); + } + if (!a_apply) + break; + assert(a_item_apply_to); + a_item_apply_to->is_delegated = true; + strcpy(a_item_apply_to->delegated_from, l_basic_token->ticker); + a_item_apply_to->emission_rate = l_delegate->emission_rate; + } break; + + default: + log_it(L_ERROR, "Unexpected TSD type %hu", l_tsd->type); + return m_ret_cleanup(DAP_LEDGER_CHECK_PARSE_ERROR); + } + } + if (l_new_signs_total < l_new_signs_valid) + return m_ret_cleanup(DAP_LEDGER_CHECK_NOT_ENOUGH_VALID_SIGNS); + + if (!a_apply) + return m_ret_cleanup(DAP_LEDGER_CHECK_OK); +#undef m_ret_cleanup + + if (l_was_tx_recv_allow_copied) { + a_item_apply_to->tx_recv_allow_size = l_new_tx_recv_allow_size; + DAP_DEL_Z(a_item_apply_to->tx_recv_allow); + a_item_apply_to->tx_recv_allow = l_new_tx_recv_allow; + } + if (l_was_tx_recv_block_copied) { + a_item_apply_to->tx_recv_block_size = l_new_tx_recv_block_size; + DAP_DEL_Z(a_item_apply_to->tx_recv_block); + a_item_apply_to->tx_recv_block = l_new_tx_recv_block; + } + if (l_was_tx_send_allow_copied) { + a_item_apply_to->tx_send_allow_size = l_new_tx_send_allow_size; + DAP_DEL_Z(a_item_apply_to->tx_send_allow); + a_item_apply_to->tx_send_allow = l_new_tx_send_allow; + } + if (l_was_tx_send_block_copied) { + a_item_apply_to->tx_send_block_size = l_new_tx_send_block_size; + DAP_DEL_Z(a_item_apply_to->tx_send_block); + a_item_apply_to->tx_send_block = l_new_tx_send_block; + } + a_item_apply_to->auth_signs_valid = l_new_signs_valid; + if (l_was_pkeys_copied) { + for (size_t i = 0; i < a_item_apply_to->auth_signs_total; i++) + DAP_DELETE(a_item_apply_to->auth_pkeys[i]); + DAP_DEL_Z(a_item_apply_to->auth_pkeys); + DAP_DEL_Z(a_item_apply_to->auth_pkey_hashes); + a_item_apply_to->auth_signs_total = l_new_signs_total; + a_item_apply_to->auth_pkeys = l_new_pkeys; + a_item_apply_to->auth_pkey_hashes = l_new_pkey_hashes; + } + return DAP_LEDGER_CHECK_OK; +} + +/** + * @brief dap_ledger_token_check + * @param a_ledger + * @param a_token + * @param a_token_size + * @return + */ +int s_token_add_check(dap_ledger_t *a_ledger, byte_t *a_token, size_t a_token_size, + dap_ledger_token_item_t **a_token_item, dap_chain_datum_token_t **a_token_out, + size_t *a_tsd_total_size, size_t *a_signs_size, + dap_hash_fast_t *a_token_update_hash) +{ + size_t l_token_size = a_token_size; + dap_chain_datum_token_t *l_token = dap_chain_datum_token_read(a_token, &l_token_size); + if (!l_token) + return DAP_LEDGER_CHECK_INVALID_SIZE; + if (!dap_chain_datum_token_check_ticker(l_token->ticker)) { + log_it(L_WARNING, "Token ticker %*s isn't a valid one", DAP_CHAIN_TICKER_SIZE_MAX, l_token->ticker); + DAP_DELETE(l_token); + return DAP_LEDGER_CHECK_INVALID_TICKER; + } + bool l_legacy_type = a_token_size != l_token_size; + if (l_legacy_type && !a_token_item) { // It's mempool check + log_it(L_WARNING, "Legacy token type %hu isn't supported for a new declaration", l_token->type); + DAP_DELETE(l_token); + return DAP_LEDGER_TOKEN_ADD_CHECK_LEGACY_FORBIDDEN; + } + if (l_token->type != DAP_CHAIN_DATUM_TOKEN_TYPE_UPDATE && l_token->type != DAP_CHAIN_DATUM_TOKEN_TYPE_DECL) { + log_it(L_WARNING, "Unknown token type %hu", l_token->type); + DAP_DELETE(l_token); + return DAP_LEDGER_CHECK_PARSE_ERROR; + } + if (!l_token->ticker[0] || l_token->ticker[DAP_CHAIN_TICKER_SIZE_MAX - 1]) { + log_it(L_WARNING, "Unreadable token ticker"); + DAP_DELETE(l_token); + return DAP_LEDGER_CHECK_PARSE_ERROR; + } + char *ptr = l_token->ticker; + while (*ptr) { + if (!dap_ascii_isalnum(*ptr++)) { + log_it(L_WARNING, "Token ticker is not alpha-numeric"); + DAP_DELETE(l_token); + return DAP_LEDGER_CHECK_PARSE_ERROR; + } + } + if (!l_token->signs_total) { + log_it(L_WARNING, "No auth signs in token '%s' datum!", l_token->ticker); + DAP_DELETE(l_token); + return DAP_LEDGER_TOKEN_ADD_CHECK_NOT_ENOUGH_UNIQUE_SIGNS; + } + bool l_update_token = l_token->type == DAP_CHAIN_DATUM_TOKEN_TYPE_UPDATE; + dap_ledger_token_item_t *l_token_item = dap_ledger_pvt_find_token(a_ledger, l_token->ticker); + dap_hash_fast_t l_token_update_hash = {}; + if (l_token_item) { + if (!l_update_token) { + log_it(L_WARNING, "Duplicate token declaration for ticker '%s'", l_token->ticker); + DAP_DELETE(l_token); + return DAP_LEDGER_CHECK_ALREADY_CACHED; + } + if (l_token->signs_total < l_token_item->auth_signs_valid) { + log_it(L_WARNING, "Datum token for ticker '%s' has only %hu signatures out of %zu", + l_token->ticker, l_token->signs_total, l_token_item->auth_signs_valid); + DAP_DELETE(l_token); + return DAP_LEDGER_TOKEN_ADD_CHECK_NOT_ENOUGH_UNIQUE_SIGNS; + } + dap_hash_fast(l_token, l_token_size, &l_token_update_hash); + dap_ledger_token_update_item_t *l_token_update_item = NULL; + pthread_rwlock_rdlock(&l_token_item->token_ts_updated_rwlock); + HASH_FIND(hh, l_token_item->token_ts_updated, &l_token_update_hash, sizeof(dap_hash_fast_t), l_token_update_item); + pthread_rwlock_unlock(&l_token_item->token_ts_updated_rwlock); + if (l_token_update_item) { + log_it(L_WARNING, "This update for token '%s' was already applied", l_token->ticker); + DAP_DELETE(l_token); + return DAP_LEDGER_CHECK_ALREADY_CACHED; + } + if (a_token_update_hash) + *a_token_update_hash = l_token_update_hash; + } else if (l_update_token) { + log_it(L_WARNING, "Can't update token that doesn't exist for ticker '%s'", l_token->ticker); + DAP_DELETE(l_token); + return DAP_LEDGER_CHECK_TICKER_NOT_FOUND; + } else if (l_token->signs_total < l_token->signs_valid) { + log_it(L_WARNING, "Datum token for ticker '%s' has only %hu signatures out of %hu", + l_token->ticker, l_token->signs_total, l_token->signs_valid); + DAP_DELETE(l_token); + return DAP_LEDGER_TOKEN_ADD_CHECK_NOT_ENOUGH_UNIQUE_SIGNS; + } + // Check TSD + size_t l_size_tsd_section = 0; + if (l_update_token) { + switch (l_token->subtype) { + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PRIVATE: + l_size_tsd_section = l_token->header_private_decl.tsd_total_size; break; + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE: + l_size_tsd_section = l_token->header_native_decl.tsd_total_size; break; + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_SIMPLE: + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PUBLIC: + break; + default: + /* Bogdanoff, unknown token subtype update. What shall we TODO? */ + log_it(L_WARNING, "Unknown token subtype '0x%0hX' update! Ticker: %s, total_supply: %s, signs_valid: %hu, signs_total: %hu", + l_token->type, l_token->ticker, dap_uint256_to_char(l_token->total_supply, NULL), + l_token->signs_valid, l_token->signs_total); + /* Dump it right now */ + DAP_DELETE(l_token); + return DAP_LEDGER_CHECK_PARSE_ERROR; + } + } else { + switch (l_token->subtype) { + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PRIVATE: + l_size_tsd_section = l_token->header_private_update.tsd_total_size; break; + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE: + l_size_tsd_section = l_token->header_native_update.tsd_total_size; break; + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_SIMPLE: + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PUBLIC: + break; + default: + /* Bogdanoff, unknown token subtype declaration. What shall we TODO? */ + log_it(L_WARNING, "Unknown token subtype '0x%0hX' declaration! Ticker: %s, total_supply: %s, signs_valid: %hu, signs_total: %hu", + l_token->type, l_token->ticker, dap_uint256_to_char(l_token->total_supply, NULL), + l_token->signs_valid, l_token->signs_total); + /* Dump it right now */ + DAP_DELETE(l_token); + return DAP_LEDGER_CHECK_PARSE_ERROR; + } + } + if (sizeof(dap_chain_datum_token_t) + l_size_tsd_section > l_token_size || + sizeof(dap_chain_datum_token_t) + l_size_tsd_section < l_size_tsd_section) { + log_it(L_WARNING, "Incorrect size %zu of datum token, expected at least %zu", l_token_size, + sizeof(dap_chain_datum_token_t) + l_size_tsd_section); + DAP_DELETE(l_token); + return DAP_LEDGER_CHECK_INVALID_SIZE; + } + // Check signs + byte_t *l_signs_ptr = l_token->tsd_n_signs + l_size_tsd_section; + uint64_t l_signs_size = 0, l_signs_offset = sizeof(dap_chain_datum_token_t) + l_size_tsd_section; + for (uint16_t l_signs_passed = 0; l_signs_passed < l_token->signs_total; l_signs_passed++) { + dap_sign_t *l_sign = (dap_sign_t *)(l_signs_ptr + l_signs_size); + if (l_signs_offset + l_signs_size + sizeof(dap_sign_t) > l_token_size || + l_signs_offset + l_signs_size + sizeof(dap_sign_t) < l_signs_offset) { + log_it(L_WARNING, "Incorrect size %zu of datum token, expected at least %zu", l_token_size, + l_signs_offset + l_signs_size + sizeof(dap_sign_t)); + DAP_DELETE(l_token); + return DAP_LEDGER_CHECK_INVALID_SIZE; + } + uint64_t l_sign_size = dap_sign_get_size(l_sign); + if (!l_sign_size || l_sign_size + l_signs_size < l_signs_size) { + log_it(L_WARNING, "Incorrect size %zu of datum token sign", l_sign_size); + DAP_DELETE(l_token); + return DAP_LEDGER_CHECK_INVALID_SIZE; + } + l_signs_size += l_sign_size; + } + if (l_token_size != l_signs_offset + l_signs_size) { + log_it(L_WARNING, "Incorrect size %zu of datum token, expected %zu", l_token_size, l_signs_offset + l_signs_size); + DAP_DELETE(l_token); + return DAP_LEDGER_CHECK_INVALID_SIZE; + } + size_t l_signs_unique = l_token->signs_total; + dap_sign_t **l_signs = dap_sign_get_unique_signs(l_signs_ptr, l_signs_size, &l_signs_unique); + if (l_signs_unique != l_token->signs_total) { + DAP_DEL_Z(l_signs); + log_it(L_WARNING, "The number of unique token signs %zu is less than total token signs set to %hu", + l_signs_unique, l_token->signs_total); + DAP_DELETE(l_token); + return DAP_LEDGER_TOKEN_ADD_CHECK_NOT_ENOUGH_UNIQUE_SIGNS; + } + size_t l_signs_approve = 0; + size_t l_verify_size = 0; + uint16_t l_tmp_auth_signs = 0; + if (l_legacy_type) + l_verify_size = sizeof(dap_chain_datum_token_old_t) - sizeof(uint16_t); + else { + l_verify_size = l_signs_offset; + l_tmp_auth_signs = l_token->signs_total; + l_token->signs_total = 0; + } + for (size_t i = 0; i < l_signs_unique; i++) { + if (!dap_sign_verify(l_signs[i], l_legacy_type ? a_token : (void *)l_token, l_verify_size)) { + if (l_update_token) { + for (size_t j = 0; j < l_token_item->auth_signs_total; j++) { + if (dap_pkey_compare_with_sign(l_token_item->auth_pkeys[j], l_signs[i])) { + l_signs_approve++; + break; + } + } + } else + l_signs_approve++; + } + } + DAP_DELETE(l_signs); + if (!l_legacy_type) + l_token->signs_total = l_tmp_auth_signs; + size_t l_signs_need = l_update_token ? l_token_item->auth_signs_valid : l_token->signs_total; + if (l_signs_approve < l_signs_need) { + log_it(L_WARNING, "Datum token for ticker '%s' has only %zu valid signatures out of %zu", + l_token->ticker, l_signs_approve, l_signs_need); + DAP_DELETE(l_token); + return DAP_LEDGER_CHECK_NOT_ENOUGH_VALID_SIGNS; + } + // Check content & size of enclosed TSD sections + pthread_rwlock_rdlock(&PVT(a_ledger)->tokens_rwlock); + int ret = s_token_tsd_parse(l_token_item, l_token, a_ledger, l_token->tsd_n_signs, l_size_tsd_section, false); + pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); + dap_ledger_hal_item_t *l_hash_found = NULL; + if (ret != DAP_LEDGER_CHECK_OK) { + if (PVT(a_ledger)->hal_items) { + dap_hash_fast_t l_token_hash; + if (!dap_hash_fast_is_blank(&l_token_update_hash)) + l_token_hash = l_token_update_hash; + else + dap_hash_fast(a_token, a_token_size, &l_token_hash); + l_hash_found = s_check_hal(a_ledger, &l_token_hash); + } + if (!l_hash_found) { + DAP_DELETE(l_token); + return ret; + } + } + if (a_token_item) + *a_token_item = l_token_item; + if (a_token_out) + *a_token_out = l_token; + else + DAP_DELETE(l_token); + if (a_tsd_total_size) + *a_tsd_total_size = l_size_tsd_section; + if (a_signs_size) + *a_signs_size = l_signs_size; + return l_hash_found ? DAP_LEDGER_CHECK_WHITELISTED : DAP_LEDGER_CHECK_OK; +} + +int dap_ledger_token_add_check(dap_ledger_t *a_ledger, byte_t *a_token, size_t a_token_size) +{ + dap_return_val_if_fail(a_ledger && a_token && a_token_size, DAP_LEDGER_CHECK_INVALID_ARGS); + int ret = s_token_add_check(a_ledger, a_token, a_token_size, NULL, NULL, NULL, NULL, NULL); + if (ret == DAP_LEDGER_CHECK_WHITELISTED) + ret = DAP_LEDGER_CHECK_OK; + return ret; +} + +/** + * @brief dap_ledger_token_ticker_check + * @param a_ledger + * @param a_token_ticker + * @return + */ +dap_chain_datum_token_t *dap_ledger_token_ticker_check(dap_ledger_t *a_ledger, const char *a_token_ticker) +{ + dap_return_val_if_fail(a_ledger && a_token_ticker, NULL); + dap_ledger_token_item_t *l_token_item = dap_ledger_pvt_find_token(a_ledger, a_token_ticker); + return l_token_item ? l_token_item->datum_token : NULL; +} + +/** + * @brief update current_supply in token cache + * + * @param a_ledger ledger object + * @param l_token_item token item object + */ +void s_ledger_token_cache_update(dap_ledger_t *a_ledger, dap_ledger_token_item_t *l_token_item) +{ + if (!PVT(a_ledger)->cached) + return; + char *l_gdb_group = dap_ledger_get_gdb_group(a_ledger, DAP_LEDGER_TOKENS_STR); + size_t l_cache_size = l_token_item->datum_token_size + sizeof(uint256_t); + uint8_t *l_cache = DAP_NEW_STACK_SIZE(uint8_t, l_cache_size); + if ( !l_cache ) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return; + } + memcpy(l_cache, &l_token_item->current_supply, sizeof(uint256_t)); + memcpy(l_cache + sizeof(uint256_t), l_token_item->datum_token, l_token_item->datum_token_size); + if (dap_global_db_set(l_gdb_group, l_token_item->ticker, l_cache, l_cache_size, false, NULL, NULL)) { + char *l_supply = dap_chain_balance_datoshi_print(l_token_item->current_supply); + log_it(L_WARNING, "Ledger cache mismatch, can't add token [%s] with supply %s", l_token_item->ticker, l_supply); + DAP_DELETE(l_supply); + } + DAP_DELETE(l_gdb_group); +} + +bool dap_ledger_pvt_token_supply_check(dap_ledger_token_item_t *a_token_item, uint256_t a_value) +{ + if ((IS_ZERO_256(a_token_item->total_supply) || IS_ZERO_256(a_value))) + return true; + if (compare256(a_token_item->current_supply, a_value) >= 0) + return true; + char *l_supply_str = dap_chain_balance_datoshi_print(a_token_item->current_supply); + char *l_value_str = dap_chain_balance_datoshi_print(a_value); + log_it(L_WARNING, "Token current supply %s < emission value %s", l_supply_str, l_value_str); + DAP_DEL_MULTY(l_supply_str, l_value_str); + return false; +} + +bool dap_ledger_pvt_token_supply_check_update(dap_ledger_t *a_ledger, dap_ledger_token_item_t *a_token_item, uint256_t a_value, bool a_for_removing) +{ + assert(a_token_item); + if ((IS_ZERO_256(a_token_item->total_supply) || IS_ZERO_256(a_value))) + return true; + if (!dap_ledger_pvt_token_supply_check(a_token_item, a_value) && !a_for_removing) + return false; + int l_overflow = false; + if(a_for_removing) + l_overflow = SUM_256_256(a_token_item->current_supply, a_value, &a_token_item->current_supply); + else + l_overflow = SUBTRACT_256_256(a_token_item->current_supply, a_value, &a_token_item->current_supply); + assert(!l_overflow); + const char *l_balance; dap_uint256_to_char(a_token_item->current_supply, &l_balance); + log_it(L_NOTICE, "New current supply %s for token %s", l_balance, a_token_item->ticker); + s_ledger_token_cache_update(a_ledger, a_token_item); + return true; +} + +/** + * @brief dap_ledger_token_add + * @param a_token + * @param a_token_size + * @return + */ +int dap_ledger_token_add(dap_ledger_t *a_ledger, byte_t *a_token, size_t a_token_size) +{ + dap_return_val_if_fail(a_ledger && a_token && a_token_size, DAP_LEDGER_CHECK_INVALID_ARGS); + dap_ledger_token_item_t *l_token_item = NULL; + dap_chain_datum_token_t *l_token = NULL; + size_t l_tsd_total_size = 0, l_signs_size = 0; + dap_hash_fast_t l_token_update_hash; + int ret = s_token_add_check(a_ledger, a_token, a_token_size, &l_token_item, &l_token, + &l_tsd_total_size, &l_signs_size, &l_token_update_hash); + if (ret != DAP_LEDGER_CHECK_OK && ret != DAP_LEDGER_CHECK_WHITELISTED) + return ret; + + if (!l_token_item) { + assert(l_token->type == DAP_CHAIN_DATUM_TOKEN_TYPE_DECL); + l_token_item = DAP_NEW_Z(dap_ledger_token_item_t); + if ( !l_token_item ) { + DAP_DELETE(l_token); + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; + } + *l_token_item = (dap_ledger_token_item_t) { + .subtype = l_token->subtype, + .total_supply = l_token->total_supply, + .current_supply = l_token->total_supply, + .auth_signs_total = l_token->signs_total, + .auth_signs_valid = l_token->signs_valid, + .token_emissions_rwlock = PTHREAD_RWLOCK_INITIALIZER, + .token_ts_updated_rwlock = PTHREAD_RWLOCK_INITIALIZER, + .auth_pkeys = DAP_NEW_Z_SIZE(dap_pkey_t*, sizeof(dap_pkey_t*) * l_token->signs_total), + .auth_pkey_hashes = DAP_NEW_Z_SIZE(dap_chain_hash_fast_t, sizeof(dap_chain_hash_fast_t) * l_token->signs_total), + .flags = 0 + }; + switch (l_token->subtype) { + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PRIVATE: + l_token_item->flags = l_token->header_private_decl.flags; break; + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE: + l_token_item->flags = l_token->header_native_decl.flags; break; + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PUBLIC: + l_token_item->flags = l_token->header_public.flags; break; + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_SIMPLE: + default:; + } + if ( !l_token_item->auth_pkeys ) { + DAP_DEL_MULTY(l_token, l_token_item); + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; + }; + if ( !l_token_item->auth_pkey_hashes ) { + DAP_DEL_MULTY(l_token, l_token_item->auth_pkeys, l_token_item); + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; + } + size_t l_auth_signs_total = l_token->signs_total; + dap_sign_t **l_signs = dap_sign_get_unique_signs(l_token->tsd_n_signs + l_tsd_total_size, + l_signs_size, + &l_auth_signs_total); +#define CLEAN_UP DAP_DEL_MULTY(l_token, l_token_item->auth_pkeys, l_token_item->auth_pkey_hashes, l_token_item) + if (!l_signs) { + CLEAN_UP; + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; + } + dap_stpcpy((char *)l_token_item->ticker, l_token->ticker); + for (uint16_t k = 0; k < l_token_item->auth_signs_total; k++) { + l_token_item->auth_pkeys[k] = dap_pkey_get_from_sign(l_signs[k]); + if (!l_token_item->auth_pkeys[k]) { + CLEAN_UP; + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; + } + dap_pkey_get_hash(l_token_item->auth_pkeys[k], &l_token_item->auth_pkey_hashes[k]); + } +#undef CLEAN_UP + DAP_DELETE(l_signs); + l_token_item->datum_token_size = sizeof(dap_chain_datum_token_t) + l_tsd_total_size + l_signs_size; + l_token_item->datum_token = l_token; + pthread_rwlock_wrlock(&PVT(a_ledger)->tokens_rwlock); + HASH_ADD_STR(PVT(a_ledger)->tokens, ticker, l_token_item); + } else { + assert(l_token->type == DAP_CHAIN_DATUM_TOKEN_TYPE_UPDATE); + pthread_rwlock_wrlock(&PVT(a_ledger)->tokens_rwlock); + dap_ledger_token_update_item_t *l_token_update_item = NULL; + pthread_rwlock_wrlock(&l_token_item->token_ts_updated_rwlock); + HASH_FIND(hh, l_token_item->token_ts_updated, &l_token_update_hash, sizeof(dap_hash_fast_t), l_token_update_item); + if (l_token_update_item) { + pthread_rwlock_unlock(&l_token_item->token_ts_updated_rwlock); + pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); + log_it(L_ERROR, "Token update with hash %s already exist in token %s hash-table", + dap_hash_fast_to_str_static(&l_token_update_hash), l_token->ticker); + DAP_DELETE(l_token); + return DAP_LEDGER_CHECK_APPLY_ERROR; + } + l_token_update_item = DAP_NEW(dap_ledger_token_update_item_t); + if (!l_token_update_item) { + pthread_rwlock_unlock(&l_token_item->token_ts_updated_rwlock); + pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + DAP_DELETE(l_token); + return DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; + } + *l_token_update_item = (dap_ledger_token_update_item_t) { + .update_token_hash = l_token_update_hash, + .datum_token_update = l_token, + .datum_token_update_size = sizeof(dap_chain_datum_token_t) + l_tsd_total_size + l_signs_size, + .updated_time = dap_time_now() + }; + HASH_ADD(hh, l_token_item->token_ts_updated, update_token_hash, sizeof(dap_chain_hash_fast_t), l_token_update_item); + pthread_rwlock_unlock(&l_token_item->token_ts_updated_rwlock); + l_token_item->last_update_token_time = l_token_update_item->updated_time; + } + if (ret != DAP_LEDGER_CHECK_WHITELISTED) { + ret = s_token_tsd_parse(l_token_item, l_token, a_ledger, l_token->tsd_n_signs, l_tsd_total_size, true); + assert(ret == DAP_LEDGER_CHECK_OK); + } + pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); + const char *l_balance_dbg = NULL, *l_declare_update_str = NULL, *l_type_str = NULL; + if (g_debug_ledger) + dap_uint256_to_char(l_token->total_supply, &l_balance_dbg); + switch (l_token->type) { + case DAP_CHAIN_DATUM_TOKEN_TYPE_DECL: l_declare_update_str = "declared"; break; + case DAP_CHAIN_DATUM_TOKEN_TYPE_UPDATE: l_declare_update_str = "updated"; break; + default: assert(false); break; + } + switch (l_token->subtype) { + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_SIMPLE: l_type_str = "Simple"; break; + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PRIVATE: l_type_str = "Private"; break; + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE: l_type_str = "CF20"; break; + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PUBLIC: l_type_str = "Public"; break; + default: assert(false); break; + } + debug_if(g_debug_ledger, L_INFO, "%s token %s has been %s, total_supply: %s, signs_valid: %zu, signs_total: %zu", + l_type_str, l_token_item->ticker, l_declare_update_str, + l_balance_dbg, l_token_item->auth_signs_valid, l_token_item->auth_signs_total); + s_ledger_token_cache_update(a_ledger, l_token_item); + return ret; +} + +int dap_ledger_token_load(dap_ledger_t *a_ledger, byte_t *a_token, size_t a_token_size) +{ + if (dap_chain_net_get_load_mode(a_ledger->net)) { + const char *l_ticker = NULL; + switch (*(uint16_t *)a_token) { + case DAP_CHAIN_DATUM_TOKEN_TYPE_DECL: + l_ticker = ((dap_chain_datum_token_t *)a_token)->ticker; + break; + case DAP_CHAIN_DATUM_TOKEN_TYPE_OLD_SIMPLE: + case DAP_CHAIN_DATUM_TOKEN_TYPE_OLD_PUBLIC: + case DAP_CHAIN_DATUM_TOKEN_TYPE_OLD_NATIVE_DECL: + case DAP_CHAIN_DATUM_TOKEN_TYPE_OLD_PRIVATE_DECL: + l_ticker = ((dap_chain_datum_token_old_t *)a_token)->ticker; + break; + } + if (l_ticker && dap_ledger_pvt_find_token(a_ledger, l_ticker)) + return DAP_LEDGER_CHECK_OK; + } + return dap_ledger_token_add(a_ledger, a_token, a_token_size); +} + +/** + * @brief dap_ledger_permissions_check + * @param a_token_item + * @param a_permission_id + * @param a_data + * @param a_data_size + * @return + */ +static bool s_ledger_permissions_check(dap_ledger_token_item_t *a_token_item, enum ledger_permissions a_permission_id, dap_chain_addr_t *a_addr) +{ + dap_chain_addr_t *l_addrs = NULL; + size_t l_addrs_count = 0; + switch (a_permission_id) { + case LEDGER_PERMISSION_RECEIVER_ALLOWED: + l_addrs = a_token_item->tx_recv_allow; + l_addrs_count = a_token_item->tx_recv_allow_size; + break; + case LEDGER_PERMISSION_RECEIVER_BLOCKED: + l_addrs = a_token_item->tx_recv_block; + l_addrs_count = a_token_item->tx_recv_block_size; + break; + case LEDGER_PERMISSION_SENDER_ALLOWED: + l_addrs = a_token_item->tx_send_allow; + l_addrs_count = a_token_item->tx_send_allow_size; + break; + case LEDGER_PERMISSION_SENDER_BLOCKED: + l_addrs = a_token_item->tx_send_block; + l_addrs_count = a_token_item->tx_send_block_size; + break; + } + for (size_t n = 0; n < l_addrs_count; n++) + if (dap_chain_addr_compare(l_addrs + n, a_addr)) + return true; + return false; +} + +dap_ledger_check_error_t dap_ledger_pvt_addr_check(dap_ledger_token_item_t *a_token_item, dap_chain_addr_t *a_addr, bool a_receive) +{ + dap_return_val_if_fail(a_token_item && a_addr, DAP_LEDGER_CHECK_INVALID_ARGS); + if (dap_chain_addr_is_blank(a_addr)) + return DAP_LEDGER_CHECK_OK; + if (a_receive) { + if ((a_token_item->flags & DAP_CHAIN_DATUM_TOKEN_FLAG_ALL_RECEIVER_BLOCKED) || + (a_token_item->flags & DAP_CHAIN_DATUM_TOKEN_FLAG_ALL_RECEIVER_FROZEN)) { + // Check we are in white list + if (!s_ledger_permissions_check(a_token_item, LEDGER_PERMISSION_RECEIVER_ALLOWED, a_addr)) + return DAP_LEDGER_CHECK_ADDR_FORBIDDEN; + } else if ((a_token_item->flags & DAP_CHAIN_DATUM_TOKEN_FLAG_ALL_RECEIVER_ALLOWED) || + (a_token_item->flags & DAP_CHAIN_DATUM_TOKEN_FLAG_ALL_RECEIVER_UNFROZEN)) { + // Check we are in black list + if (s_ledger_permissions_check(a_token_item, LEDGER_PERMISSION_RECEIVER_BLOCKED, a_addr)) + return DAP_LEDGER_CHECK_ADDR_FORBIDDEN; + } + } else { + if ((a_token_item->flags & DAP_CHAIN_DATUM_TOKEN_FLAG_ALL_SENDER_BLOCKED) || + (a_token_item->flags & DAP_CHAIN_DATUM_TOKEN_FLAG_ALL_SENDER_FROZEN)) { + // Check we are in white list + if (!s_ledger_permissions_check(a_token_item, LEDGER_PERMISSION_SENDER_ALLOWED, a_addr)) + return DAP_LEDGER_CHECK_ADDR_FORBIDDEN; + } else if ((a_token_item->flags & DAP_CHAIN_DATUM_TOKEN_FLAG_ALL_SENDER_ALLOWED) || + (a_token_item->flags & DAP_CHAIN_DATUM_TOKEN_FLAG_ALL_SENDER_UNFROZEN)) { + // Check we are in black list + if (s_ledger_permissions_check(a_token_item, LEDGER_PERMISSION_SENDER_BLOCKED, a_addr)) + return DAP_LEDGER_CHECK_ADDR_FORBIDDEN; + } + } + return DAP_LEDGER_CHECK_OK; +} + +int s_emission_add_check(dap_ledger_t *a_ledger, byte_t *a_token_emission, size_t a_token_emission_size, dap_chain_hash_fast_t *a_emission_hash, + dap_chain_datum_token_emission_t **a_emission, dap_ledger_token_item_t **a_token_item) +{ + dap_return_val_if_fail(a_token_emission && a_token_emission_size, DAP_LEDGER_CHECK_INVALID_ARGS); + size_t l_emission_size = a_token_emission_size; + dap_chain_datum_token_emission_t *l_emission = dap_chain_datum_emission_read(a_token_emission, &l_emission_size); + if (!l_emission) + return DAP_LEDGER_CHECK_INVALID_SIZE; + if (l_emission->hdr.version < 3 && !a_token_item) { // It's mempool check + log_it(L_WARNING, "Legacy emission version %hhu isn't supported for a new emissions", l_emission->hdr.version); + DAP_DELETE(l_emission); + return DAP_LEDGER_EMISSION_CHECK_LEGACY_FORBIDDEN; + } + dap_ledger_token_item_t *l_token_item = dap_ledger_pvt_find_token(a_ledger, l_emission->hdr.ticker); + if (!l_token_item) { + log_it(L_ERROR, "Check emission: token %s was not found", l_emission->hdr.ticker); + DAP_DELETE(l_emission); + return DAP_LEDGER_CHECK_TICKER_NOT_FOUND; + } + dap_ledger_token_emission_item_t *l_token_emission_item = NULL; + // check if such emission is already present in table + pthread_rwlock_rdlock(&l_token_item->token_emissions_rwlock); + HASH_FIND(hh, l_token_item->token_emissions, a_emission_hash, sizeof(*a_emission_hash), l_token_emission_item); + pthread_rwlock_unlock(&l_token_item->token_emissions_rwlock); + if (l_token_emission_item) { + debug_if(g_debug_ledger, L_WARNING, "Can't add token emission datum of %s %s ( %s ): already present in cache", + dap_uint256_to_char(l_emission->hdr.value, NULL), l_emission->hdr.ticker, + dap_chain_hash_fast_to_str_static(a_emission_hash)); + DAP_DELETE(l_emission); + return DAP_LEDGER_CHECK_ALREADY_CACHED; + } + + if (!PVT(a_ledger)->check_token_emission) + goto ret_success; + + // Check emission correctness + if (IS_ZERO_256((l_emission->hdr.value))) { + log_it(L_ERROR, "Emission check: zero %s emission value", l_token_item->ticker); + DAP_DELETE(l_emission); + return DAP_LEDGER_CHECK_ZERO_VALUE; + } + + if (!dap_ledger_pvt_token_supply_check(l_token_item, l_emission->hdr.value)) { + DAP_DELETE(l_emission); + return DAP_LEDGER_EMISSION_CHECK_VALUE_EXCEEDS_CURRENT_SUPPLY; + } + + //additional check for private tokens + if((l_token_item->subtype == DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PRIVATE) + || (l_token_item->subtype == DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE)) { + dap_ledger_check_error_t ret = dap_ledger_pvt_addr_check(l_token_item, &l_emission->hdr.address, true); + if (ret == DAP_LEDGER_CHECK_ADDR_FORBIDDEN) { + log_it(L_WARNING, "Address %s is not in allowed to receive for emission of token %s", + dap_chain_addr_to_str_static(&l_emission->hdr.address), l_token_item->ticker); + DAP_DELETE(l_emission); + return ret; + } + } + switch (l_emission->hdr.type) { + + case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_AUTH: { + size_t l_sign_data_check_size = sizeof(dap_chain_datum_token_emission_t) + l_emission->data.type_auth.tsd_total_size >= sizeof(dap_chain_datum_token_emission_t) + ? sizeof(dap_chain_datum_token_emission_t) + l_emission->data.type_auth.tsd_total_size : 0; + if (l_sign_data_check_size > l_emission_size) { + if (!s_check_hal(a_ledger, a_emission_hash)) { + log_it(L_WARNING, "Incorrect size %zu of datum emission, expected at least %zu", l_emission_size, l_sign_data_check_size); + DAP_DELETE(l_emission); + return DAP_LEDGER_CHECK_INVALID_SIZE; + } + goto ret_success; + } + size_t l_emission_check_size = sizeof(dap_chain_datum_token_emission_t) + l_emission->data.type_auth.tsd_n_signs_size >= sizeof(dap_chain_datum_token_emission_t) + ? sizeof(dap_chain_datum_token_emission_t) + l_emission->data.type_auth.tsd_n_signs_size : 0; + if (l_emission_check_size != l_emission_size) { + log_it(L_WARNING, "Incorrect size %zu of datum emission, must be %zu", l_emission_size, l_emission_check_size); + DAP_DELETE(l_emission); + return DAP_LEDGER_CHECK_INVALID_SIZE; + } + size_t l_signs_unique = l_emission->data.type_auth.signs_count; + dap_sign_t **l_signs = dap_sign_get_unique_signs(l_emission->tsd_n_signs + l_emission->data.type_auth.tsd_total_size, + l_emission->data.type_auth.tsd_n_signs_size, &l_signs_unique); + if (l_signs_unique < l_token_item->auth_signs_valid) { + + DAP_DELETE(l_signs); + + if (!s_check_hal(a_ledger, a_emission_hash)) { + + log_it(L_WARNING, "The number of unique token signs %zu is less than total token signs set to %zu", + l_signs_unique, l_token_item->auth_signs_total); + DAP_DELETE(l_emission); + return DAP_LEDGER_CHECK_NOT_ENOUGH_VALID_SIGNS; + } + + goto ret_success; + } + size_t l_sign_auth_count = l_emission->data.type_auth.signs_count; + size_t l_sign_auth_size = l_emission->data.type_auth.tsd_n_signs_size; + if (l_emission->hdr.version < 3) { + l_sign_data_check_size = sizeof(l_emission->hdr); + } else { + l_emission->data.type_auth.signs_count = 0; + l_emission->data.type_auth.tsd_n_signs_size = 0; + } + size_t l_aproves = 0; + for (uint16_t i = 0; i < l_signs_unique; i++) { + for (uint16_t k = 0; k < l_token_item->auth_signs_total; k++) { + if (dap_pkey_compare_with_sign(l_token_item->auth_pkeys[k], l_signs[i])) { + // Verify if token emission is signed + if (!dap_sign_verify(l_signs[i], l_emission, l_sign_data_check_size)) + l_aproves++; + break; + } + } + } + if (l_emission->hdr.version >= 3) { + l_emission->data.type_auth.signs_count = l_sign_auth_count; + l_emission->data.type_auth.tsd_n_signs_size = l_sign_auth_size; + } + DAP_DELETE(l_signs); + if (l_aproves < l_token_item->auth_signs_valid && + !s_check_hal(a_ledger, a_emission_hash)) { + log_it(L_WARNING, "Emission of %s datoshi of %s:%s is wrong: only %zu valid aproves when %zu need", + dap_uint256_to_char(l_emission->hdr.value, NULL), a_ledger->net->pub.name, l_emission->hdr.ticker, + l_aproves, l_token_item->auth_signs_valid); + debug_if(g_debug_ledger, L_ATT, "!!! Datum hash for HAL: %s", dap_chain_hash_fast_to_str_static(a_emission_hash)); + DAP_DELETE(l_emission); + return DAP_LEDGER_CHECK_NOT_ENOUGH_VALID_SIGNS; + } + } break; + + default: + log_it(L_ERROR, "Checking emission of type %s not implemented", dap_chain_datum_emission_type_str(l_emission->hdr.type)); + DAP_DELETE(l_emission); + return DAP_LEDGER_CHECK_PARSE_ERROR; + } + +ret_success: + if (a_token_item) + *a_token_item = l_token_item; + if (a_emission) + *a_emission = l_emission; + else + DAP_DELETE(l_emission); + + return DAP_LEDGER_CHECK_OK; +} + +int dap_ledger_token_emission_add_check(dap_ledger_t *a_ledger, byte_t *a_token_emission, size_t a_token_emission_size, dap_chain_hash_fast_t *a_emission_hash) +{ + return s_emission_add_check(a_ledger, a_token_emission, a_token_emission_size, a_emission_hash, NULL, NULL); +} + +void dap_ledger_pvt_emission_cache_update(dap_ledger_t *a_ledger, dap_ledger_token_emission_item_t *a_emission_item) +{ + if (!PVT(a_ledger)->cached) + return; + char *l_gdb_group = dap_ledger_get_gdb_group(a_ledger, DAP_LEDGER_EMISSIONS_STR); + size_t l_cache_size = a_emission_item->datum_token_emission_size + sizeof(dap_hash_fast_t); + uint8_t *l_cache = DAP_NEW_STACK_SIZE(uint8_t, l_cache_size); + memcpy(l_cache, &a_emission_item->tx_used_out, sizeof(dap_hash_fast_t)); + memcpy(l_cache + sizeof(dap_hash_fast_t), a_emission_item->datum_token_emission, a_emission_item->datum_token_emission_size); + char l_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE]; + dap_chain_hash_fast_to_str(&a_emission_item->datum_token_emission_hash, l_hash_str, sizeof(l_hash_str)); + if (dap_global_db_set(l_gdb_group, l_hash_str, l_cache, l_cache_size, false, NULL, NULL)) { + log_it(L_WARNING, "Ledger cache mismatch"); + } + DAP_DELETE(l_gdb_group); +} + +/** + * @brief dap_ledger_token_emission_add + * @param a_token_emission + * @param a_token_emision_size + * @return + */ + +int dap_ledger_token_emission_add(dap_ledger_t *a_ledger, byte_t *a_token_emission, size_t a_token_emission_size, dap_hash_fast_t *a_emission_hash) +{ + dap_ledger_token_item_t *l_token_item = NULL; + dap_chain_datum_token_emission_t *l_emission = NULL; + int l_ret = s_emission_add_check(a_ledger, a_token_emission, a_token_emission_size, a_emission_hash, &l_emission, &l_token_item); + if (l_ret != DAP_LEDGER_CHECK_OK) + return l_ret; + dap_ledger_token_emission_item_t *l_token_emission_item = NULL; + // check if such emission is already present in table + pthread_rwlock_wrlock(&l_token_item->token_emissions_rwlock); + HASH_FIND(hh, l_token_item->token_emissions, a_emission_hash, sizeof(*a_emission_hash), l_token_emission_item); + if (l_token_emission_item) { + pthread_rwlock_unlock(&l_token_item->token_emissions_rwlock); + log_it(L_ERROR, "Duplicate token emission datum of %s %s ( %s )", + dap_uint256_to_char(l_emission->hdr.value, NULL), l_emission->hdr.ticker, dap_hash_fast_to_str_static(a_emission_hash)); + DAP_DELETE(l_emission); + return DAP_LEDGER_CHECK_APPLY_ERROR; + } + l_token_emission_item = DAP_NEW_Z(dap_ledger_token_emission_item_t); + if (!l_token_emission_item) { + pthread_rwlock_unlock(&l_token_item->token_emissions_rwlock); + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; + } + l_token_emission_item->datum_token_emission = l_emission; + l_token_emission_item->datum_token_emission_hash = *a_emission_hash; + HASH_ADD(hh, l_token_item->token_emissions, datum_token_emission_hash, sizeof(*a_emission_hash), l_token_emission_item); + //Update value in ledger memory object + if (!dap_ledger_pvt_token_supply_check_update(a_ledger, l_token_item, l_emission->hdr.value, false)) { + HASH_DEL(l_token_item->token_emissions, l_token_emission_item); + pthread_rwlock_unlock(&l_token_item->token_emissions_rwlock); + DAP_DELETE(l_emission); + DAP_DELETE(l_token_emission_item); + return DAP_LEDGER_CHECK_APPLY_ERROR; + } + pthread_rwlock_unlock(&l_token_item->token_emissions_rwlock); + // Add it to cache + dap_ledger_pvt_emission_cache_update(a_ledger, l_token_emission_item); + if (g_debug_ledger) { + const char *l_balance; dap_uint256_to_char(l_token_emission_item->datum_token_emission->hdr.value, &l_balance); + log_it(L_NOTICE, "Added token emission datum to emissions cache: type=%s value=%s token=%s to_addr=%s", + dap_chain_datum_emission_type_str(l_emission->hdr.type), + l_balance, l_emission->hdr.ticker, + dap_chain_addr_to_str_static(&(l_emission->hdr.address))); + } + if (PVT(a_ledger)->threshold_enabled) + dap_ledger_pvt_threshold_txs_proc(a_ledger); + return DAP_LEDGER_CHECK_OK; +} + +int dap_ledger_token_emission_load(dap_ledger_t *a_ledger, byte_t *a_token_emission, + size_t a_token_emission_size, dap_hash_fast_t *a_token_emission_hash) +{ + if (dap_chain_net_get_load_mode(a_ledger->net)) { + dap_ledger_token_emission_item_t *l_token_emission_item = NULL; + dap_ledger_token_item_t *l_token_item, *l_item_tmp; + pthread_rwlock_rdlock(&PVT(a_ledger)->tokens_rwlock); + HASH_ITER(hh, PVT(a_ledger)->tokens, l_token_item, l_item_tmp) { + pthread_rwlock_rdlock(&l_token_item->token_emissions_rwlock); + HASH_FIND(hh, l_token_item->token_emissions, a_token_emission_hash, sizeof(*a_token_emission_hash), + l_token_emission_item); + pthread_rwlock_unlock(&l_token_item->token_emissions_rwlock); + if (l_token_emission_item) { + pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); + return 0; + } + } + pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); + } + return dap_ledger_token_emission_add(a_ledger, a_token_emission, a_token_emission_size, a_token_emission_hash); +} + +dap_ledger_token_emission_item_t *dap_ledger_pvt_emission_item_find(dap_ledger_t *a_ledger, + const char *a_token_ticker, const dap_chain_hash_fast_t *a_token_emission_hash, dap_ledger_token_item_t **a_token_item) +{ + dap_ledger_token_item_t *l_token_item = dap_ledger_pvt_find_token(a_ledger, a_token_ticker); + if (!l_token_item) + return NULL; + else if (a_token_item) + *a_token_item = l_token_item; + dap_ledger_token_emission_item_t *l_token_emission_item = NULL; + pthread_rwlock_rdlock(&l_token_item->token_emissions_rwlock); + HASH_FIND(hh, l_token_item->token_emissions, a_token_emission_hash, sizeof(*a_token_emission_hash), l_token_emission_item); + pthread_rwlock_unlock(&l_token_item->token_emissions_rwlock); + return l_token_emission_item; +} + +/** + * @brief dap_ledger_token_emission_find + * @param a_token_ticker + * @param a_token_emission_hash + * @return + */ +dap_chain_datum_token_emission_t *dap_ledger_token_emission_find(dap_ledger_t *a_ledger, const dap_chain_hash_fast_t *a_token_emission_hash) +{ + dap_ledger_token_emission_item_t *l_emission_item = NULL; + pthread_rwlock_rdlock(&PVT(a_ledger)->tokens_rwlock); + for (dap_ledger_token_item_t *l_item = PVT(a_ledger)->tokens; l_item; l_item = l_item->hh.next) { + l_emission_item = dap_ledger_pvt_emission_item_find(a_ledger, l_item->ticker, a_token_emission_hash, NULL); + if (l_emission_item) + break; + } + pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); + return l_emission_item ? l_emission_item->datum_token_emission : NULL; +} + +const char *dap_ledger_get_description_by_ticker(dap_ledger_t *a_ledger, const char *a_token_ticker) +{ + dap_return_val_if_fail(a_ledger && a_token_ticker, NULL); + dap_ledger_token_item_t *l_token_item = dap_ledger_pvt_find_token(a_ledger, a_token_ticker); + return l_token_item ? l_token_item->description : NULL; +} + +/** + * @breif dap_ledger_token_get_auth_signs_valid + * @param a_ledger + * @param a_token_ticker + * @return 0 if no ticker found + */ +size_t dap_ledger_token_get_auth_signs_valid(dap_ledger_t *a_ledger, const char *a_token_ticker) +{ + dap_ledger_token_item_t *l_token_item = dap_ledger_pvt_find_token(a_ledger, a_token_ticker); + if (!l_token_item) + return 0; + return l_token_item->auth_signs_valid; +} + +/** + * @breif dap_ledger_token_get_auth_signs_total + * @param a_ledger + * @param a_token_ticker + * @return + */ +size_t dap_ledger_token_get_auth_signs_total(dap_ledger_t *a_ledger, const char *a_token_ticker) +{ + dap_ledger_token_item_t *l_token_item = dap_ledger_pvt_find_token(a_ledger, a_token_ticker); + if (!l_token_item) + return 0; + return l_token_item->auth_signs_total; +} + +/** + * @breif dap_ledger_token_auth_signs_hashes + * @param a_ledger + * @param a_token_ticker + * @return + */ +dap_list_t *dap_ledger_token_get_auth_pkeys_hashes(dap_ledger_t *a_ledger, const char *a_token_ticker) +{ + dap_list_t *l_ret = NULL; + dap_ledger_token_item_t *l_token_item = dap_ledger_pvt_find_token(a_ledger, a_token_ticker); + if (!l_token_item) + return l_ret; + debug_if(g_debug_ledger, L_INFO, " ! Token %s : total %lu auth signs", a_token_ticker, l_token_item->auth_signs_total); + for (size_t i = 0; i < l_token_item->auth_signs_total; i++) + l_ret = dap_list_append(l_ret, l_token_item->auth_pkey_hashes + i); + return l_ret; +} + +uint256_t dap_ledger_token_get_emission_rate(dap_ledger_t *a_ledger, const char *a_token_ticker) +{ + dap_ledger_token_item_t *l_token_item = dap_ledger_pvt_find_token(a_ledger, a_token_ticker); + if (!l_token_item || !l_token_item->is_delegated) + return uint256_0; + return l_token_item->emission_rate; +} + +json_object *s_token_item_to_json(dap_ledger_token_item_t *a_token_item) +{ + json_object *json_obj_datum = json_object_new_object(); + const char *l_type_str = NULL; + switch (a_token_item->subtype) { + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_SIMPLE: + l_type_str = "SIMPLE"; break; + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PRIVATE: + l_type_str = "PRIVATE"; break; + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE: + l_type_str = "CF20"; break; + case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PUBLIC: + l_type_str = "PUBLIC"; break; + default: l_type_str = "UNKNOWN"; break; + } + json_object_object_add(json_obj_datum, "-->Token name", json_object_new_string(a_token_item->ticker)); + json_object_object_add(json_obj_datum, "type", json_object_new_string(l_type_str)); + if (a_token_item->subtype != DAP_CHAIN_DATUM_TOKEN_SUBTYPE_SIMPLE && a_token_item->subtype != DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PUBLIC) { + dap_chain_datum_token_flags_dump_to_json(json_obj_datum, "flags", a_token_item->flags); + json_object_object_add(json_obj_datum, "description", a_token_item->description ? + json_object_new_string(a_token_item->description) : + json_object_new_string("The token description is not set")); + } + json_object_object_add(json_obj_datum, "Supply current", json_object_new_string(dap_uint256_to_char(a_token_item->current_supply, NULL))); + json_object_object_add(json_obj_datum, "Supply total", json_object_new_string(dap_uint256_to_char(a_token_item->total_supply, NULL))); + json_object_object_add(json_obj_datum, "Decimals", json_object_new_string("18")); + json_object_object_add(json_obj_datum, "Auth signs valid", json_object_new_int(a_token_item->auth_signs_valid)); + json_object_object_add(json_obj_datum, "Auth signs total", json_object_new_int(a_token_item->auth_signs_total)); + json_object *l_json_arr_pkeys = json_object_new_array(); + for (uint16_t i = 0; i < a_token_item->auth_signs_total; i++) { + json_object *l_json_obj_out = json_object_new_object(); + json_object_object_add(l_json_obj_out, "line", json_object_new_int(i)); + json_object_object_add(l_json_obj_out, "hash", json_object_new_string(dap_hash_fast_to_str_static(a_token_item->auth_pkey_hashes + i))); + json_object_object_add(l_json_obj_out, "pkey_type", json_object_new_string(dap_pkey_type_to_str(a_token_item->auth_pkeys[i]->header.type))); + json_object_object_add(l_json_obj_out, "bytes", json_object_new_int(a_token_item->auth_pkeys[i]->header.size)); + json_object_array_add(l_json_arr_pkeys, l_json_obj_out); + } + json_object *l_json_arr_tx_recv_allow = json_object_new_array(); + for (size_t i = 0; i < a_token_item->tx_recv_allow_size; i++) { + dap_chain_addr_t l_addr = a_token_item->tx_recv_allow[i]; + const char *l_addr_str = dap_chain_addr_to_str_static(&l_addr); + json_object_array_add(l_json_arr_tx_recv_allow, json_object_new_string(l_addr_str)); + } + json_object *l_json_arr_tx_recv_block = json_object_new_array(); + for (size_t i = 0; i < a_token_item->tx_recv_block_size; i++) { + dap_chain_addr_t l_addr = a_token_item->tx_recv_block[i]; + const char *l_addr_str = dap_chain_addr_to_str_static(&l_addr); + json_object_array_add(l_json_arr_tx_recv_block, json_object_new_string(l_addr_str)); + } + json_object *l_json_arr_tx_send_allow = json_object_new_array(); + for (size_t i = 0; i < a_token_item->tx_send_allow_size; i++) { + dap_chain_addr_t l_addr = a_token_item->tx_send_allow[i]; + const char *l_addr_str = dap_chain_addr_to_str_static(&l_addr); + json_object_array_add(l_json_arr_tx_send_allow, json_object_new_string(l_addr_str)); + } + json_object *l_json_arr_tx_send_block = json_object_new_array(); + for (size_t i = 0; i < a_token_item->tx_send_block_size; i++) { + dap_chain_addr_t l_addr = a_token_item->tx_send_block[i]; + const char *l_addr_str = dap_chain_addr_to_str_static(&l_addr); + json_object_array_add(l_json_arr_tx_send_block, json_object_new_string(l_addr_str)); + } + json_object_object_add(json_obj_datum, "Signatures public keys", l_json_arr_pkeys); + a_token_item->tx_recv_allow_size ? json_object_object_add(json_obj_datum, "tx_recv_allow", l_json_arr_tx_recv_allow) : + json_object_put(l_json_arr_tx_recv_allow); + a_token_item->tx_recv_block_size ? json_object_object_add(json_obj_datum, "tx_recv_block", l_json_arr_tx_recv_block) : + json_object_put(l_json_arr_tx_recv_block); + a_token_item->tx_send_allow_size ? json_object_object_add(json_obj_datum, "tx_send_allow", l_json_arr_tx_send_allow) : + json_object_put(l_json_arr_tx_send_allow); + a_token_item->tx_send_block_size ? json_object_object_add(json_obj_datum, "tx_send_block", l_json_arr_tx_send_block) : + json_object_put(l_json_arr_tx_send_block); + json_object_object_add(json_obj_datum, "Total emissions", json_object_new_int(HASH_COUNT(a_token_item->token_emissions))); + return json_obj_datum; +} + +/** + * @brief Compose string list of all tokens with information + * @param a_ledger + * @return + */ +json_object *dap_ledger_token_info(dap_ledger_t *a_ledger, size_t a_limit, size_t a_offset) +{ + json_object * json_obj_datum; + json_object * json_arr_out = json_object_new_array(); + dap_ledger_token_item_t *l_token_item, *l_tmp_item; + pthread_rwlock_rdlock(&PVT(a_ledger)->tokens_rwlock); + size_t l_arr_start = 0; + if (a_offset > 0) { + l_arr_start = a_offset; + json_object* json_obj_tx = json_object_new_object(); + json_object_object_add(json_obj_tx, "offset", json_object_new_int(l_arr_start)); + json_object_array_add(json_arr_out, json_obj_tx); + } + size_t l_arr_end = HASH_COUNT(PVT(a_ledger)->tokens); + if (a_limit) { + json_object* json_obj_tx = json_object_new_object(); + json_object_object_add(json_obj_tx, "limit", json_object_new_int(a_limit)); + json_object_array_add(json_arr_out, json_obj_tx); + l_arr_end = l_arr_start + a_limit; + if (l_arr_end > HASH_COUNT(PVT(a_ledger)->tokens)) { + l_arr_end = HASH_COUNT(PVT(a_ledger)->tokens); + } + } + size_t i = 0; + HASH_ITER(hh, PVT(a_ledger)->tokens, l_token_item, l_tmp_item) { + if (i < l_arr_start || i >= l_arr_end) { + i++; + continue; + } + json_obj_datum = s_token_item_to_json(l_token_item); + json_object_array_add(json_arr_out, json_obj_datum); + i++; + } + pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); + return json_arr_out; +} + +/** + * @breif Forms a JSON object with a token description for the specified ticker. + * @param a_ledger + * @param a_token_ticker + * @return + */ +json_object *dap_ledger_token_info_by_name(dap_ledger_t *a_ledger, const char *a_token_ticker) +{ + dap_ledger_token_item_t *l_token_item = NULL; + HASH_FIND_STR(PVT(a_ledger)->tokens, a_token_ticker, l_token_item); + if (l_token_item) + return s_token_item_to_json(l_token_item); + return json_object_new_null(); +} + +/** + * @brief Get all token declatations + * @param a_ledger + * @return + */ +dap_list_t* dap_ledger_token_decl_all(dap_ledger_t *a_ledger) +{ + dap_list_t * l_ret = NULL; + dap_ledger_token_item_t *l_token_item, *l_tmp_item; + pthread_rwlock_rdlock(&PVT(a_ledger)->tokens_rwlock); + + HASH_ITER(hh, PVT(a_ledger)->tokens, l_token_item, l_tmp_item) { + dap_chain_datum_token_t *l_token = l_token_item->datum_token; + l_ret = dap_list_append(l_ret, l_token); + } + pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); + return l_ret; +} + +/** + * @brief Get list of all tickets for ledger and address. If address is NULL returns all the tockens present in system + * @param a_ledger + * @param a_addr + * @param a_tickers + * @param a_tickers_size + */ +void dap_ledger_addr_get_token_ticker_all(dap_ledger_t *a_ledger, dap_chain_addr_t * a_addr, + char *** a_tickers, size_t * a_tickers_size) +{ + if (a_addr == NULL){ // Get all tockens + pthread_rwlock_rdlock(&PVT(a_ledger)->tokens_rwlock); + size_t l_count = HASH_COUNT(PVT(a_ledger)->tokens); + if (l_count && a_tickers){ + dap_ledger_token_item_t * l_token_item, *l_tmp; + char **l_tickers = DAP_NEW_Z_SIZE(char*, l_count * sizeof(char*)); + if (!l_tickers) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + pthread_rwlock_unlock(&PVT(a_ledger)->balance_accounts_rwlock); + return; + } + l_count = 0; + HASH_ITER(hh, PVT(a_ledger)->tokens, l_token_item, l_tmp) { + l_tickers[l_count] = dap_strdup(l_token_item->ticker); + l_count++; + } + *a_tickers = l_tickers; + } + pthread_rwlock_unlock(&PVT(a_ledger)->tokens_rwlock); + if(a_tickers_size) + *a_tickers_size = l_count; + }else{ // Calc only tokens from address balance + dap_ledger_wallet_balance_t *wallet_balance, *tmp; + size_t l_count = HASH_COUNT(PVT(a_ledger)->balance_accounts); + if(l_count && a_tickers){ + char **l_tickers = DAP_NEW_Z_SIZE(char*, l_count * sizeof(char*)); + if (!l_tickers) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + pthread_rwlock_unlock(&PVT(a_ledger)->balance_accounts_rwlock); + return; + } + l_count = 0; + const char *l_addr = dap_chain_addr_to_str_static(a_addr); + pthread_rwlock_rdlock(&PVT(a_ledger)->balance_accounts_rwlock); + HASH_ITER(hh, PVT(a_ledger)->balance_accounts, wallet_balance, tmp) { + char **l_keys = dap_strsplit(wallet_balance->key, " ", -1); + if (!dap_strcmp(l_keys[0], l_addr)) { + l_tickers[l_count] = dap_strdup(wallet_balance->token_ticker); + ++l_count; + } + dap_strfreev(l_keys); + } + pthread_rwlock_unlock(&PVT(a_ledger)->balance_accounts_rwlock); + *a_tickers = l_tickers; + } + if(a_tickers_size) + *a_tickers_size = l_count; + } +} diff --git a/modules/ledger/dap_chain_ledger_tx.c b/modules/ledger/dap_chain_ledger_tx.c new file mode 100644 index 0000000000000000000000000000000000000000..8ef61c000606a09a676bc5facd5847c8f426138d --- /dev/null +++ b/modules/ledger/dap_chain_ledger_tx.c @@ -0,0 +1,2118 @@ + +/* + * Authors: + * Dmitriy A. Gearasimov <gerasimov.dmitriy@demlabs.net> + * Alexander Lysikov <alexander.lysikov@demlabs.net> + * Roman Khlopkov <roman.khlopkov@demlabs.net> + * DeM Labs Inc. https://demlabs.net + * DeM Labs Open source community https://github.com/demlabsinc + * Copyright (c) 2017-2024 + * All rights reserved. + + This file is part of CellFrame SDK the open source project + + CellFrame SDK 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. + + CellFrame SDK 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 CellFrame SDK based project. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "dap_chain_ledger_pvt.h" +#include "dap_notify_srv.h" + +#define LOG_TAG "dap_ledger_tx" + +typedef struct dap_ledger_verificator { + int subtype; // hash key + dap_ledger_verificator_callback_t callback; + dap_ledger_updater_callback_t callback_added; + dap_ledger_delete_callback_t callback_deleted; + UT_hash_handle hh; +} dap_ledger_verificator_t; + +typedef struct dap_chain_ledger_votings_callbacks { + dap_ledger_voting_callback_t voting_callback; + dap_ledger_voting_delete_callback_t voting_delete_callback; +} dap_chain_ledger_votings_callbacks_t; + +static dap_ledger_verificator_t *s_verificators; +static pthread_rwlock_t s_verificators_rwlock = PTHREAD_RWLOCK_INITIALIZER; + +static dap_chain_ledger_votings_callbacks_t s_voting_callbacks; +static dap_ledger_tax_callback_t s_tax_callback = NULL; + +typedef struct dap_ledger_tokenizer { + char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX]; + uint256_t sum; + UT_hash_handle hh; +} dap_ledger_tokenizer_t; + +typedef struct dap_ledger_tx_bound { + uint8_t type; + uint16_t prev_out_idx; + uint256_t value; + union { + dap_ledger_token_item_t *token_item; // For current_supply update on emissions + dap_chain_tx_out_cond_t *cond; // For conditional output + struct { + char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX]; + dap_chain_addr_t addr_from; + } in; + }; + union { + dap_ledger_tx_item_t *prev_item; // For not emission TX + dap_ledger_token_emission_item_t *emission_item; + dap_ledger_stake_lock_item_t *stake_lock_item; + dap_ledger_reward_key_t reward_key; + }; +} dap_ledger_tx_bound_t; + +typedef struct dap_ledger_cache_item { + dap_chain_hash_fast_t *hash; + bool found; +} dap_ledger_cache_item_t; + +typedef struct dap_ledger_cache_str_item { + char *key; + bool found; +} dap_ledger_cache_str_item_t; + +typedef struct dap_ledger_tx_notifier { + dap_ledger_tx_add_notify_t callback; + void *arg; +} dap_ledger_tx_notifier_t; + +typedef struct dap_ledger_bridged_tx_notifier { + dap_ledger_bridged_tx_notify_t callback; + void *arg; +} dap_ledger_bridged_tx_notifier_t; + +static dap_ledger_tx_item_t *s_tx_item_find_by_addr(dap_ledger_t *a_ledger, const dap_chain_addr_t *a_addr, const char * a_token, dap_chain_hash_fast_t *a_tx_first_hash); +static bool s_ledger_tx_hash_is_used_out_item(dap_ledger_tx_item_t *a_item, int a_idx_out, dap_hash_fast_t *a_out_spender_hash); +static dap_ledger_stake_lock_item_t *s_emissions_for_stake_lock_item_find(dap_ledger_t *a_ledger, const dap_chain_hash_fast_t *a_token_emission_hash); + +static dap_chain_datum_tx_t *s_tx_find_by_hash(dap_ledger_t *a_ledger, const dap_chain_hash_fast_t *a_tx_hash, dap_ledger_tx_item_t **a_item_out, bool a_unspent_only); +static struct json_object *s_wallet_info_json_collect(dap_ledger_t *a_ledger, dap_ledger_wallet_balance_t* a_bal); + +static void s_ledger_stake_lock_cache_update(dap_ledger_t *a_ledger, dap_ledger_stake_lock_item_t *a_stake_lock_item); + +static int s_sort_ledger_tx_item(dap_ledger_tx_item_t *a, dap_ledger_tx_item_t *b) +{ + if (a->cache_data.ts_created < b->cache_data.ts_created) + return -1; + if (a->cache_data.ts_created == b->cache_data.ts_created) + return 0; + return 1; +} + +/** + * @brief s_load_cache_gdb_loaded_txs_callback + * @param a_global_db_context + * @param a_rc + * @param a_group + * @param a_key + * @param a_values_total + * @param a_values_shift + * @param a_values_count + * @param a_values + * @param a_arg + */ +static bool s_load_cache_gdb_loaded_txs_callback(dap_global_db_instance_t *a_dbi, + int a_rc, const char *a_group, + const size_t a_values_total, const size_t a_values_count, + dap_global_db_obj_t *a_values, void *a_arg) +{ + dap_ledger_t * l_ledger = (dap_ledger_t*) a_arg; + dap_ledger_private_t * l_ledger_pvt = PVT(l_ledger); + for (size_t i = 0; i < a_values_count; i++) { + dap_ledger_tx_item_t *l_tx_item = DAP_NEW_Z(dap_ledger_tx_item_t); + if ( !l_tx_item ) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return false; + } + dap_chain_hash_fast_from_str(a_values[i].key, &l_tx_item->tx_hash_fast); + l_tx_item->tx = DAP_NEW_Z_SIZE(dap_chain_datum_tx_t, a_values[i].value_len - sizeof(l_tx_item->cache_data)); + if ( !l_tx_item->tx ) { + DAP_DELETE(l_tx_item); + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return false; + } + memcpy(&l_tx_item->cache_data, a_values[i].value, sizeof(l_tx_item->cache_data)); + memcpy(l_tx_item->tx, a_values[i].value + sizeof(l_tx_item->cache_data), a_values[i].value_len - sizeof(l_tx_item->cache_data)); + l_tx_item->ts_added = dap_nanotime_now(); + HASH_ADD(hh, l_ledger_pvt->ledger_items, tx_hash_fast, sizeof(dap_chain_hash_fast_t), l_tx_item); + } + HASH_SORT(l_ledger_pvt->ledger_items, s_sort_ledger_tx_item); + return true; +} + + +bool dap_ledger_pvt_cache_gdb_load_stake_lock_callback(dap_global_db_instance_t *a_dbi, + int a_rc, const char *a_group, + const size_t a_values_total, const size_t a_values_count, + dap_global_db_obj_t *a_values, void *a_arg) +{ + dap_ledger_t *l_ledger = (dap_ledger_t *) a_arg; + dap_ledger_private_t *l_ledger_pvt = PVT(l_ledger); + + for (size_t i = 0; i < a_values_count; i++) { + if (a_values[i].value_len != sizeof(dap_hash_fast_t)) + continue; + dap_ledger_stake_lock_item_t *l_new_stake_lock_emission = DAP_NEW(dap_ledger_stake_lock_item_t); + if (!l_new_stake_lock_emission) { + debug_if(g_debug_ledger, L_ERROR, "Error: memory allocation when try adding item 'dap_ledger_stake_lock_item_t' to hash-table"); + continue; + } + dap_chain_hash_fast_from_str(a_values[i].key, &l_new_stake_lock_emission->tx_for_stake_lock_hash); + l_new_stake_lock_emission->tx_used_out = *(dap_hash_fast_t *)(a_values[i].value); + HASH_ADD(hh, l_ledger_pvt->emissions_for_stake_lock, tx_for_stake_lock_hash, sizeof(dap_chain_hash_fast_t), l_new_stake_lock_emission); + } + + char* l_gdb_group = dap_ledger_get_gdb_group(l_ledger, DAP_LEDGER_TXS_STR); + dap_global_db_get_all(l_gdb_group, 0, s_load_cache_gdb_loaded_txs_callback, l_ledger); + DAP_DELETE(l_gdb_group); + return true; +} + +void dap_ledger_load_end(dap_ledger_t *a_ledger) +{ + pthread_rwlock_wrlock(&PVT(a_ledger)->ledger_rwlock); + HASH_SORT(PVT(a_ledger)->ledger_items, s_sort_ledger_tx_item); + pthread_rwlock_unlock(&PVT(a_ledger)->ledger_rwlock); +} + +static dap_ledger_reward_item_t *s_find_reward(dap_ledger_t *a_ledger, dap_ledger_reward_key_t *a_search_key) +{ + dap_ledger_reward_item_t *l_reward_item = NULL; + pthread_rwlock_rdlock(&PVT(a_ledger)->rewards_rwlock); + HASH_FIND(hh, PVT(a_ledger)->rewards, a_search_key, sizeof(*a_search_key), l_reward_item); + pthread_rwlock_unlock(&PVT(a_ledger)->rewards_rwlock); + return l_reward_item; +} + +bool dap_ledger_is_used_reward(dap_ledger_t *a_ledger, dap_hash_fast_t *a_block_hash, dap_hash_fast_t *a_sign_pkey_hash) +{ + dap_ledger_reward_key_t l_search_key = { .block_hash = *a_block_hash, .sign_pkey_hash = *a_sign_pkey_hash }; + return s_find_reward(a_ledger, &l_search_key); +} + +/** + * Checking a new transaction before adding to the cache + * + * return 0 OK, otherwise error + */ +// Checking a new transaction before adding to the cache +static int s_tx_cache_check(dap_ledger_t *a_ledger, + dap_chain_datum_tx_t *a_tx, + dap_hash_fast_t *a_tx_hash, + bool a_from_threshold, + dap_list_t **a_list_bound_items, + dap_list_t **a_list_tx_out, + char *a_main_ticker, + dap_chain_srv_uid_t *a_tag, + dap_chain_tx_tag_action_type_t *a_action, + bool a_check_for_removing) +{ + dap_return_val_if_fail(a_ledger && a_tx && a_tx_hash, DAP_LEDGER_CHECK_INVALID_ARGS); + if (!a_from_threshold) { + dap_ledger_tx_item_t *l_ledger_item = NULL; + pthread_rwlock_rdlock(&PVT(a_ledger)->ledger_rwlock); + HASH_FIND(hh, PVT(a_ledger)->ledger_items, a_tx_hash, sizeof(dap_chain_hash_fast_t), l_ledger_item); + pthread_rwlock_unlock(&PVT(a_ledger)->ledger_rwlock); + if (l_ledger_item && !a_check_for_removing ) { // transaction already present in the cache list + if (g_debug_ledger) { + log_it(L_WARNING, "Transaction %s already present in the cache", dap_chain_hash_fast_to_str_static(a_tx_hash)); + if (a_tag) *a_tag = l_ledger_item->cache_data.tag; + if (a_action) *a_action = l_ledger_item->cache_data.action; + } + return DAP_LEDGER_CHECK_ALREADY_CACHED; + } else if (!l_ledger_item && a_check_for_removing) { // transaction already present in the cache list + debug_if(g_debug_ledger, L_WARNING, "Transaction %s not present in the cache. Can not delete it. Skip.", dap_chain_hash_fast_to_str_static(a_tx_hash)); + return DAP_LEDGER_TX_CHECK_FOR_REMOVING_CANT_FIND_TX; + } + } +/* + * Steps of checking for current transaction tx2 and every previous transaction tx1: + * 1. valid(tx2.dap_chain_datum_tx_sig.pkey) + * && + * 2. tx2.input != tx2.inputs.used + * && + * 3. !is_used_out(tx1.dap_chain_datum_tx_out) + * && + * 4. tx1.dap_chain_datum_tx_out.addr.data.key == tx2.dap_chain_datum_tx_sig.pkey for unconditional output + * \\ + * 5. tx1.dap_chain_datum_tx_out.condition == verify_svc_type(tx2) for conditional output + * && + * 6. sum( find (tx2.input.tx_prev_hash).output[tx2.input_tx_prev_idx].value ) == sum (tx2.outputs.value) per token + * && + * 7. valid(fee) +*/ + dap_list_t *l_list_bound_items = NULL; + dap_list_t *l_list_tx_out = NULL; + + // sum of values in 'out' items from the previous transactions + dap_ledger_tokenizer_t *l_values_from_prev_tx = NULL, *l_values_from_cur_tx = NULL, + *l_value_cur = NULL, *l_tmp = NULL, *l_res = NULL; + const char *l_token = NULL, *l_main_ticker = NULL; + + int l_err_num = DAP_LEDGER_CHECK_OK; + int l_prev_tx_count = 0; + + // 1. Verify signature in current transaction + if (!a_from_threshold && dap_chain_datum_tx_verify_sign(a_tx)) + return DAP_LEDGER_CHECK_NOT_ENOUGH_VALID_SIGNS; + + // ---------------------------------------------------------------- + // find all 'in' && 'in_cond' && 'in_ems' && 'in_reward' items in current transaction + dap_list_t *l_list_in = dap_chain_datum_tx_items_get(a_tx, TX_ITEM_TYPE_IN_ALL, &l_prev_tx_count); + if (!l_list_in) { + log_it(L_WARNING, "Tx check: no valid inputs found"); + return DAP_LEDGER_TX_CHECK_TX_NO_VALID_INPUTS; + } + dap_chain_hash_fast_t l_tx_first_sign_pkey_hash = {}; + dap_pkey_t *l_tx_first_sign_pkey = NULL; + bool l_girdled_ems_used = false; + uint256_t l_taxed_value = {}; + + if(a_tag) dap_ledger_deduct_tx_tag(a_ledger, a_tx, NULL, a_tag, a_action); + + // find all previous transactions + for (dap_list_t *it = l_list_in; it; it = it->next) { + dap_ledger_tx_bound_t *l_bound_item = DAP_NEW_Z(dap_ledger_tx_bound_t); + if (!l_bound_item) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + l_err_num = DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; + break; + } + l_list_bound_items = dap_list_append(l_list_bound_items, l_bound_item); + + uint8_t l_cond_type = *(uint8_t *)it->data; + l_bound_item->type = l_cond_type; + uint256_t l_value = uint256_0; + void *l_tx_prev_out = NULL; + dap_chain_datum_tx_t *l_tx_prev = NULL; + dap_ledger_token_emission_item_t *l_emission_item = NULL; + dap_ledger_stake_lock_item_t *l_stake_lock_emission = NULL; + bool l_girdled_ems = false; + + switch (l_cond_type) { + case TX_ITEM_TYPE_IN_EMS: { // It's the emission (base) TX + dap_chain_tx_in_ems_t *l_tx_in_ems = it->data; + l_token = l_tx_in_ems->header.ticker; + if (!dap_chain_datum_token_check_ticker(l_token)) { + l_err_num = DAP_LEDGER_CHECK_INVALID_TICKER; + break; + } + dap_hash_fast_t *l_emission_hash = &l_tx_in_ems->header.token_emission_hash; + // 2. Check current transaction for doubles in input items list + for (dap_list_t *l_iter = it->next; l_iter; l_iter = l_iter->next) { + dap_chain_tx_in_ems_t *l_in_ems_check = l_iter->data; + if (l_in_ems_check->header.type == TX_ITEM_TYPE_IN_EMS && + dap_hash_fast_compare(&l_in_ems_check->header.token_emission_hash, l_emission_hash) && !a_check_for_removing) + { + debug_if(g_debug_ledger, L_ERROR, "Emission output already used in current tx"); + l_err_num = DAP_LEDGER_TX_CHECK_PREV_OUT_ALREADY_USED_IN_CURRENT_TX; + break; + } + } + if (l_err_num) + break; + if ((l_girdled_ems = dap_hash_fast_is_blank(l_emission_hash)) || + (l_stake_lock_emission = s_emissions_for_stake_lock_item_find(a_ledger, l_emission_hash))) { + dap_chain_datum_tx_t *l_tx_stake_lock = a_tx; + // 3. Check emission for STAKE_LOCK + if (!dap_hash_fast_is_blank(l_emission_hash)) { + dap_hash_fast_t cur_tx_hash; + dap_hash_fast(a_tx, dap_chain_datum_tx_get_size(a_tx), &cur_tx_hash); + if (!dap_hash_fast_is_blank(&l_stake_lock_emission->tx_used_out) && !a_check_for_removing) { + if (!dap_hash_fast_compare(&cur_tx_hash, &l_stake_lock_emission->tx_used_out)) + debug_if(g_debug_ledger, L_WARNING, "stake_lock_emission already present in cache for IN_EMS [%s]", l_token); + else + debug_if(g_debug_ledger, L_WARNING, "stake_lock_emission is used out for IN_EMS [%s]", l_token); + l_err_num = DAP_LEDGER_TX_CHECK_STAKE_LOCK_IN_EMS_ALREADY_USED; + break; + } + l_tx_stake_lock = dap_ledger_tx_find_by_hash(a_ledger, l_emission_hash); + } else { + // 2. The only allowed item with girdled emission + if (l_girdled_ems_used && !a_check_for_removing) { + debug_if(g_debug_ledger, L_WARNING, "stake_lock_emission is used out for IN_EMS [%s]", l_token); + l_err_num = DAP_LEDGER_TX_CHECK_STAKE_LOCK_IN_EMS_ALREADY_USED; + break; + } else + l_girdled_ems_used = true; + } + if (!l_tx_stake_lock) { + debug_if(g_debug_ledger, L_WARNING, "Not found stake_lock transaction"); + l_err_num = DAP_CHAIN_CS_VERIFY_CODE_TX_NO_EMISSION; + break; + } + + dap_ledger_token_item_t *l_delegated_item = dap_ledger_pvt_find_token(a_ledger, l_token); + if (!l_delegated_item) { + debug_if(g_debug_ledger, L_WARNING, "Token [%s] not found", l_token); + l_err_num = DAP_LEDGER_CHECK_TICKER_NOT_FOUND; + break; + } + if (!l_delegated_item->is_delegated) { + debug_if(g_debug_ledger, L_WARNING, "Token [%s] not valid for stake_lock transaction", l_token); + l_err_num = DAP_LEDGER_TX_CHECK_STAKE_LOCK_INVALID_TOKEN; + break; + } + if (!dap_ledger_token_ticker_check(a_ledger, l_delegated_item->delegated_from)) { + debug_if(g_debug_ledger, L_WARNING, "Token [%s] not found", l_delegated_item->delegated_from); + l_err_num = DAP_LEDGER_CHECK_TICKER_NOT_FOUND; + break; + } + + if (l_girdled_ems) + l_main_ticker = l_delegated_item->delegated_from; + + dap_chain_tx_out_cond_t *l_tx_stake_lock_out_cond = dap_chain_datum_tx_out_cond_get(l_tx_stake_lock, DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK, NULL); + if (!l_tx_stake_lock_out_cond) { + debug_if(g_debug_ledger, L_WARNING, "No OUT_COND of stake_lock subtype for IN_EMS [%s]", l_tx_in_ems->header.ticker); + l_err_num = DAP_LEDGER_TX_CHECK_STAKE_LOCK_NO_OUT_COND_FOR_IN_EMS; + break; + } + uint256_t l_value_expected ={}; + if (MULT_256_COIN(l_tx_stake_lock_out_cond->header.value, l_delegated_item->emission_rate, &l_value_expected)) { + if (g_debug_ledger) { + char *l_emission_rate_str = dap_chain_balance_coins_print(l_delegated_item->emission_rate); + const char *l_locked_value_str; dap_uint256_to_char(l_tx_stake_lock_out_cond->header.value, &l_locked_value_str); + log_it( L_WARNING, "Multiplication overflow for %s emission: locked value %s emission rate %s", + l_tx_in_ems->header.ticker, l_locked_value_str, l_emission_rate_str); + DAP_DEL_Z(l_emission_rate_str); + } + l_err_num = DAP_LEDGER_CHECK_INTEGER_OVERFLOW; + break; + } + dap_chain_tx_out_ext_t *l_tx_out_ext = NULL; + uint256_t l_stake_lock_ems_value = {}; + int l_item_idx = 0; + do { + l_tx_out_ext = (dap_chain_tx_out_ext_t *)dap_chain_datum_tx_item_get(a_tx, &l_item_idx, NULL, TX_ITEM_TYPE_OUT_EXT, NULL); + if (!l_tx_out_ext) { + if (l_girdled_ems) { + debug_if(g_debug_ledger, L_WARNING, "No OUT_EXT for girdled IN_EMS [%s]", l_tx_in_ems->header.ticker); + l_err_num = DAP_LEDGER_TX_CHECK_NO_OUT_EXT_FOR_GIRDLED_IN_EMS; + } + break; + } + l_item_idx++; + } while (strcmp(l_tx_out_ext->token, l_token)); + if (!l_tx_out_ext) { + dap_chain_tx_out_t *l_tx_out = (dap_chain_tx_out_t *)dap_chain_datum_tx_item_get(a_tx, NULL, NULL, TX_ITEM_TYPE_OUT, NULL); + if (!l_tx_out) { + debug_if(true, L_WARNING, "Can't find OUT nor OUT_EXT item for base TX with IN_EMS [%s]", l_tx_in_ems->header.ticker); + l_err_num = DAP_LEDGER_TX_CHECK_NO_OUT_ITEMS_FOR_BASE_TX; + break; + } else + l_stake_lock_ems_value = l_tx_out->header.value; + } else + l_stake_lock_ems_value = l_tx_out_ext->header.value; + if (!dap_ledger_pvt_token_supply_check(l_delegated_item, l_stake_lock_ems_value)) { + l_err_num = DAP_LEDGER_EMISSION_CHECK_VALUE_EXCEEDS_CURRENT_SUPPLY; + break; + } + if (!EQUAL_256(l_value_expected, l_stake_lock_ems_value)) { + // !!! A terrible legacy crutch, TODO !!! + SUM_256_256(l_value_expected, GET_256_FROM_64(10), &l_value_expected); + if (!EQUAL_256(l_value_expected, l_stake_lock_ems_value)) { + char *l_value_expected_str = dap_chain_balance_datoshi_print(l_value_expected); + char *l_locked_value_str = dap_chain_balance_datoshi_print(l_stake_lock_ems_value); + + debug_if(g_debug_ledger, L_WARNING, "Value %s != %s expected for [%s]",l_locked_value_str, l_value_expected_str, + l_tx_in_ems->header.ticker); + + DAP_DEL_Z(l_value_expected_str); + DAP_DEL_Z(l_locked_value_str); + l_err_num = DAP_LEDGER_TX_CHECK_STAKE_LOCK_UNEXPECTED_VALUE; + break; + } + } + if (!l_girdled_ems) { + // check tiker + const char *l_tx_ticker = dap_ledger_tx_get_token_ticker_by_hash(a_ledger, l_emission_hash); + if (!l_tx_ticker) { + debug_if(g_debug_ledger, L_WARNING, "No ticker found for stake_lock tx [expected '%s']", l_tx_in_ems->header.ticker); + l_err_num = DAP_LEDGER_CHECK_TICKER_NOT_FOUND; + break; + } + if (strcmp(l_tx_ticker, l_delegated_item->delegated_from)) { + debug_if(g_debug_ledger, L_WARNING, "Ticker '%s' != expected '%s'", l_tx_ticker, l_tx_in_ems->header.ticker); + l_err_num = DAP_LEDGER_TX_CHECK_STAKE_LOCK_OTHER_TICKER_EXPECTED; + break; + } + } + debug_if(g_debug_ledger, L_NOTICE, "Check emission passed for IN_EMS [%s]", l_tx_in_ems->header.ticker); + if (l_stake_lock_emission) { + l_bound_item->stake_lock_item = l_stake_lock_emission; + l_value = l_stake_lock_ems_value; + } else // girdled emission + l_value = l_tx_out_ext->header.value; + l_bound_item->token_item = l_delegated_item; + l_bound_item->type = TX_ITEM_TYPE_IN_EMS_LOCK; + } else if ( (l_emission_item = dap_ledger_pvt_emission_item_find(a_ledger, l_token, l_emission_hash, &l_bound_item->token_item)) ) { + // 3. Check AUTH token emission + if (!dap_hash_fast_is_blank(&l_emission_item->tx_used_out) && !a_check_for_removing) { + debug_if(g_debug_ledger, L_WARNING, "Emission for IN_EMS [%s] is already used", l_tx_in_ems->header.ticker); + l_err_num = DAP_LEDGER_TX_CHECK_IN_EMS_ALREADY_USED; + break; + } + l_value = l_emission_item->datum_token_emission->hdr.value; + l_bound_item->emission_item = l_emission_item; + } else { + l_err_num = DAP_CHAIN_CS_VERIFY_CODE_TX_NO_EMISSION; + break; + } + } break; + + case TX_ITEM_TYPE_IN_REWARD: { + dap_chain_tx_in_reward_t *l_tx_in_reward = it->data; + dap_hash_fast_t *l_block_hash = &l_tx_in_reward->block_hash; + // 2. Check current transaction for doubles in input items list + for (dap_list_t *l_iter = l_list_in; l_iter; l_iter = l_iter->next) { + dap_chain_tx_in_reward_t *l_in_reward_check = l_iter->data; + if (l_tx_in_reward != l_in_reward_check && + l_in_reward_check->type == TX_ITEM_TYPE_IN_REWARD && + dap_hash_fast_compare(&l_in_reward_check->block_hash, l_block_hash) && !a_check_for_removing) { + debug_if(g_debug_ledger, L_ERROR, "Reward for this block sign already used in current tx"); + l_err_num = DAP_LEDGER_TX_CHECK_PREV_OUT_ALREADY_USED_IN_CURRENT_TX; + break; + } + } + if (l_err_num) + break; + if (!l_tx_first_sign_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, NULL, + TX_ITEM_TYPE_SIG, NULL); + assert(l_tx_sig); + // Get sign from sign item + dap_sign_t *l_tx_first_sign = dap_chain_datum_tx_item_sign_get_sig(l_tx_sig); + assert(l_tx_first_sign); + // calculate hash from sign public key + dap_sign_get_pkey_hash(l_tx_first_sign, &l_tx_first_sign_pkey_hash); + l_tx_first_sign_pkey = dap_pkey_get_from_sign(l_tx_first_sign); + } + // 3. Check if already spent reward + dap_ledger_reward_key_t l_search_key = { .block_hash = *l_block_hash, .sign_pkey_hash = l_tx_first_sign_pkey_hash }; + dap_ledger_reward_item_t *l_reward_item = s_find_reward(a_ledger, &l_search_key); + if (l_reward_item && !a_check_for_removing) { + l_err_num = DAP_LEDGER_TX_CHECK_REWARD_ITEM_ALREADY_USED; + char l_block_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE], + l_sign_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE], + l_spender_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE]; + dap_chain_hash_fast_to_str(l_block_hash, l_block_hash_str, sizeof(l_block_hash_str)); + dap_chain_hash_fast_to_str(&l_tx_first_sign_pkey_hash, l_sign_hash_str, sizeof(l_sign_hash_str)); + dap_chain_hash_fast_to_str(&l_reward_item->spender_tx, l_spender_hash_str, sizeof(l_spender_hash_str)); + debug_if(g_debug_ledger, L_WARNING, "Reward for block %s sign %s already spent by %s", l_block_hash_str, l_sign_hash_str, l_spender_hash_str); + break; + } + // Check reward legitimacy & amount + dap_chain_t *l_chain; + DL_FOREACH(a_ledger->net->pub.chains, l_chain) { + if (l_chain->callback_calc_reward) { + l_value = l_chain->callback_calc_reward(l_chain, l_block_hash, l_tx_first_sign_pkey); + break; + } + } + if (IS_ZERO_256(l_value)) { + l_err_num = DAP_LEDGER_TX_CHECK_REWARD_ITEM_ILLEGAL; + char l_block_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE], + l_sign_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE]; + dap_chain_hash_fast_to_str(l_block_hash, l_block_hash_str, sizeof(l_block_hash_str)); + dap_chain_hash_fast_to_str(&l_tx_first_sign_pkey_hash, l_sign_hash_str, sizeof(l_sign_hash_str)); + debug_if(g_debug_ledger, L_DEBUG, "Can't find block %s with sign %s", l_block_hash_str, l_sign_hash_str); + break; + } + // Reward nominated in net native ticker only + l_token = l_main_ticker = a_ledger->net->pub.native_ticker; + dap_ledger_token_item_t *l_token_item = dap_ledger_pvt_find_token(a_ledger, l_token); + if (!l_token_item) { + debug_if(g_debug_ledger, L_ERROR, "Native token ticker not found"); + l_err_num = DAP_LEDGER_CHECK_TICKER_NOT_FOUND; + break; + } + if (!dap_ledger_pvt_token_supply_check(l_token_item, l_value) && !a_check_for_removing) { + l_err_num = DAP_LEDGER_EMISSION_CHECK_VALUE_EXCEEDS_CURRENT_SUPPLY; + break; + } + l_bound_item->token_item = l_token_item; + l_bound_item->reward_key = l_search_key; + // Overflow checked later with overall values sum + SUM_256_256(l_taxed_value, l_value, &l_taxed_value); + } break; + + case TX_ITEM_TYPE_IN: + case TX_ITEM_TYPE_IN_COND: { // Not emission types + uint32_t l_tx_prev_out_idx = (uint32_t)-1; + dap_hash_fast_t *l_tx_prev_hash; + if (l_cond_type == TX_ITEM_TYPE_IN) { + dap_chain_tx_in_t *l_tx_in = it->data; + l_tx_prev_hash = &l_tx_in->header.tx_prev_hash; + if (dap_hash_fast_is_blank(l_tx_prev_hash)) { + DAP_DELETE(l_bound_item); + l_list_bound_items = dap_list_delete_link(l_list_bound_items, dap_list_last(l_list_bound_items)); + continue; // old base tx compliance + } + l_tx_prev_out_idx = l_tx_in->header.tx_out_prev_idx; + // 2. Check current transaction for doubles in input items list + for (dap_list_t *l_iter = l_list_in; l_iter; l_iter = l_iter->next) { + dap_chain_tx_in_t *l_in_check = l_iter->data; + if (l_tx_in != l_in_check && + l_in_check->header.type == TX_ITEM_TYPE_IN && + l_in_check->header.tx_out_prev_idx == l_tx_prev_out_idx && + dap_hash_fast_compare(&l_in_check->header.tx_prev_hash, l_tx_prev_hash) && !a_check_for_removing) { + debug_if(g_debug_ledger, L_ERROR, "This previous tx output already used in current tx"); + l_err_num = DAP_LEDGER_TX_CHECK_PREV_OUT_ALREADY_USED_IN_CURRENT_TX; + break; + } + } + if (l_err_num) + break; + } else { + dap_chain_tx_in_cond_t *l_tx_in_cond = it->data; + l_tx_prev_hash = &l_tx_in_cond->header.tx_prev_hash; + l_tx_prev_out_idx = l_tx_in_cond->header.tx_out_prev_idx; + // 2. Check current transaction for doubles in input items list + for (dap_list_t *l_iter = l_list_in; l_iter; l_iter = l_iter->next) { + dap_chain_tx_in_cond_t *l_in_cond_check = l_iter->data; + if (l_tx_in_cond != l_in_cond_check && + l_in_cond_check->header.type == TX_ITEM_TYPE_IN_COND && + l_in_cond_check->header.tx_out_prev_idx == l_tx_prev_out_idx && + dap_hash_fast_compare(&l_in_cond_check->header.tx_prev_hash, l_tx_prev_hash) && !a_check_for_removing) { + debug_if(g_debug_ledger, L_ERROR, "This previous tx output already used in current tx"); + l_err_num = DAP_LEDGER_TX_CHECK_PREV_OUT_ALREADY_USED_IN_CURRENT_TX; + break; + } + } + if (l_err_num) + break; + } + // Get previous transaction in the cache by hash + dap_ledger_tx_item_t *l_item_out = NULL; + l_tx_prev = s_tx_find_by_hash(a_ledger, l_tx_prev_hash, &l_item_out, false); + char l_tx_prev_hash_str[DAP_HASH_FAST_STR_SIZE]; + dap_hash_fast_to_str(l_tx_prev_hash, l_tx_prev_hash_str, DAP_HASH_FAST_STR_SIZE); + if (!l_tx_prev) { // Unchained transaction or previous TX was already spent and removed from ledger + debug_if(g_debug_ledger && !a_from_threshold, L_DEBUG, "No previous transaction was found for hash %s", l_tx_prev_hash_str); + l_err_num = DAP_CHAIN_CS_VERIFY_CODE_TX_NO_PREVIOUS; + break; + } else if (l_item_out->cache_data.ts_spent && !a_check_for_removing) { + l_err_num = DAP_LEDGER_TX_CHECK_OUT_ITEM_ALREADY_USED; + debug_if(g_debug_ledger, L_WARNING, "All 'out' items of previous tx %s were already spent", l_tx_prev_hash_str); + break; + } + l_bound_item->prev_item = l_item_out; + l_bound_item->prev_out_idx = l_tx_prev_out_idx; + l_token = l_item_out->cache_data.token_ticker; + debug_if(g_debug_ledger && !a_from_threshold, L_INFO, "Previous transaction was found for hash %s",l_tx_prev_hash_str); + + // 2. Check if out in previous transaction has spent + dap_hash_fast_t l_spender = {}; + if (s_ledger_tx_hash_is_used_out_item(l_item_out, l_tx_prev_out_idx, &l_spender) && !a_check_for_removing) { + l_err_num = DAP_LEDGER_TX_CHECK_OUT_ITEM_ALREADY_USED; + char l_hash[DAP_CHAIN_HASH_FAST_STR_SIZE]; + dap_chain_hash_fast_to_str(&l_spender, l_hash, sizeof(l_hash)); + debug_if(g_debug_ledger, L_INFO, "'Out' item of previous tx %s already spent by %s", l_tx_prev_hash_str, l_hash); + break; + } + + // Get one 'out' item in previous transaction bound with current 'in' item + l_tx_prev_out = dap_chain_datum_tx_item_get_nth(l_tx_prev, TX_ITEM_TYPE_OUT_ALL, l_tx_prev_out_idx); + if(!l_tx_prev_out) { + l_err_num = DAP_LEDGER_TX_CHECK_PREV_OUT_ITEM_NOT_FOUND; + break; + } + if (dap_hash_fast_is_blank(&l_tx_first_sign_pkey_hash)) { + // 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, NULL, + TX_ITEM_TYPE_SIG, NULL); + assert(l_tx_sig); + // Get sign from sign item + dap_sign_t *l_tx_first_sign = dap_chain_datum_tx_item_sign_get_sig(l_tx_sig); + assert(l_tx_first_sign); + // calculate hash from sign public key + dap_sign_get_pkey_hash(l_tx_first_sign, &l_tx_first_sign_pkey_hash); + } + if (l_cond_type == TX_ITEM_TYPE_IN) { + dap_chain_addr_t *l_addr_from = NULL; + dap_chain_tx_item_type_t l_type = *(uint8_t *)l_tx_prev_out; + switch (l_type) { + case TX_ITEM_TYPE_OUT_OLD: // Deprecated + l_addr_from = &((dap_chain_tx_out_old_t *)l_tx_prev_out)->addr; + l_value = dap_chain_uint256_from(((dap_chain_tx_out_old_t *)l_tx_prev_out)->header.value); + break; + case TX_ITEM_TYPE_OUT: + l_addr_from = &((dap_chain_tx_out_t *)l_tx_prev_out)->addr; + l_value = ((dap_chain_tx_out_t *)l_tx_prev_out)->header.value; + break; + case TX_ITEM_TYPE_OUT_EXT: + l_addr_from = &((dap_chain_tx_out_ext_t *)l_tx_prev_out)->addr; + l_value = ((dap_chain_tx_out_ext_t *)l_tx_prev_out)->header.value; + l_token = ((dap_chain_tx_out_ext_t *)l_tx_prev_out)->token; + break; + default: + l_err_num = DAP_LEDGER_TX_CHECK_PREV_OUT_ITEM_MISSTYPED; + break; + } + if (l_err_num) + break; + l_bound_item->in.addr_from = *l_addr_from; + dap_strncpy(l_bound_item->in.token_ticker, l_token, DAP_CHAIN_TICKER_SIZE_MAX); + // 4. compare public key hashes in the signature of the current transaction and in the 'out' item of the previous transaction + if (l_addr_from->net_id.uint64 != a_ledger->net->pub.id.uint64 || + !dap_hash_fast_compare(&l_tx_first_sign_pkey_hash, &l_addr_from->data.hash_fast)) { + l_err_num = DAP_LEDGER_TX_CHECK_PKEY_HASHES_DONT_MATCH; + break; + } + + if ( !l_token || !*l_token ) { + log_it(L_WARNING, "No token ticker found in previous transaction"); + l_err_num = DAP_LEDGER_TX_CHECK_NO_MAIN_TICKER; + break; + } + // Get permissions + dap_ledger_token_item_t *l_token_item = dap_ledger_pvt_find_token(a_ledger, l_token); + if (!l_token_item) { + debug_if(g_debug_ledger, L_WARNING, "Token with ticker %s not found", l_token); + l_err_num = DAP_LEDGER_CHECK_TICKER_NOT_FOUND; + break; + } + // Check permissions + if (dap_ledger_pvt_addr_check(l_token_item, l_addr_from, false) == DAP_LEDGER_CHECK_ADDR_FORBIDDEN) { + debug_if(g_debug_ledger, L_WARNING, "No permission to send for addr %s", dap_chain_addr_to_str_static(l_addr_from)); + l_err_num = DAP_LEDGER_CHECK_ADDR_FORBIDDEN; + break; + } + } else { // l_cond_type == TX_ITEM_TYPE_IN_COND + if(*(uint8_t *)l_tx_prev_out != TX_ITEM_TYPE_OUT_COND) { + l_err_num = DAP_LEDGER_TX_CHECK_PREV_OUT_ITEM_MISSTYPED; + break; + } + dap_chain_tx_out_cond_t *l_tx_prev_out_cond = NULL; + l_tx_prev_out_cond = (dap_chain_tx_out_cond_t *)l_tx_prev_out; + + // 5a. Check for condition owner + // Get owner tx + dap_hash_fast_t l_owner_tx_hash = dap_ledger_get_first_chain_tx_hash(a_ledger, l_tx_prev, l_tx_prev_out_cond); + dap_chain_datum_tx_t *l_owner_tx = dap_hash_fast_is_blank(&l_owner_tx_hash) + ? l_tx_prev + : dap_ledger_tx_find_by_hash(a_ledger, &l_owner_tx_hash); + dap_chain_tx_sig_t *l_tx_sig = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get(a_tx, NULL, NULL, TX_ITEM_TYPE_SIG, NULL); + dap_sign_t *l_sign = dap_chain_datum_tx_item_sign_get_sig((dap_chain_tx_sig_t *)l_tx_sig); + dap_chain_tx_sig_t *l_owner_tx_sig = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get(l_owner_tx, NULL, NULL, TX_ITEM_TYPE_SIG, NULL); + dap_sign_t *l_owner_sign = dap_chain_datum_tx_item_sign_get_sig((dap_chain_tx_sig_t *)l_owner_tx_sig); + + bool l_owner = false; + l_owner = dap_sign_compare_pkeys(l_owner_sign, l_sign); + + // 5b. Call verificator for conditional output + dap_ledger_verificator_t *l_verificator = NULL; + int l_sub_tmp = l_tx_prev_out_cond->header.subtype; + + pthread_rwlock_rdlock(&s_verificators_rwlock); + HASH_FIND_INT(s_verificators, &l_sub_tmp, l_verificator); + pthread_rwlock_unlock(&s_verificators_rwlock); + if (!l_verificator || !l_verificator->callback) { + debug_if(g_debug_ledger, L_ERROR, "No verificator set for conditional output subtype %d", l_sub_tmp); + l_err_num = DAP_LEDGER_TX_CHECK_NO_VERIFICATOR_SET; + break; + } + + int l_verificator_error = l_verificator->callback(a_ledger, l_tx_prev_out_cond, a_tx, l_owner); + if (l_verificator_error != DAP_LEDGER_CHECK_OK) { // TODO add string representation for verificator return codes + debug_if(g_debug_ledger, L_WARNING, "Verificator check error %d for conditional output %s", + l_verificator_error, dap_chain_tx_out_cond_subtype_to_str(l_sub_tmp)); + l_err_num = DAP_LEDGER_TX_CHECK_VERIFICATOR_CHECK_FAILURE; + break; + } + l_bound_item->cond = l_tx_prev_out_cond; + l_value = l_tx_prev_out_cond->header.value; + if (l_tx_prev_out_cond->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE) { + l_token = a_ledger->net->pub.native_ticker; + // Overflow checked later with overall values sum + SUM_256_256(l_taxed_value, l_value, &l_taxed_value); + } + l_main_ticker = l_token; + } + } break; + + default: + break; + } + if (l_err_num) + break; + + l_bound_item->value = l_value; + + if (l_cond_type != TX_ITEM_TYPE_IN) { + // If not checked earlier + if (!l_token || !*l_token) { + log_it(L_WARNING, "No token ticker found in previous transaction"); + l_err_num = DAP_LEDGER_TX_CHECK_NO_MAIN_TICKER; + break; + } + } + HASH_FIND_STR(l_values_from_prev_tx, l_token, l_value_cur); + if (!l_value_cur) { + l_value_cur = DAP_NEW_Z(dap_ledger_tokenizer_t); + if ( !l_value_cur ) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + l_err_num = DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; + break; + } + strcpy(l_value_cur->token_ticker, l_token); + HASH_ADD_STR(l_values_from_prev_tx, token_ticker, l_value_cur); + } + // calculate from previous transactions per each token + if (SUM_256_256(l_value_cur->sum, l_value, &l_value_cur->sum)) { + debug_if(g_debug_ledger, L_WARNING, "Sum result overflow for tx_add_check with ticker %s", + l_value_cur->token_ticker); + l_err_num = DAP_LEDGER_CHECK_INTEGER_OVERFLOW; + break; + } + } + + dap_list_free(l_list_in); + DAP_DELETE(l_tx_first_sign_pkey); + if (l_err_num) { + if ( l_list_bound_items ) + dap_list_free_full(l_list_bound_items, NULL); + HASH_ITER(hh, l_values_from_prev_tx, l_value_cur, l_tmp) { + HASH_DEL(l_values_from_prev_tx, l_value_cur); + DAP_DELETE(l_value_cur); + } + return l_err_num; + } + + // 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 + bool l_multichannel = false; + if (HASH_COUNT(l_values_from_prev_tx) > 1) { + l_multichannel = true; + if (HASH_COUNT(l_values_from_prev_tx) == 2 && !l_main_ticker) { + HASH_FIND_STR(l_values_from_prev_tx, a_ledger->net->pub.native_ticker, l_value_cur); + if (l_value_cur) { + l_value_cur = l_value_cur->hh.next ? l_value_cur->hh.next : l_value_cur->hh.prev; + l_main_ticker = l_value_cur->token_ticker; + } + } + } else { + l_value_cur = DAP_NEW_Z(dap_ledger_tokenizer_t); + if ( !l_value_cur ) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + l_err_num = DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; + if ( l_list_bound_items ) + dap_list_free_full(l_list_bound_items, NULL); + HASH_ITER(hh, l_values_from_prev_tx, l_value_cur, l_tmp) { + HASH_DEL(l_values_from_prev_tx, l_value_cur); + DAP_DELETE(l_value_cur); + } + return l_err_num; + } + dap_stpcpy(l_value_cur->token_ticker, l_token); + if (!l_main_ticker) + l_main_ticker = l_value_cur->token_ticker; + HASH_ADD_STR(l_values_from_cur_tx, token_ticker, l_value_cur); + } + dap_chain_addr_t l_sovereign_addr; + uint256_t l_sovereign_tax; + bool l_tax_check = s_tax_callback ? s_tax_callback(a_ledger->net->pub.id, &l_tx_first_sign_pkey_hash, &l_sovereign_addr, &l_sovereign_tax) + : false; + // find 'out' items + uint256_t l_value = {}, l_fee_sum = {}, l_tax_sum = {}; + bool l_fee_check = !IS_ZERO_256(a_ledger->net->pub.fee_value) && !dap_chain_addr_is_blank(&a_ledger->net->pub.fee_addr); + int l_item_idx = 0; + byte_t *it; size_t l_size; + TX_ITEM_ITER_TX(it, l_size, a_tx) { + dap_chain_addr_t l_tx_out_to = { }; + switch ( *it ) { + case TX_ITEM_TYPE_OUT_OLD: { + dap_chain_tx_out_old_t *l_tx_out = (dap_chain_tx_out_old_t*)it; + if (l_multichannel) { // token ticker is mandatory for multichannel transactions + l_err_num = DAP_LEDGER_TX_CHECK_NO_MAIN_TICKER; + break; + } + l_value = dap_chain_uint256_from(l_tx_out->header.value); + l_tx_out_to = l_tx_out->addr; + l_list_tx_out = dap_list_append(l_list_tx_out, l_tx_out); + } break; + case TX_ITEM_TYPE_OUT: { // 256 + dap_chain_tx_out_t *l_tx_out = (dap_chain_tx_out_t *)it; + if (l_multichannel) { // token ticker is mandatory for multichannel transactions + if (l_main_ticker) + l_token = l_main_ticker; + else { + l_err_num = DAP_LEDGER_TX_CHECK_NO_MAIN_TICKER; + break; + } + } + l_value = l_tx_out->header.value; + l_tx_out_to = l_tx_out->addr; + l_list_tx_out = dap_list_append(l_list_tx_out, l_tx_out); + } break; + case TX_ITEM_TYPE_OUT_EXT: { // 256 + dap_chain_tx_out_ext_t *l_tx_out = (dap_chain_tx_out_ext_t *)it; + if (!l_multichannel) { // token ticker is forbiden for single-channel transactions + l_err_num = DAP_LEDGER_TX_CHECK_UNEXPECTED_TOKENIZED_OUT; + break; + } + l_value = l_tx_out->header.value; + l_token = l_tx_out->token; + l_tx_out_to = l_tx_out->addr; + l_list_tx_out = dap_list_append(l_list_tx_out, l_tx_out); + } break; + case TX_ITEM_TYPE_OUT_COND: { + dap_chain_tx_out_cond_t *l_tx_out = (dap_chain_tx_out_cond_t *)it; + if (l_multichannel) { + if (l_tx_out->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE) + l_token = (char *)a_ledger->net->pub.native_ticker; + else if (l_main_ticker) + l_token = l_main_ticker; + else { + log_it(L_WARNING, "No conditional output support for multichannel transaction"); + l_err_num = DAP_LEDGER_TX_CHECK_NO_MAIN_TICKER; + break; + } + } + l_value = l_tx_out->header.value; + l_list_tx_out = dap_list_append(l_list_tx_out, l_tx_out); + if (l_tax_check && l_tx_out->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE && + SUBTRACT_256_256(l_taxed_value, l_value, &l_taxed_value)) { + log_it(L_WARNING, "Fee is greater than sum of inputs"); + l_err_num = DAP_LEDGER_CHECK_INTEGER_OVERFLOW; + break; + } + } break; + default: + continue; + } + + if (l_err_num) + break; + 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_ledger_tokenizer_t); + if ( !l_value_cur ) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + l_err_num = DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY; + break; + } + strcpy(l_value_cur->token_ticker, l_token); + HASH_ADD_STR(l_values_from_cur_tx, token_ticker, l_value_cur); + } + } + if (SUM_256_256(l_value_cur->sum, l_value, &l_value_cur->sum)) { + debug_if(g_debug_ledger, L_WARNING, "Sum result overflow for tx_add_check with ticker %s", + l_value_cur->token_ticker); + l_err_num = DAP_LEDGER_CHECK_INTEGER_OVERFLOW; + break; + } + + // Find token item + dap_ledger_token_item_t *l_token_item = dap_ledger_pvt_find_token(a_ledger, l_token); + if (!l_token_item) { + debug_if(g_debug_ledger, L_WARNING, "Token with ticker %s not found", l_token); + l_err_num = DAP_LEDGER_CHECK_TICKER_NOT_FOUND; + break; + } + // Check permissions + if (dap_ledger_pvt_addr_check(l_token_item, &l_tx_out_to, true) == DAP_LEDGER_CHECK_ADDR_FORBIDDEN) { + debug_if(g_debug_ledger, L_WARNING, "No permission to receive for addr %s", dap_chain_addr_to_str_static(&l_tx_out_to)); + l_err_num = DAP_LEDGER_CHECK_ADDR_FORBIDDEN; + break; + } + if (l_fee_check && dap_chain_addr_compare(&l_tx_out_to, &a_ledger->net->pub.fee_addr) && + !dap_strcmp(l_value_cur->token_ticker, a_ledger->net->pub.native_ticker)) + SUM_256_256(l_fee_sum, l_value, &l_fee_sum); + + if (l_tax_check && dap_chain_addr_compare(&l_tx_out_to, &l_sovereign_addr) && + !dap_strcmp(l_value_cur->token_ticker, a_ledger->net->pub.native_ticker)) + SUM_256_256(l_tax_sum, l_value, &l_tax_sum); + } + + // Check for transaction consistency (sum(ins) == sum(outs)) + if (!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 || !EQUAL_256(l_res->sum, l_value_cur->sum) ) { + if (g_debug_ledger) { + char *l_balance = dap_chain_balance_coins_print(l_res ? l_res->sum : uint256_0); + char *l_balance_cur = dap_chain_balance_coins_print(l_value_cur->sum); + log_it(L_ERROR, "Sum of values of out items from current tx (%s) is not equal outs from previous txs (%s) for token %s", + l_balance, l_balance_cur, l_value_cur->token_ticker); + DAP_DELETE(l_balance); + DAP_DELETE(l_balance_cur); + } + l_err_num = DAP_LEDGER_TX_CHECK_SUM_INS_NOT_EQUAL_SUM_OUTS; + break; + } + } + } + + // 7. Check the network fee + if (!l_err_num && l_fee_check) { + // Check for PoA-cert-signed "service" no-tax tx + if (compare256(l_fee_sum, a_ledger->net->pub.fee_value) == -1 && + !dap_ledger_tx_poa_signed(a_ledger, a_tx)) { + char *l_current_fee = dap_chain_balance_coins_print(l_fee_sum); + char *l_expected_fee = dap_chain_balance_coins_print(a_ledger->net->pub.fee_value); + log_it(L_WARNING, "Fee value is invalid, expected %s pointed %s", l_expected_fee, l_current_fee); + l_err_num = DAP_LEDGER_TX_CHECK_NOT_ENOUGH_FEE; + DAP_DEL_Z(l_current_fee); + DAP_DEL_Z(l_expected_fee); + } + if (l_tax_check && SUBTRACT_256_256(l_taxed_value, l_fee_sum, &l_taxed_value)) { + log_it(L_WARNING, "Fee is greater than sum of inputs"); + l_err_num = DAP_LEDGER_CHECK_INTEGER_OVERFLOW; + } + } + + // 8. Check sovereign tax + if (l_tax_check && !l_err_num) { + uint256_t l_expected_tax = {}; + MULT_256_COIN(l_taxed_value, l_sovereign_tax, &l_expected_tax); + if (compare256(l_tax_sum, l_expected_tax) == -1) { + char *l_current_tax_str = dap_chain_balance_coins_print(l_tax_sum); + char *l_expected_tax_str = dap_chain_balance_coins_print(l_expected_tax); + log_it(L_WARNING, "Tax value is invalid, expected %s pointed %s", l_expected_tax_str, l_current_tax_str); + l_err_num = DAP_LEDGER_TX_CHECK_NOT_ENOUGH_TAX; + DAP_DEL_Z(l_current_tax_str); + DAP_DEL_Z(l_expected_tax_str); + } + } + + if (!l_err_num) { + // TODO move it to service tag deduction + if ( dap_chain_datum_tx_item_get(a_tx, NULL, NULL, TX_ITEM_TYPE_VOTING, NULL ) ) { + if (s_voting_callbacks.voting_callback) { + if ((l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTING, a_tx, a_tx_hash, false))) { + debug_if(g_debug_ledger, L_WARNING, "Verificator check error %d for voting", l_err_num); + l_err_num = DAP_LEDGER_TX_CHECK_VERIFICATOR_CHECK_FAILURE; + } + } else { + debug_if(g_debug_ledger, L_WARNING, "Verificator check error for voting item"); + l_err_num = DAP_LEDGER_TX_CHECK_NO_VERIFICATOR_SET; + } + if (a_tag) + a_tag->uint64 = DAP_CHAIN_TX_TAG_ACTION_VOTING; + } else if ( dap_chain_datum_tx_item_get(a_tx, NULL, NULL, TX_ITEM_TYPE_VOTE, NULL) ) { + if (s_voting_callbacks.voting_callback) { + if ((l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTE, a_tx, a_tx_hash, false))) { + debug_if(g_debug_ledger, L_WARNING, "Verificator check error %d for vote", l_err_num); + l_err_num = DAP_LEDGER_TX_CHECK_VERIFICATOR_CHECK_FAILURE; + } + } else { + debug_if(g_debug_ledger, L_WARNING, "Verificator check error for vote item"); + l_err_num = DAP_LEDGER_TX_CHECK_NO_VERIFICATOR_SET; + } + if (a_tag) + a_tag->uint64 = DAP_CHAIN_TX_TAG_ACTION_VOTE; + } + } + + if (a_main_ticker && !l_err_num) + dap_strncpy(a_main_ticker, l_main_ticker, DAP_CHAIN_TICKER_SIZE_MAX); + + HASH_ITER(hh, l_values_from_prev_tx, l_value_cur, l_tmp) { + HASH_DEL(l_values_from_prev_tx, l_value_cur); + DAP_DELETE(l_value_cur); + } + HASH_ITER(hh, l_values_from_cur_tx, l_value_cur, l_tmp) { + HASH_DEL(l_values_from_cur_tx, l_value_cur); + DAP_DELETE(l_value_cur); + } + if (!a_list_bound_items || l_err_num) { + dap_list_free_full(l_list_bound_items, NULL); + } else { + *a_list_bound_items = l_list_bound_items; + } + + if (!a_list_tx_out || l_err_num) { + dap_list_free(l_list_tx_out); + } else { + *a_list_tx_out = l_list_tx_out; + } + + return l_err_num; +} + +/** + * @brief dap_ledger_tx_check + * @param a_ledger + * @param a_tx + * @return + */ +int dap_ledger_tx_add_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, size_t a_datum_size, dap_hash_fast_t *a_datum_hash) +{ + dap_return_val_if_fail(a_tx && a_ledger, DAP_LEDGER_CHECK_INVALID_ARGS); + + size_t l_tx_size = dap_chain_datum_tx_get_size(a_tx); + if (l_tx_size != a_datum_size) { + log_it (L_WARNING, "Inconsistent datum TX: datum size %zu != tx size %zu", a_datum_size, l_tx_size); + return DAP_LEDGER_CHECK_INVALID_SIZE; + } + int l_ret_check = s_tx_cache_check(a_ledger, a_tx, a_datum_hash, false, NULL, NULL, NULL, NULL, NULL, false); + if(g_debug_ledger) { + if (l_ret_check) + log_it(L_NOTICE, "Ledger TX adding check not passed for TX %s: error %s", + dap_chain_hash_fast_to_str_static(a_datum_hash), dap_ledger_check_error_str(l_ret_check)); + else + log_it(L_INFO, "Ledger TX adding check passed for TX %s", dap_chain_hash_fast_to_str_static(a_datum_hash)); + } + + return l_ret_check; +} + +static struct json_object *s_wallet_info_json_collect(dap_ledger_t *a_ledger, dap_ledger_wallet_balance_t *a_bal) +{ + struct json_object *l_json = json_object_new_object(); + json_object_object_add(l_json, "class", json_object_new_string("Wallet")); + struct json_object *l_network = json_object_new_object(); + json_object_object_add(l_network, "name", json_object_new_string(a_ledger->net->pub.name)); + char *pos = strrchr(a_bal->key, ' '); + if (pos) { + size_t l_addr_len = pos - a_bal->key; + char *l_addr_str = DAP_NEW_STACK_SIZE(char, l_addr_len + 1); + if ( !l_addr_str ) + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + memcpy(l_addr_str, a_bal->key, pos - a_bal->key); + *(l_addr_str + l_addr_len) = '\0'; + json_object_object_add(l_network, "address", json_object_new_string(l_addr_str)); + } else { + json_object_object_add(l_network, "address", json_object_new_string("Unknown")); + } + struct json_object *l_token = json_object_new_object(); + json_object_object_add(l_token, "name", json_object_new_string(a_bal->token_ticker)); + const char *l_balance_coins, *l_balance_datoshi = dap_uint256_to_char(a_bal->balance, &l_balance_coins); + json_object_object_add(l_token, "full_balance", json_object_new_string(l_balance_coins)); + json_object_object_add(l_token, "datoshi", json_object_new_string(l_balance_datoshi)); + json_object_object_add(l_network, "tokens", l_token); + json_object_object_add(l_json, "networks", l_network); + return l_json; +} + +/** + * @brief s_balance_cache_update + * @param a_ledger + * @param a_balance + * @return + */ +static int s_balance_cache_update(dap_ledger_t *a_ledger, dap_ledger_wallet_balance_t *a_balance) +{ + if (PVT(a_ledger)->cached) { + char *l_gdb_group = dap_ledger_get_gdb_group(a_ledger, DAP_LEDGER_BALANCES_STR); + if (dap_global_db_set(l_gdb_group, a_balance->key, &a_balance->balance, sizeof(uint256_t), false, NULL, NULL)) { + debug_if(g_debug_ledger, L_WARNING, "Ledger cache mismatch"); + return -1; + } + DAP_DELETE(l_gdb_group); + } + /* Notify the world*/ + if ( !dap_chain_net_get_load_mode(a_ledger->net) ) { + struct json_object *l_json = s_wallet_info_json_collect(a_ledger, a_balance); + dap_notify_server_send_mt(json_object_get_string(l_json)); + json_object_put(l_json); + } + return 0; +} + +/** + * @brief Add new transaction to the cache list + * @param a_ledger + * @param a_tx + * @param a_tx_hash + * @param a_from_threshold + * @return return 1 OK, -1 error + */ +int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash, bool a_from_threshold, dap_ledger_datum_iter_data_t *a_datum_index_data) +{ + if(!a_tx) { + debug_if(g_debug_ledger, L_ERROR, "NULL tx detected"); + return -1; + } + int l_ret = 0; + dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); + dap_list_t *l_list_bound_items = NULL; + dap_list_t *l_list_tx_out = NULL; + char l_main_token_ticker[DAP_CHAIN_TICKER_SIZE_MAX] = { '\0' }; + + bool l_from_threshold = a_from_threshold; + char l_tx_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE]; + dap_chain_hash_fast_to_str(a_tx_hash, l_tx_hash_str, sizeof(l_tx_hash_str)); + + int l_ret_check; + dap_chain_srv_uid_t l_tag = { .uint64 = 0 }; + dap_chain_tx_tag_action_type_t l_action = DAP_CHAIN_TX_TAG_ACTION_UNKNOWN; + + if( (l_ret_check = s_tx_cache_check(a_ledger, a_tx, a_tx_hash, a_from_threshold, + &l_list_bound_items, &l_list_tx_out, + l_main_token_ticker, &l_tag, &l_action, false))) { + if ((l_ret_check == DAP_CHAIN_CS_VERIFY_CODE_TX_NO_PREVIOUS || + l_ret_check == DAP_CHAIN_CS_VERIFY_CODE_TX_NO_EMISSION) && + l_ledger_pvt->threshold_enabled && !dap_chain_net_get_load_mode(a_ledger->net)) { + if (!l_from_threshold) + dap_ledger_pvt_threshold_txs_add(a_ledger, a_tx, a_tx_hash); + } else { + debug_if(g_debug_ledger, L_WARNING, "dap_ledger_tx_add() tx %s not passed the check: %s ", l_tx_hash_str, + dap_ledger_check_error_str(l_ret_check)); + } + + if ( l_list_bound_items ) + dap_list_free_full(l_list_bound_items, NULL); + + return l_ret_check; + } + debug_if(g_debug_ledger, L_DEBUG, "dap_ledger_tx_add() check passed for tx %s", l_tx_hash_str); + if ( a_datum_index_data != NULL){ + dap_strncpy(a_datum_index_data->token_ticker, l_main_token_ticker, DAP_CHAIN_TICKER_SIZE_MAX); + a_datum_index_data->action = l_action; + a_datum_index_data->uid = l_tag; + } + // Mark 'out' items in cache if they were used & delete previous transactions from cache if it need + // find all bound pairs 'in' and 'out' + size_t l_outs_used = dap_list_length(l_list_bound_items); + + dap_store_obj_t *l_cache_used_outs = NULL; + char *l_ledger_cache_group = NULL; + if (PVT(a_ledger)->cached) { + l_cache_used_outs = DAP_NEW_Z_SIZE(dap_store_obj_t, sizeof(dap_store_obj_t) * (l_outs_used + 1)); + if ( !l_cache_used_outs ) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + l_ret = -1; + goto FIN; + } + l_ledger_cache_group = dap_ledger_get_gdb_group(a_ledger, DAP_LEDGER_TXS_STR); + } + const char *l_cur_token_ticker = NULL; + + // Update balance: deducts + int l_spent_idx = 0; + for (dap_list_t *it = l_list_bound_items; it; it = it->next) { + dap_ledger_tx_bound_t *l_bound_item = it->data; + dap_chain_tx_item_type_t l_type = l_bound_item->type; + if (l_type == TX_ITEM_TYPE_IN || l_type == TX_ITEM_TYPE_IN_COND) { + if (l_bound_item->prev_item->cache_data.n_outs <= l_bound_item->prev_item->cache_data.n_outs_used) { + log_it(L_ERROR, "[!] Irrelevant prev tx: out items mismatch %d <= %d", + l_bound_item->prev_item->cache_data.n_outs, l_bound_item->prev_item->cache_data.n_outs_used); + l_outs_used--; + continue; + } + l_spent_idx++; + } + + if ((l_type == TX_ITEM_TYPE_IN_EMS_LOCK || l_type == TX_ITEM_TYPE_IN_REWARD) && + !dap_ledger_pvt_token_supply_check_update(a_ledger, l_bound_item->token_item, l_bound_item->value, false)) + log_it(L_ERROR, "Insufficient supply for token %s", l_bound_item->token_item->ticker); + + switch (l_type) { + case TX_ITEM_TYPE_IN_EMS: + // Mark it as used with current tx hash + l_bound_item->emission_item->tx_used_out = *a_tx_hash; + dap_ledger_pvt_emission_cache_update(a_ledger, l_bound_item->emission_item); + l_outs_used--; // Do not calc this output with tx used items + continue; + + case TX_ITEM_TYPE_IN_EMS_LOCK: + if (l_bound_item->stake_lock_item) { // Legacy stake lock emission + // Mark it as used with current tx hash + l_bound_item->stake_lock_item->tx_used_out = *a_tx_hash; + s_ledger_stake_lock_cache_update(a_ledger, l_bound_item->stake_lock_item); + } + l_outs_used--; // Do not calc this output with tx used items + continue; + + case TX_ITEM_TYPE_IN_REWARD: { + dap_ledger_reward_item_t *l_item = DAP_NEW_Z(dap_ledger_reward_item_t); + if (!l_item) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + l_ret = -1; + goto FIN; + } + l_item->key = l_bound_item->reward_key; + l_item->spender_tx = *a_tx_hash; + pthread_rwlock_wrlock(&l_ledger_pvt->rewards_rwlock); + HASH_ADD(hh, l_ledger_pvt->rewards, key, sizeof(l_item->key), l_item); + pthread_rwlock_unlock(&l_ledger_pvt->rewards_rwlock); + } + l_outs_used--; // Do not calc this output with tx used items + continue; + + case TX_ITEM_TYPE_IN: { + dap_ledger_wallet_balance_t *wallet_balance = NULL; + l_cur_token_ticker = l_bound_item->in.token_ticker; + const char *l_addr_str = dap_chain_addr_to_str_static(&l_bound_item->in.addr_from); + char *l_wallet_balance_key = dap_strjoin(" ", l_addr_str, l_cur_token_ticker, (char*)NULL); + pthread_rwlock_rdlock(&PVT(a_ledger)->balance_accounts_rwlock); + HASH_FIND_STR(PVT(a_ledger)->balance_accounts, l_wallet_balance_key, wallet_balance); + pthread_rwlock_unlock(&PVT(a_ledger)->balance_accounts_rwlock); + if (wallet_balance) { + debug_if(g_debug_ledger, L_DEBUG, "SPEND %s from addr: %s", + dap_uint256_to_char(l_bound_item->value, NULL), l_wallet_balance_key); + SUBTRACT_256_256(wallet_balance->balance, l_bound_item->value, &wallet_balance->balance); + // Update the cache + s_balance_cache_update(a_ledger, wallet_balance); + } else { + if(g_debug_ledger) + log_it(L_ERROR,"!!! Attempt to SPEND from some non-existent balance !!!: %s %s", l_addr_str, l_cur_token_ticker); + } + + DAP_DELETE(l_wallet_balance_key); + } break; + + case TX_ITEM_TYPE_IN_COND: { // all balance deducts performed with previous conditional transaction + // Update service items if any + dap_ledger_verificator_t *l_verificator = NULL; + int l_tmp = l_bound_item->cond->header.subtype; + pthread_rwlock_rdlock(&s_verificators_rwlock); + HASH_FIND_INT(s_verificators, &l_tmp, l_verificator); + pthread_rwlock_unlock(&s_verificators_rwlock); + if (l_verificator && l_verificator->callback_added) + l_verificator->callback_added(a_ledger, a_tx, a_tx_hash, l_bound_item->cond); + } break; + + default: + log_it(L_ERROR, "Unknown item type %d in ledger TX bound for IN part", l_type); + break; + } + + // add a used output + dap_ledger_tx_item_t *l_prev_item_out = l_bound_item->prev_item; + l_prev_item_out->cache_data.tx_hash_spent_fast[l_bound_item->prev_out_idx] = *a_tx_hash; + l_prev_item_out->cache_data.n_outs_used++; + if (PVT(a_ledger)->cached) { + // mirror it in the cache + size_t l_tx_size = dap_chain_datum_tx_get_size(l_prev_item_out->tx); + size_t l_tx_cache_sz = l_tx_size + sizeof(l_prev_item_out->cache_data); + byte_t *l_tx_cache = DAP_NEW_Z_SIZE(byte_t, l_tx_cache_sz); + memcpy(l_tx_cache, &l_prev_item_out->cache_data, sizeof(l_prev_item_out->cache_data)); + memcpy(l_tx_cache + sizeof(l_prev_item_out->cache_data), l_prev_item_out->tx, l_tx_size); + char *l_tx_i_hash = dap_chain_hash_fast_to_str_new(&l_prev_item_out->tx_hash_fast); + l_cache_used_outs[l_spent_idx] = (dap_store_obj_t) { + .key = l_tx_i_hash, + .value = l_tx_cache, + .value_len = l_tx_cache_sz, + .group = l_ledger_cache_group, + }; + l_cache_used_outs[l_spent_idx].timestamp = dap_nanotime_now(); + } + // mark previous transactions as used with the extra timestamp + if (l_prev_item_out->cache_data.n_outs_used == l_prev_item_out->cache_data.n_outs) + l_prev_item_out->cache_data.ts_spent = a_tx->header.ts_created; + } + + + //Update balance : raise + bool l_multichannel = false; + bool l_cross_network = false; + uint32_t l_outs_count = 0; + for (dap_list_t *l_tx_out = l_list_tx_out; l_tx_out; l_tx_out = l_tx_out->next, l_outs_count++) { + if (!l_tx_out->data) { + log_it(L_ERROR, "Can't detect tx ticker or matching output, can't append balances cache"); + continue; + } + dap_chain_tx_item_type_t l_type = *(uint8_t *)l_tx_out->data; + if (l_type == TX_ITEM_TYPE_OUT_COND) { + // Update service items if any + dap_chain_tx_out_cond_t *l_cond = (dap_chain_tx_out_cond_t *)l_tx_out->data; + dap_ledger_verificator_t *l_verificator = NULL; + int l_tmp = l_cond->header.subtype; + pthread_rwlock_rdlock(&s_verificators_rwlock); + HASH_FIND_INT(s_verificators, &l_tmp, l_verificator); + pthread_rwlock_unlock(&s_verificators_rwlock); + if (l_verificator && l_verificator->callback_added) + l_verificator->callback_added(a_ledger, a_tx, a_tx_hash, NULL); + continue; // balance raise will be with next conditional transaction + } + + dap_chain_addr_t *l_addr = NULL; + uint256_t l_value = {}; + switch (l_type) { + case TX_ITEM_TYPE_OUT: { + dap_chain_tx_out_t *l_out_item_256 = (dap_chain_tx_out_t *)l_tx_out->data; + l_addr = &l_out_item_256->addr; + l_value = l_out_item_256->header.value; + l_cur_token_ticker = l_main_token_ticker; + } break; + case TX_ITEM_TYPE_OUT_OLD: { + dap_chain_tx_out_old_t *l_out_item = (dap_chain_tx_out_old_t *)l_tx_out->data; + l_addr = &l_out_item->addr; + l_value = GET_256_FROM_64(l_out_item->header.value); + l_cur_token_ticker = l_main_token_ticker; + } break; + case TX_ITEM_TYPE_OUT_EXT: { + dap_chain_tx_out_ext_t *l_out_item_ext_256 = (dap_chain_tx_out_ext_t *)l_tx_out->data; + l_addr = &l_out_item_ext_256->addr; + l_value = l_out_item_ext_256->header.value; + l_cur_token_ticker = l_out_item_ext_256->token; + l_multichannel = true; + } break; + default: + log_it(L_ERROR, "Unknown item type %d", l_type); + break; + } + if (!l_addr) + continue; + else if (l_addr->net_id.uint64 != a_ledger->net->pub.id.uint64 && + !dap_chain_addr_is_blank(l_addr)) + l_cross_network = true; + const char *l_addr_str = dap_chain_addr_to_str_static(l_addr); + dap_ledger_wallet_balance_t *wallet_balance = NULL; + char *l_wallet_balance_key = dap_strjoin(" ", l_addr_str, l_cur_token_ticker, (char*)NULL); + debug_if(g_debug_ledger, L_DEBUG, "GOT %s to addr: %s", + dap_uint256_to_char(l_value, NULL), l_wallet_balance_key); + pthread_rwlock_rdlock(&l_ledger_pvt->balance_accounts_rwlock); + HASH_FIND_STR(PVT(a_ledger)->balance_accounts, l_wallet_balance_key, wallet_balance); + pthread_rwlock_unlock(&l_ledger_pvt->balance_accounts_rwlock); + if (wallet_balance) { + //if(g_debug_ledger) + // log_it(L_DEBUG, "Balance item is present in cache"); + SUM_256_256(wallet_balance->balance, l_value, &wallet_balance->balance); + DAP_DELETE (l_wallet_balance_key); + // Update the cache + s_balance_cache_update(a_ledger, wallet_balance); + } else { + wallet_balance = DAP_NEW_Z(dap_ledger_wallet_balance_t); + if (!wallet_balance) { + log_it(L_CRITICAL, "Memory allocation error in s_load_cache_gdb_loaded_txs_callback"); + l_ret = -1; + goto FIN; + } + wallet_balance->key = l_wallet_balance_key; + strcpy(wallet_balance->token_ticker, l_cur_token_ticker); + SUM_256_256(wallet_balance->balance, l_value, &wallet_balance->balance); + if(g_debug_ledger) + log_it(L_DEBUG, "Create new balance item: %s %s", l_addr_str, l_cur_token_ticker); + pthread_rwlock_wrlock(&l_ledger_pvt->balance_accounts_rwlock); + HASH_ADD_KEYPTR(hh, PVT(a_ledger)->balance_accounts, wallet_balance->key, + strlen(l_wallet_balance_key), wallet_balance); + pthread_rwlock_unlock(&l_ledger_pvt->balance_accounts_rwlock); + // Add it to cache + s_balance_cache_update(a_ledger, wallet_balance); + } + } + int l_err_num = 0; + if (s_voting_callbacks.voting_callback) { + if (l_tag.uint64 == DAP_CHAIN_TX_TAG_ACTION_VOTING) + l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTING, a_tx, a_tx_hash, true); + else if (l_tag.uint64 == DAP_CHAIN_TX_TAG_ACTION_VOTE) + l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTE, a_tx, a_tx_hash, true); + } + assert(!l_err_num); + + // add transaction to the cache list + dap_ledger_tx_item_t *l_tx_item = DAP_NEW_Z(dap_ledger_tx_item_t); + if ( !l_tx_item ) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + l_ret = -1; + goto FIN; + } + l_tx_item->tx_hash_fast = *a_tx_hash; + size_t l_tx_size = dap_chain_datum_tx_get_size(a_tx); + l_tx_item->tx = l_ledger_pvt->mapped ? a_tx : DAP_DUP_SIZE(a_tx, l_tx_size); + l_tx_item->cache_data.n_outs = l_outs_count; + l_tx_item->cache_data.tag = l_tag; + l_tx_item->cache_data.action = l_action; + dap_stpcpy(l_tx_item->cache_data.token_ticker, l_main_token_ticker); + + l_tx_item->cache_data.multichannel = l_multichannel; + l_tx_item->ts_added = dap_nanotime_now(); + pthread_rwlock_wrlock(&l_ledger_pvt->ledger_rwlock); + if (dap_chain_net_get_load_mode(a_ledger->net) || dap_chain_net_get_state(a_ledger->net) == NET_STATE_SYNC_CHAINS) + HASH_ADD(hh, l_ledger_pvt->ledger_items, tx_hash_fast, sizeof(dap_chain_hash_fast_t), l_tx_item); + else + HASH_ADD_INORDER(hh, l_ledger_pvt->ledger_items, tx_hash_fast, sizeof(dap_chain_hash_fast_t), + l_tx_item, s_sort_ledger_tx_item); // tx_hash_fast: name of key field + pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); + // Callable callback + dap_list_t *l_notifier; + DL_FOREACH(PVT(a_ledger)->tx_add_notifiers, l_notifier) { + dap_ledger_tx_notifier_t *l_notify = (dap_ledger_tx_notifier_t*)l_notifier->data; + l_notify->callback(l_notify->arg, a_ledger, l_tx_item->tx, DAP_LEDGER_NOTIFY_OPCODE_ADDED); + } + if (l_cross_network) { + dap_list_t *l_notifier; + DL_FOREACH(PVT(a_ledger)->bridged_tx_notifiers, l_notifier) { + dap_ledger_bridged_tx_notifier_t *l_notify = l_notifier->data; + l_notify->callback(a_ledger, a_tx, a_tx_hash, l_notify->arg, DAP_LEDGER_NOTIFY_OPCODE_ADDED); + } + } + if (PVT(a_ledger)->cached) { + // Add it to cache + size_t l_tx_cache_sz = l_tx_size + sizeof(l_tx_item->cache_data); + uint8_t *l_tx_cache = DAP_NEW_STACK_SIZE(uint8_t, l_tx_cache_sz); + memcpy(l_tx_cache, &l_tx_item->cache_data, sizeof(l_tx_item->cache_data)); + memcpy(l_tx_cache + sizeof(l_tx_item->cache_data), a_tx, l_tx_size); + l_cache_used_outs[0] = (dap_store_obj_t) { + .key = l_tx_hash_str, + .value = l_tx_cache, + .value_len = l_tx_cache_sz, + .group = l_ledger_cache_group, + }; + l_cache_used_outs[0].timestamp = dap_nanotime_now(); + // Apply it with single DB transaction + if (dap_global_db_set_raw(l_cache_used_outs, l_outs_used + 1, NULL, NULL)) + debug_if(g_debug_ledger, L_WARNING, "Ledger cache mismatch"); + } + if (!a_from_threshold && l_ledger_pvt->threshold_enabled) + dap_ledger_pvt_threshold_txs_proc(a_ledger); +FIN: + if (l_list_bound_items) + dap_list_free_full(l_list_bound_items, NULL); + if (l_list_tx_out) + dap_list_free(l_list_tx_out); + if (PVT(a_ledger)->cached) { + if (l_cache_used_outs) { + for (size_t i = 1; i <= l_outs_used; i++) { + DAP_DEL_Z(l_cache_used_outs[i].key); + DAP_DEL_Z(l_cache_used_outs[i].value); + } + } + DAP_DEL_Z(l_cache_used_outs); + DAP_DEL_Z(l_ledger_cache_group); + } + return l_ret; +} + +/** + * @brief Remove transaction from the cache list + * @param a_ledger + * @param a_tx + * @param a_tx_hash + * @param a_from_threshold + * @return return 1 OK, -1 error + */ +int dap_ledger_tx_remove(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash) +{ + int l_ret = 0; + dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); + dap_list_t *l_list_bound_items = NULL; + dap_list_t *l_list_tx_out = NULL; + dap_chain_srv_uid_t l_tag = { .uint64 = 0 }; + char l_main_token_ticker[DAP_CHAIN_TICKER_SIZE_MAX] = { '\0' }; + + char l_tx_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE]; + dap_chain_hash_fast_to_str(a_tx_hash, l_tx_hash_str, sizeof(l_tx_hash_str)); + + // Get boundary items list into l_list_bound_items + // Get tx outs list into l_list_tx_out + int l_ret_check; + if( (l_ret_check = s_tx_cache_check(a_ledger, a_tx, a_tx_hash, false, + &l_list_bound_items, &l_list_tx_out, + l_main_token_ticker, &l_tag, NULL, true))) { + debug_if(g_debug_ledger, L_WARNING, "dap_ledger_tx_remove() tx %s not passed the check: %s ", l_tx_hash_str, + dap_ledger_check_error_str(l_ret_check)); + return l_ret_check; + } + + dap_ledger_tx_item_t *l_ledger_item = NULL; + pthread_rwlock_rdlock(&PVT(a_ledger)->ledger_rwlock); + HASH_FIND(hh, PVT(a_ledger)->ledger_items, a_tx_hash, sizeof(dap_chain_hash_fast_t), l_ledger_item); + pthread_rwlock_unlock(&PVT(a_ledger)->ledger_rwlock); + if (l_ledger_item && l_ledger_item->cache_data.n_outs_used != 0) { // transaction already present in the cache list + return DAP_LEDGER_TX_CHECK_OUT_ITEM_ALREADY_USED; + } + + // find all bound pairs 'in' and 'out' + size_t l_outs_used = dap_list_length(l_list_bound_items); + + dap_store_obj_t *l_cache_used_outs = NULL; + char *l_ledger_cache_group = NULL; + if (PVT(a_ledger)->cached) { + l_cache_used_outs = DAP_NEW_Z_SIZE(dap_store_obj_t, sizeof(dap_store_obj_t) * (l_outs_used)); + if ( !l_cache_used_outs ) { + log_it(L_CRITICAL, "Memory allocation error"); + l_ret = -1; + goto FIN; + } + l_ledger_cache_group = dap_ledger_get_gdb_group(a_ledger, DAP_LEDGER_TXS_STR); + } + const char *l_cur_token_ticker = NULL; + + // Update balance : raise all bound items to balances + int l_spent_idx = 0; + for (dap_list_t *it = l_list_bound_items; it; it = it->next) { + dap_ledger_tx_bound_t *l_bound_item = it->data; + dap_chain_tx_item_type_t l_type = l_bound_item->type; + if ((l_type == TX_ITEM_TYPE_IN_EMS_LOCK || l_type == TX_ITEM_TYPE_IN_REWARD) && + !dap_ledger_pvt_token_supply_check_update(a_ledger, l_bound_item->token_item, l_bound_item->value, true)) + log_it(L_ERROR, "Insufficient supply for token %s", l_bound_item->token_item->ticker); + + switch (l_type) { + case TX_ITEM_TYPE_IN_EMS: + // Mark it as unused + memset(&(l_bound_item->emission_item->tx_used_out), 0, sizeof(dap_hash_fast_t)); + dap_ledger_pvt_emission_cache_update(a_ledger, l_bound_item->emission_item); + l_outs_used--; // Do not calc this output with tx used items + continue; + + case TX_ITEM_TYPE_IN_EMS_LOCK: + if (l_bound_item->stake_lock_item) { // Legacy stake lock emission + // Mark it as used with current tx hash + memset(&(l_bound_item->stake_lock_item->tx_used_out), 0, sizeof(dap_hash_fast_t)); + s_ledger_stake_lock_cache_update(a_ledger, l_bound_item->stake_lock_item); + } + l_outs_used--; // Do not calc this output with tx used items + continue; + + case TX_ITEM_TYPE_IN_REWARD: { + dap_ledger_reward_item_t *l_item = NULL; + pthread_rwlock_wrlock(&l_ledger_pvt->rewards_rwlock); + HASH_FIND(hh, l_ledger_pvt->rewards, &l_bound_item->reward_key, sizeof(l_bound_item->reward_key), l_item); + if(l_item){ + HASH_DEL(l_ledger_pvt->rewards, l_item); + DAP_DEL_Z(l_item); + } + pthread_rwlock_unlock(&l_ledger_pvt->rewards_rwlock); + } + l_outs_used--; // Do not calc this output with tx used items + continue; + + case TX_ITEM_TYPE_IN: { + dap_ledger_wallet_balance_t *wallet_balance = NULL; + l_cur_token_ticker = l_bound_item->in.token_ticker; + const char *l_addr_str = dap_chain_addr_to_str_static(&l_bound_item->in.addr_from); + char *l_wallet_balance_key = dap_strjoin(" ", l_addr_str, l_cur_token_ticker, (char*)NULL); + pthread_rwlock_rdlock(&PVT(a_ledger)->balance_accounts_rwlock); + HASH_FIND_STR(PVT(a_ledger)->balance_accounts, l_wallet_balance_key, wallet_balance); + pthread_rwlock_unlock(&PVT(a_ledger)->balance_accounts_rwlock); + if (wallet_balance) { + if(g_debug_ledger) { + char *l_balance = dap_chain_balance_datoshi_print(l_bound_item->value); + log_it(L_DEBUG,"REFUND %s from addr: %s because tx was removed.", l_balance, l_wallet_balance_key); + DAP_DELETE(l_balance); + } + SUM_256_256(wallet_balance->balance, l_bound_item->value, &wallet_balance->balance); + // Update the cache + s_balance_cache_update(a_ledger, wallet_balance); + } else { + if(g_debug_ledger) + log_it(L_ERROR,"!!! Attempt to SPEND from some non-existent balance !!!: %s %s", l_addr_str, l_cur_token_ticker); + } + DAP_DELETE(l_wallet_balance_key); + } break; + + case TX_ITEM_TYPE_IN_COND: { // all balance deducts performed with previous conditional transaction + // Update service items if any + dap_ledger_verificator_t *l_verificator = NULL; + int l_tmp = l_bound_item->cond->header.subtype; + pthread_rwlock_rdlock(&s_verificators_rwlock); + HASH_FIND_INT(s_verificators, &l_tmp, l_verificator); + pthread_rwlock_unlock(&s_verificators_rwlock); + if (l_verificator && l_verificator->callback_deleted) + l_verificator->callback_deleted(a_ledger, a_tx, l_bound_item->cond); + } break; + + default: + log_it(L_ERROR, "Unknown item type %d in ledger TX bound for IN part", l_type); + break; + } + + // add a used output + dap_ledger_tx_item_t *l_prev_item_out = l_bound_item->prev_item; + memset(&(l_prev_item_out->cache_data.tx_hash_spent_fast[l_bound_item->prev_out_idx]), 0, sizeof(dap_hash_fast_t)); + l_prev_item_out->cache_data.n_outs_used--; + if (PVT(a_ledger)->cached) { + // mirror it in the cache + size_t l_tx_size = dap_chain_datum_tx_get_size(l_prev_item_out->tx); + size_t l_tx_cache_sz = l_tx_size + sizeof(l_prev_item_out->cache_data); + byte_t *l_tx_cache = DAP_NEW_Z_SIZE(byte_t, l_tx_cache_sz); + memcpy(l_tx_cache, &l_prev_item_out->cache_data, sizeof(l_prev_item_out->cache_data)); + memcpy(l_tx_cache + sizeof(l_prev_item_out->cache_data), l_prev_item_out->tx, l_tx_size); + char *l_tx_i_hash = dap_chain_hash_fast_to_str_new(&l_prev_item_out->tx_hash_fast); + l_cache_used_outs[l_spent_idx] = (dap_store_obj_t) { + .key = l_tx_i_hash, + .value = l_tx_cache, + .value_len = l_tx_cache_sz, + .group = l_ledger_cache_group + }; + l_cache_used_outs[l_spent_idx].timestamp = 0; + } + // mark previous transactions as used with the extra timestamp + if(l_prev_item_out->cache_data.n_outs_used != l_prev_item_out->cache_data.n_outs) + l_prev_item_out->cache_data.ts_spent = 0; + + if (l_type == TX_ITEM_TYPE_IN || l_type == TX_ITEM_TYPE_IN_COND) { + l_spent_idx++; + } + } + + // Update balance: deducts all outs from balances + bool l_cross_network = false; + for (dap_list_t *l_tx_out = l_list_tx_out; l_tx_out; l_tx_out = dap_list_next(l_tx_out)) { + if (!l_tx_out->data) { + debug_if(g_debug_ledger, L_WARNING, "Can't detect tx ticker or matching output, can't append balances cache"); + continue; + } + dap_chain_tx_item_type_t l_type = *(uint8_t *)l_tx_out->data; + if (l_type == TX_ITEM_TYPE_OUT_COND) { + // Update service items if any + dap_chain_tx_out_cond_t *l_cond = (dap_chain_tx_out_cond_t *)l_tx_out->data; + dap_ledger_verificator_t *l_verificator = NULL; + int l_tmp = l_cond->header.subtype; + pthread_rwlock_rdlock(&s_verificators_rwlock); + HASH_FIND_INT(s_verificators, &l_tmp, l_verificator); + pthread_rwlock_unlock(&s_verificators_rwlock); + if (l_verificator && l_verificator->callback_deleted) + l_verificator->callback_deleted(a_ledger, a_tx, NULL); + continue; // balance raise will be with next conditional transaction + } + + dap_chain_addr_t *l_addr = NULL; + uint256_t l_value = {}; + switch (l_type) { + case TX_ITEM_TYPE_OUT: { + dap_chain_tx_out_t *l_out_item_256 = (dap_chain_tx_out_t *)l_tx_out->data; + l_addr = &l_out_item_256->addr; + l_value = l_out_item_256->header.value; + l_cur_token_ticker = l_main_token_ticker; + } break; + case TX_ITEM_TYPE_OUT_OLD: { + dap_chain_tx_out_old_t *l_out_item = (dap_chain_tx_out_old_t *)l_tx_out->data; + l_addr = &l_out_item->addr; + l_value = GET_256_FROM_64(l_out_item->header.value); + l_cur_token_ticker = l_main_token_ticker; + } break; + case TX_ITEM_TYPE_OUT_EXT: { + dap_chain_tx_out_ext_t *l_out_item_ext_256 = (dap_chain_tx_out_ext_t *)l_tx_out->data; + l_addr = &l_out_item_ext_256->addr; + l_value = l_out_item_ext_256->header.value; + l_cur_token_ticker = l_out_item_ext_256->token; + } break; + default: + log_it(L_DEBUG, "Unknown item type %d", l_type); + break; + } + if (!l_addr) + continue; + else if (l_addr->net_id.uint64 != a_ledger->net->pub.id.uint64 && + !dap_chain_addr_is_blank(l_addr)) + l_cross_network = true; + const char *l_addr_str = dap_chain_addr_to_str_static(l_addr); + dap_ledger_wallet_balance_t *wallet_balance = NULL; + char *l_wallet_balance_key = dap_strjoin(" ", l_addr_str, l_cur_token_ticker, (char*)NULL); + if(g_debug_ledger) { + char *l_balance = dap_chain_balance_datoshi_print(l_value); + log_it(L_DEBUG, "UNDO %s from addr: %s", l_balance, l_wallet_balance_key); + DAP_DELETE(l_balance); + } + pthread_rwlock_rdlock(&l_ledger_pvt->balance_accounts_rwlock); + HASH_FIND_STR(PVT(a_ledger)->balance_accounts, l_wallet_balance_key, wallet_balance); + pthread_rwlock_unlock(&l_ledger_pvt->balance_accounts_rwlock); + if (wallet_balance) { + //if(g_debug_ledger) + // log_it(L_DEBUG, "Balance item is present in cache"); + SUBTRACT_256_256(wallet_balance->balance, l_value, &wallet_balance->balance); + DAP_DELETE (l_wallet_balance_key); + // Update the cache + s_balance_cache_update(a_ledger, wallet_balance); + } else { + log_it(L_CRITICAL, "Wallet is not presented in cache. Can't substract out value from balance."); + } + } + + if (s_voting_callbacks.voting_delete_callback) { + if (l_tag.uint64 == DAP_CHAIN_TX_TAG_ACTION_VOTING) + s_voting_callbacks.voting_delete_callback(a_ledger, TX_ITEM_TYPE_VOTING, a_tx); + else if (l_tag.uint64 == DAP_CHAIN_TX_TAG_ACTION_VOTE) + s_voting_callbacks.voting_delete_callback(a_ledger, TX_ITEM_TYPE_VOTE, a_tx); + } + + // remove transaction from ledger + dap_ledger_tx_item_t *l_tx_item = NULL; + pthread_rwlock_wrlock(&l_ledger_pvt->ledger_rwlock); + HASH_FIND(hh, l_ledger_pvt->ledger_items, a_tx_hash, sizeof(dap_chain_hash_fast_t), l_tx_item); + if (l_tx_item) + HASH_DEL(l_ledger_pvt->ledger_items, l_tx_item); + pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); + + // Callable callback + dap_list_t *l_notifier; + DL_FOREACH(PVT(a_ledger)->tx_add_notifiers, l_notifier) { + dap_ledger_tx_notifier_t *l_notify = (dap_ledger_tx_notifier_t*)l_notifier->data; + l_notify->callback(l_notify->arg, a_ledger, l_tx_item->tx, DAP_LEDGER_NOTIFY_OPCODE_DELETED); + } + if (l_cross_network) { + dap_list_t *l_notifier; + DL_FOREACH(PVT(a_ledger)->bridged_tx_notifiers, l_notifier) { + dap_ledger_bridged_tx_notifier_t *l_notify = l_notifier->data; + l_notify->callback(a_ledger, a_tx, a_tx_hash, l_notify->arg, DAP_LEDGER_NOTIFY_OPCODE_DELETED); + } + } + + if (PVT(a_ledger)->cached) { + // Add it to cache + dap_global_db_del_sync(l_ledger_cache_group, l_tx_hash_str); + // Apply it with single DB transaction + if (dap_global_db_set_raw(l_cache_used_outs, l_outs_used, NULL, NULL)) + debug_if(g_debug_ledger, L_WARNING, "Ledger cache mismatch"); + } +FIN: + if (l_list_bound_items) + dap_list_free_full(l_list_bound_items, NULL); + if (l_list_tx_out) + dap_list_free(l_list_tx_out); + if (PVT(a_ledger)->cached) { + if (l_cache_used_outs) { + for (size_t i = 1; i < l_outs_used; i++) { + DAP_DEL_Z(l_cache_used_outs[i].key); + DAP_DEL_Z(l_cache_used_outs[i].value); + } + } + DAP_DEL_Z(l_cache_used_outs); + DAP_DEL_Z(l_ledger_cache_group); + } + return l_ret; +} + +int dap_ledger_tx_load(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_chain_hash_fast_t *a_tx_hash, dap_ledger_datum_iter_data_t *a_datum_index_data) +{ +#ifndef DAP_LEDGER_TEST + if (dap_chain_net_get_load_mode(a_ledger->net)) { + if (PVT(a_ledger)->cache_tx_check_callback) + PVT(a_ledger)->cache_tx_check_callback(a_ledger, a_tx_hash); + dap_ledger_tx_item_t *l_tx_item = NULL; + unsigned l_hash_value; + HASH_VALUE(a_tx_hash, sizeof(dap_chain_hash_fast_t), l_hash_value); + pthread_rwlock_rdlock(&PVT(a_ledger)->ledger_rwlock); + HASH_FIND_BYHASHVALUE(hh, PVT(a_ledger)->ledger_items, a_tx_hash, sizeof(dap_chain_hash_fast_t), l_hash_value, l_tx_item); + pthread_rwlock_unlock(&PVT(a_ledger)->ledger_rwlock); + if (l_tx_item) + return DAP_LEDGER_CHECK_ALREADY_CACHED; + } +#endif + return dap_ledger_tx_add(a_ledger, a_tx, a_tx_hash, false, a_datum_index_data); +} + + + +static void s_ledger_stake_lock_cache_update(dap_ledger_t *a_ledger, dap_ledger_stake_lock_item_t *a_stake_lock_item) +{ + if (!PVT(a_ledger)->cached) + return; + char l_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE]; + dap_chain_hash_fast_to_str(&a_stake_lock_item->tx_for_stake_lock_hash, l_hash_str, sizeof(l_hash_str)); + char *l_group = dap_ledger_get_gdb_group(a_ledger, DAP_LEDGER_STAKE_LOCK_STR); + if (dap_global_db_set(l_group, l_hash_str, &a_stake_lock_item->tx_used_out, sizeof(dap_hash_fast_t), false, NULL, NULL)) + log_it(L_WARNING, "Ledger cache mismatch"); + DAP_DEL_Z(l_group); +} + +int dap_ledger_emission_for_stake_lock_item_add(dap_ledger_t *a_ledger, const dap_chain_hash_fast_t *a_tx_hash) +{ + dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); + dap_ledger_stake_lock_item_t *l_new_stake_lock_emission = NULL; + pthread_rwlock_rdlock(&l_ledger_pvt->stake_lock_rwlock); + HASH_FIND(hh, l_ledger_pvt->emissions_for_stake_lock, a_tx_hash, sizeof(dap_hash_fast_t), + l_new_stake_lock_emission); + pthread_rwlock_unlock(&l_ledger_pvt->stake_lock_rwlock); + if (l_new_stake_lock_emission) { + return -1; + } + l_new_stake_lock_emission = DAP_NEW_Z(dap_ledger_stake_lock_item_t); + if (!l_new_stake_lock_emission) { + if (g_debug_ledger) { + log_it(L_ERROR, "Error: memory allocation when try adding item 'dap_ledger_stake_lock_item_t' to hash-table"); + } + return -13; + } + l_new_stake_lock_emission->tx_for_stake_lock_hash = *a_tx_hash; + pthread_rwlock_wrlock(&l_ledger_pvt->stake_lock_rwlock); + HASH_ADD(hh, l_ledger_pvt->emissions_for_stake_lock, tx_for_stake_lock_hash, sizeof(dap_chain_hash_fast_t), l_new_stake_lock_emission); + pthread_rwlock_unlock(&l_ledger_pvt->stake_lock_rwlock); + + s_ledger_stake_lock_cache_update(a_ledger, l_new_stake_lock_emission); + + return 0; + +} + +static dap_ledger_stake_lock_item_t *s_emissions_for_stake_lock_item_find(dap_ledger_t *a_ledger, const dap_chain_hash_fast_t *a_token_emission_hash) +{ + dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); + dap_ledger_stake_lock_item_t *l_new_stake_lock_emission = NULL; + pthread_rwlock_rdlock(&l_ledger_pvt->stake_lock_rwlock); + HASH_FIND(hh, l_ledger_pvt->emissions_for_stake_lock, a_token_emission_hash, sizeof(dap_chain_hash_fast_t), + l_new_stake_lock_emission); + pthread_rwlock_unlock(&l_ledger_pvt->stake_lock_rwlock); + return l_new_stake_lock_emission; +} + +/** + * @brief dap_ledger_tx_get_token_ticker_by_hash + * @param a_ledger + * @param a_tx_hash + * @return + */ +const char *dap_ledger_tx_get_token_ticker_by_hash(dap_ledger_t *a_ledger,dap_chain_hash_fast_t *a_tx_hash) +{ + if(!a_ledger || !a_tx_hash) + return NULL; + dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); + + if ( dap_hash_fast_is_blank(a_tx_hash) ) + return NULL; + + dap_ledger_tx_item_t *l_item = NULL; + unsigned l_hash_value; + HASH_VALUE(a_tx_hash, sizeof(*a_tx_hash), l_hash_value); + pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); + HASH_FIND_BYHASHVALUE(hh, l_ledger_pvt->ledger_items, a_tx_hash, sizeof(*a_tx_hash), l_hash_value, l_item); + pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); + return l_item ? l_item->cache_data.token_ticker : NULL; +} + +/** + * Get transaction in the cache by hash + * + * return transaction, or NULL if transaction not found in the cache + */ +static dap_chain_datum_tx_t *s_tx_find_by_hash(dap_ledger_t *a_ledger, const dap_chain_hash_fast_t *a_tx_hash, dap_ledger_tx_item_t **a_item_out, bool a_unspent_only) +{ + if ( !a_tx_hash || dap_hash_fast_is_blank(a_tx_hash) ) + return NULL; + dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); + dap_chain_datum_tx_t *l_tx_ret = NULL; + dap_ledger_tx_item_t *l_tx_item = NULL; + pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); + HASH_FIND(hh, l_ledger_pvt->ledger_items, a_tx_hash, sizeof(dap_chain_hash_fast_t), l_tx_item); + pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); + if(l_tx_item) { + if (!a_unspent_only || !l_tx_item->cache_data.ts_spent) { + l_tx_ret = l_tx_item->tx; + if(a_item_out) + *a_item_out = l_tx_item; + } + } + return l_tx_ret; +} + +dap_chain_datum_tx_t *dap_ledger_tx_find_by_hash(dap_ledger_t *a_ledger, const dap_chain_hash_fast_t *a_tx_hash) +{ + return s_tx_find_by_hash(a_ledger, a_tx_hash, NULL, false); +} + +dap_chain_datum_tx_t *dap_ledger_tx_unspent_find_by_hash(dap_ledger_t *a_ledger, dap_chain_hash_fast_t *a_tx_hash) +{ + return s_tx_find_by_hash(a_ledger, a_tx_hash, NULL, true); +} + +dap_hash_fast_t dap_ledger_get_first_chain_tx_hash(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_chain_tx_out_cond_t *a_cond_out) +{ + dap_hash_fast_t l_hash = { }, l_hash_tmp; + if (!a_ledger || !a_cond_out || !a_tx){ + log_it(L_ERROR, "Argument is NULL in dap_ledger_get_first_chain_tx_hash()"); + return l_hash; + } + dap_chain_datum_tx_t *l_prev_tx = a_tx; + byte_t *l_iter = a_tx->tx_items; + while (( l_iter = dap_chain_datum_tx_item_get(l_prev_tx, NULL, l_iter, TX_ITEM_TYPE_IN_COND, NULL) )) { + l_hash_tmp = ((dap_chain_tx_in_cond_t *)l_iter)->header.tx_prev_hash; + if ( dap_hash_fast_is_blank(&l_hash_tmp) ) + return l_hash_tmp; + if (( l_prev_tx = dap_ledger_tx_find_by_hash(a_ledger, &l_hash_tmp) ) && + ( dap_chain_datum_tx_out_cond_get(l_prev_tx, a_cond_out->header.subtype, NULL) )) { + l_hash = l_hash_tmp; + l_iter = l_prev_tx->tx_items; + } + } + return l_hash; +} + +dap_hash_fast_t dap_ledger_get_final_chain_tx_hash(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_cond_type, dap_chain_hash_fast_t *a_tx_hash) +{ + dap_chain_hash_fast_t l_hash = { }, l_hash_tmp; + if (!a_ledger || !a_tx_hash || dap_hash_fast_is_blank(a_tx_hash)) + return l_hash; + + dap_chain_datum_tx_t *l_tx = NULL; + l_hash = *a_tx_hash; + int l_out_num = 0; + dap_ledger_tx_item_t *l_item = NULL; + while (( l_tx = s_tx_find_by_hash(a_ledger, &l_hash, &l_item, false) )) { + if ( !dap_chain_datum_tx_out_cond_get(l_tx, a_cond_type, &l_out_num) + || dap_hash_fast_is_blank(&l_item->cache_data.tx_hash_spent_fast[l_out_num])) + break; + + l_hash = l_item->cache_data.tx_hash_spent_fast[l_out_num]; + } + return l_hash; +} + +/** + * Check whether used 'out' items (local function) + */ +static bool s_ledger_tx_hash_is_used_out_item(dap_ledger_tx_item_t *a_item, int a_idx_out, dap_hash_fast_t *a_out_spender_hash) +{ + if (!a_item || !a_item->cache_data.n_outs) { + //log_it(L_DEBUG, "list_cached_item is NULL"); + return true; + } + if(a_idx_out >= MAX_OUT_ITEMS) { + if(g_debug_ledger) + log_it(L_ERROR, "Too big index(%d) of 'out' items (max=%d)", a_idx_out, MAX_OUT_ITEMS); + } + assert(a_idx_out < MAX_OUT_ITEMS); + // if there are used 'out' items + if ((a_item->cache_data.n_outs_used > 0) && !dap_hash_fast_is_blank(&(a_item->cache_data.tx_hash_spent_fast[a_idx_out]))) { + if (a_out_spender_hash) + *a_out_spender_hash = a_item->cache_data.tx_hash_spent_fast[a_idx_out]; + return true; + } + return false; +} + +/** + * Check whether used 'out' items + */ +bool dap_ledger_tx_hash_is_used_out_item(dap_ledger_t *a_ledger, dap_chain_hash_fast_t *a_tx_hash, int a_idx_out, dap_hash_fast_t *a_out_spender) +{ + dap_ledger_tx_item_t *l_item_out = NULL; + /*dap_chain_datum_tx_t *l_tx =*/ s_tx_find_by_hash(a_ledger, a_tx_hash, &l_item_out, false); + return l_item_out ? s_ledger_tx_hash_is_used_out_item(l_item_out, a_idx_out, a_out_spender) : true; +} + +void dap_ledger_tx_add_notify(dap_ledger_t *a_ledger, dap_ledger_tx_add_notify_t a_callback, void *a_arg) +{ + dap_return_if_fail(a_ledger && a_callback); + dap_ledger_tx_notifier_t *l_notifier; + DAP_NEW_Z_RET(l_notifier, dap_ledger_tx_notifier_t, NULL); + *l_notifier = (dap_ledger_tx_notifier_t) { .callback = a_callback, .arg = a_arg }; + PVT(a_ledger)->tx_add_notifiers = dap_list_append(PVT(a_ledger)->tx_add_notifiers, l_notifier); +} + +void dap_ledger_bridged_tx_notify_add(dap_ledger_t *a_ledger, dap_ledger_bridged_tx_notify_t a_callback, void *a_arg) +{ + dap_return_if_fail(a_ledger && a_callback); + dap_ledger_bridged_tx_notifier_t *l_notifier; + DAP_NEW_Z_RET(l_notifier, dap_ledger_bridged_tx_notifier_t, NULL); + *l_notifier = (dap_ledger_bridged_tx_notifier_t) { .callback = a_callback, .arg = a_arg }; + PVT(a_ledger)->bridged_tx_notifiers = dap_list_append(PVT(a_ledger)->bridged_tx_notifiers , l_notifier); +} + +const char *dap_ledger_tx_calculate_main_ticker(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, int *a_ledger_rc) +{ + static _Thread_local char s_main_ticker[DAP_CHAIN_TICKER_SIZE_MAX] = { '\0' }; + dap_hash_fast_t l_tx_hash = dap_chain_node_datum_tx_calc_hash(a_tx); + int l_rc = s_tx_cache_check(a_ledger, a_tx, &l_tx_hash, false, NULL, NULL, s_main_ticker, NULL, NULL, false); + if (l_rc == DAP_LEDGER_CHECK_ALREADY_CACHED) + dap_strncpy( s_main_ticker, dap_ledger_tx_get_token_ticker_by_hash(a_ledger, &l_tx_hash), DAP_CHAIN_TICKER_SIZE_MAX ); + if (a_ledger_rc) + *a_ledger_rc = l_rc; + return s_main_ticker; +} + +// Add new verificator callback with associated subtype. Returns 1 if callback replaced, -1 error, overwise returns 0 +int dap_ledger_verificator_add(dap_chain_tx_out_cond_subtype_t a_subtype, dap_ledger_verificator_callback_t a_callback, dap_ledger_updater_callback_t a_callback_added, dap_ledger_delete_callback_t a_callback_deleted) +{ + dap_ledger_verificator_t *l_new_verificator = NULL; + int l_tmp = (int)a_subtype; + pthread_rwlock_rdlock(&s_verificators_rwlock); + HASH_FIND_INT(s_verificators, &l_tmp, l_new_verificator); + pthread_rwlock_unlock(&s_verificators_rwlock); + if (l_new_verificator) { + l_new_verificator->callback = a_callback; + return 1; + } + l_new_verificator = DAP_NEW(dap_ledger_verificator_t); + if (!l_new_verificator) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return -1; + } + l_new_verificator->subtype = (int)a_subtype; + l_new_verificator->callback = a_callback; + l_new_verificator->callback_added = a_callback_added; + l_new_verificator->callback_deleted = a_callback_deleted; + pthread_rwlock_wrlock(&s_verificators_rwlock); + HASH_ADD_INT(s_verificators, subtype, l_new_verificator); + pthread_rwlock_unlock(&s_verificators_rwlock); + return 0; +} + +int dap_ledger_voting_verificator_add(dap_ledger_voting_callback_t a_callback, dap_ledger_voting_delete_callback_t a_callback_delete) +{ + if (!a_callback) + return -1; + + if (!s_voting_callbacks.voting_callback || !s_voting_callbacks.voting_delete_callback){ + s_voting_callbacks.voting_callback = a_callback; + s_voting_callbacks.voting_delete_callback = a_callback_delete; + return 1; + } + + s_voting_callbacks.voting_callback = a_callback; + s_voting_callbacks.voting_delete_callback = a_callback_delete; + return 0; +} + +int dap_ledger_tax_callback_set(dap_ledger_tax_callback_t a_callback) +{ + if (s_tax_callback) + return -1; + s_tax_callback = a_callback; + return 0; +} + +uint256_t dap_ledger_calc_balance_full(dap_ledger_t *a_ledger, const dap_chain_addr_t *a_addr, const char *a_token_ticker) +{ + uint256_t balance = uint256_0; + + if(!a_addr || dap_chain_addr_check_sum(a_addr)) + return balance; + + dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); + dap_ledger_tx_item_t *l_iter_current, *l_item_tmp; + pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); + HASH_ITER(hh, l_ledger_pvt->ledger_items , l_iter_current, l_item_tmp) + { + dap_chain_datum_tx_t *l_cur_tx = l_iter_current->tx; + // Get 'out' items from transaction + int l_out_idx = 0; + byte_t *it; size_t l_size; + TX_ITEM_ITER_TX(it, l_size, l_cur_tx) { + if ( l_out_idx > MAX_OUT_ITEMS ) + return log_it(L_ERROR, "Number of 'out' items exeeds max number %d", MAX_OUT_ITEMS), uint256_0; + uint256_t l_add = { }; + dap_chain_addr_t l_out_addr = { }; + switch (*it) { + case TX_ITEM_TYPE_OUT_OLD: { + dap_chain_tx_out_old_t *l_tx_out = (dap_chain_tx_out_old_t*)it; + l_add = dap_chain_uint256_from(l_tx_out->header.value); + l_out_addr = l_tx_out->addr; + } break; + case TX_ITEM_TYPE_OUT: { + dap_chain_tx_out_t *l_tx_out = (dap_chain_tx_out_t*)it; + l_add = l_tx_out->header.value; + l_out_addr = l_tx_out->addr; + } break; + case TX_ITEM_TYPE_OUT_EXT: { + dap_chain_tx_out_ext_t *l_tx_out = (dap_chain_tx_out_ext_t*)it; + l_add = l_tx_out->header.value; + l_out_addr = l_tx_out->addr; + } break; + case TX_ITEM_TYPE_OUT_COND: + ++l_out_idx; + default: + continue; + } + ++l_out_idx; + if ( !dap_strcmp( a_token_ticker, l_iter_current->cache_data.token_ticker ) // Tokens match + && !dap_chain_addr_compare( a_addr, &l_out_addr ) // Addresses match + && !s_ledger_tx_hash_is_used_out_item( l_iter_current, l_out_idx, NULL ) // Output is unused + && !dap_chain_datum_tx_verify_sign(l_cur_tx) // Signs are valid + ) SUM_256_256(balance, l_add, &balance); + } + } + pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); + return balance; +} diff --git a/modules/ledger/include/dap_chain_ledger.h b/modules/ledger/include/dap_chain_ledger.h index 4c153120a529a5524511fb70f59ec19ca7149180..d8e3ec6c824298a060d144206ee577cd4a1b6e69 100644 --- a/modules/ledger/include/dap_chain_ledger.h +++ b/modules/ledger/include/dap_chain_ledger.h @@ -23,7 +23,6 @@ along with any CellFrame SDK based project. If not, see <http://www.gnu.org/licenses/>. */ - #pragma once #include <stdint.h> #include <stdbool.h> @@ -35,13 +34,13 @@ #include "dap_chain_datum_token.h" #include "dap_chain_datum_tx.h" #include "dap_chain_datum_tx_items.h" +#include "dap_chain_datum_decree.h" +#include "dap_chain_datum_anchor.h" #include "dap_chain_net.h" #define DAP_CHAIN_NET_SRV_TRANSFER_ID 0x07 #define DAP_CHAIN_NET_SRV_BLOCK_REWARD_ID 0x08 -typedef struct dap_ledger_tx_item dap_ledger_tx_item_t; - typedef struct dap_ledger { dap_chain_net_t *net; void *_internal; @@ -61,6 +60,7 @@ typedef enum dap_ledger_check_error { DAP_LEDGER_CHECK_INTEGER_OVERFLOW, DAP_LEDGER_CHECK_NOT_ENOUGH_VALID_SIGNS, DAP_LEDGER_CHECK_TICKER_NOT_FOUND, + DAP_LEDGER_CHECK_INVALID_TICKER, DAP_LEDGER_CHECK_ZERO_VALUE, DAP_LEDGER_CHECK_ADDR_FORBIDDEN, DAP_LEDGER_CHECK_WHITELISTED, @@ -118,6 +118,7 @@ DAP_STATIC_INLINE const char *dap_ledger_check_error_str(dap_ledger_check_error_ case DAP_LEDGER_CHECK_INTEGER_OVERFLOW: return "Incorrect datum values relationship lead to integer overflow, can't process"; case DAP_LEDGER_CHECK_NOT_ENOUGH_VALID_SIGNS: return "No enough valid signatures in datum"; case DAP_LEDGER_CHECK_TICKER_NOT_FOUND: return "Can't find specified token ticker"; + case DAP_LEDGER_CHECK_INVALID_TICKER: return "Specified token ticker is invalid"; case DAP_LEDGER_CHECK_ZERO_VALUE: return "Unacceptable zero value"; case DAP_LEDGER_CHECK_ADDR_FORBIDDEN: return "Specified address is forbidden"; case DAP_LEDGER_CHECK_WHITELISTED: return "Datum is in hard accept list"; @@ -191,7 +192,6 @@ typedef enum dap_chain_tx_tag_action_type { DAP_CHAIN_TX_TAG_ACTION_ALL = ~0, } dap_chain_tx_tag_action_type_t; - typedef struct dap_ledger_datum_iter { dap_chain_net_t *net; dap_chain_datum_tx_t *cur; @@ -213,10 +213,10 @@ typedef void (*dap_ledger_delete_callback_t)(dap_ledger_t *a_ledger, dap_chain_d typedef void (* dap_ledger_tx_add_notify_t)(void *a_arg, dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ledger_notify_opcodes_t a_opcode); typedef void (* dap_ledger_bridged_tx_notify_t)(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash, void *a_arg, dap_ledger_notify_opcodes_t a_opcode); typedef bool (*dap_ledger_cache_tx_check_callback_t)(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_hash); -typedef int (*dap_chain_ledger_voting_callback_t)(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash, bool a_apply); -typedef bool (*dap_chain_ledger_voting_delete_callback_t)(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx); +typedef int (*dap_ledger_voting_callback_t)(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash, bool a_apply); +typedef bool (*dap_ledger_voting_delete_callback_t)(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx); typedef bool (*dap_ledger_tag_check_callback_t)(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_chain_datum_tx_item_groups_t *a_items_grp, dap_chain_tx_tag_action_type_t *a_action); - +typedef bool (*dap_ledger_tax_callback_t)(dap_chain_net_id_t a_net_id, dap_hash_fast_t *a_signer_pkey_hash, dap_chain_addr_t *a_tax_addr, uint256_t *a_tax_value); //Change this UUID to automatically reload ledger cache on next node startup #define DAP_LEDGER_CACHE_RELOAD_ONCE_UUID "0c92b759-a565-448f-b8bd-99103dacf7fc" @@ -257,14 +257,9 @@ void dap_ledger_deinit(); dap_ledger_t *dap_ledger_create(dap_chain_net_t *a_net, uint16_t a_flags); -// Remove dap_ledger_t structure +// Clear & remove dap_ledger_t structure void dap_ledger_handle_free(dap_ledger_t *a_ledger); -// Load ledger from mempool -//int dap_ledger_load(const char *a_net_name, const char *a_chain_name); - -void dap_ledger_set_local_cell_id(dap_ledger_t *a_ledger, dap_chain_cell_id_t a_local_cell_id); - DAP_STATIC_INLINE char *dap_ledger_get_gdb_group(dap_ledger_t *a_ledger, const char *a_suffix) { return a_ledger && a_ledger->net && a_suffix @@ -388,19 +383,14 @@ uint256_t dap_ledger_calc_balance(dap_ledger_t *a_ledger, const dap_chain_addr_t uint256_t dap_ledger_calc_balance_full(dap_ledger_t *a_ledger, const dap_chain_addr_t *a_addr, const char *a_token_ticker); - /** * Get transaction in the cache by hash * * return transaction, or NULL if transaction not found in the cache */ -dap_chain_datum_tx_t *dap_ledger_tx_find_datum_by_hash( dap_ledger_t *a_ledger, const dap_chain_hash_fast_t *a_tx_hash, - dap_ledger_tx_item_t **a_item_out, bool a_unspent_only ); -#define dap_ledger_tx_find_by_hash(a_ledger, a_tx_hash) \ - dap_ledger_tx_find_datum_by_hash(a_ledger, a_tx_hash, NULL, false) -#define dap_ledger_tx_unspent_find_by_hash(a_ledger, a_tx_hash) \ - dap_ledger_tx_find_datum_by_hash(a_ledger, a_tx_hash, NULL, true) - +dap_chain_datum_tx_t *dap_ledger_tx_find_by_hash(dap_ledger_t *a_ledger, const dap_chain_hash_fast_t *a_tx_hash); +dap_chain_datum_tx_t *dap_ledger_tx_unspent_find_by_hash(dap_ledger_t *a_ledger, dap_chain_hash_fast_t *a_tx_hash); + dap_hash_fast_t dap_ledger_get_final_chain_tx_hash(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_cond_type, dap_chain_hash_fast_t *a_tx_hash); dap_hash_fast_t dap_ledger_get_first_chain_tx_hash(dap_ledger_t *a_ledger, dap_chain_datum_tx_t * a_tx, dap_chain_tx_out_cond_t *a_cond_out); @@ -442,7 +432,8 @@ dap_list_t *dap_ledger_get_list_tx_cond_outs(dap_ledger_t *a_ledger, const char int dap_ledger_verificator_add(dap_chain_tx_out_cond_subtype_t a_subtype, dap_ledger_verificator_callback_t a_callback, dap_ledger_updater_callback_t a_callback_added, dap_ledger_delete_callback_t a_callback_deleted); // Add new verificator callback for voting. Returns 1 if callback replaced, overwise returns 0 -int dap_chain_ledger_voting_verificator_add(dap_chain_ledger_voting_callback_t a_callback, dap_chain_ledger_voting_delete_callback_t a_callback_delete); +int dap_ledger_voting_verificator_add(dap_ledger_voting_callback_t a_callback, dap_ledger_voting_delete_callback_t a_callback_delete); +int dap_ledger_tax_callback_set(dap_ledger_tax_callback_t a_callback); // Getting a list of transactions from the ledger. dap_list_t * dap_ledger_get_txs(dap_ledger_t *a_ledger, size_t a_count, size_t a_page, bool a_reverse, bool a_unspent_only); @@ -463,3 +454,20 @@ bool dap_ledger_cache_enabled(dap_ledger_t *a_ledger); void dap_ledger_set_cache_tx_check_callback(dap_ledger_t *a_ledger, dap_ledger_cache_tx_check_callback_t a_callback); dap_chain_tx_out_cond_t* dap_chain_ledger_get_tx_out_cond_linked_to_tx_in_cond(dap_ledger_t *a_ledger, dap_chain_tx_in_cond_t *a_in_cond); void dap_ledger_load_end(dap_ledger_t *a_ledger); + +int dap_ledger_decree_create(dap_chain_net_t *a_net); +void dap_ledger_decree_purge(dap_chain_net_t *a_net); + +uint16_t dap_ledger_decree_get_min_num_of_signers(dap_ledger_t *a_ledger); +uint16_t dap_ledger_decree_get_num_of_owners(dap_ledger_t *a_ledger); +const dap_list_t *dap_ledger_decree_get_owners_pkeys(dap_ledger_t *a_ledger); + +int dap_ledger_decree_apply(dap_hash_fast_t *a_decree_hash, dap_chain_datum_decree_t * a_decree, dap_chain_t *a_chain, bool a_anchored); +int dap_ledger_decree_verify(dap_chain_net_t *a_net, dap_chain_datum_decree_t *a_decree, size_t a_data_size, dap_chain_hash_fast_t *a_decree_hash); +int dap_ledger_decree_load(dap_chain_datum_decree_t * a_decree, dap_chain_t *a_chain, dap_chain_hash_fast_t *a_decree_hash); +dap_chain_datum_decree_t *dap_ledger_decree_get_by_hash(dap_chain_net_t *a_net, dap_hash_fast_t *a_hash, bool *is_applied); +int dap_ledger_decree_reset_applied(dap_chain_net_t *a_net, dap_chain_hash_fast_t *a_decree_hash); + +int dap_ledger_anchor_verify(dap_chain_net_t *a_net, dap_chain_datum_anchor_t * a_anchor, size_t a_data_size); +int dap_ledger_anchor_load(dap_chain_datum_anchor_t * a_anchor, dap_chain_t *a_chain, dap_hash_fast_t *a_anchor_hash); +int dap_ledger_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t *a_chain, dap_hash_fast_t *a_anchor_hash); diff --git a/modules/ledger/include/dap_chain_ledger_pvt.h b/modules/ledger/include/dap_chain_ledger_pvt.h new file mode 100644 index 0000000000000000000000000000000000000000..f2e26b08ac689e99a9aad0a605c7e447babfcfdd --- /dev/null +++ b/modules/ledger/include/dap_chain_ledger_pvt.h @@ -0,0 +1,220 @@ +/* + * Authors: + * Roman Khlopkov <roman.khlopkov@demlabs.net> + * DeM Labs Inc. https://demlabs.net + * Copyright (c) 2024, All rights reserved. + + This file is part of CellFrame SDK the open source project + + CellFrame SDK 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. + + CellFrame SDK 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 CellFrame SDK based project. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include "dap_chain_ledger.h" + +#define MAX_OUT_ITEMS 10 + +enum ledger_permissions { + LEDGER_PERMISSION_RECEIVER_ALLOWED, + LEDGER_PERMISSION_RECEIVER_BLOCKED, + LEDGER_PERMISSION_SENDER_ALLOWED, + LEDGER_PERMISSION_SENDER_BLOCKED +}; + +typedef struct dap_ledger_token_emission_item { + dap_chain_hash_fast_t datum_token_emission_hash; + dap_chain_datum_token_emission_t *datum_token_emission; + size_t datum_token_emission_size; + dap_chain_hash_fast_t tx_used_out; + dap_nanotime_t ts_added; + UT_hash_handle hh; +} dap_ledger_token_emission_item_t; + +typedef struct dap_ledger_token_update_item { + dap_hash_fast_t update_token_hash; + dap_chain_datum_token_t *datum_token_update; + size_t datum_token_update_size; + time_t updated_time; + UT_hash_handle hh; +} dap_ledger_token_update_item_t; + +typedef struct dap_ledger_token_item { + char ticker[DAP_CHAIN_TICKER_SIZE_MAX]; + uint16_t subtype; + dap_chain_datum_token_t *datum_token; + uint64_t datum_token_size; + + uint256_t total_supply; + uint256_t current_supply; + + pthread_rwlock_t token_emissions_rwlock; + dap_ledger_token_emission_item_t * token_emissions; + + pthread_rwlock_t token_ts_updated_rwlock; + dap_ledger_token_update_item_t * token_ts_updated; + time_t last_update_token_time; + + // for auth operations + dap_pkey_t ** auth_pkeys; + dap_chain_hash_fast_t *auth_pkey_hashes; + size_t auth_signs_total; + size_t auth_signs_valid; + uint32_t flags; + dap_chain_addr_t *tx_recv_allow; + size_t tx_recv_allow_size; + dap_chain_addr_t *tx_recv_block; + size_t tx_recv_block_size; + dap_chain_addr_t *tx_send_allow; + size_t tx_send_allow_size; + dap_chain_addr_t *tx_send_block; + size_t tx_send_block_size; + char *description; + // For delegated tokens + bool is_delegated; + char delegated_from[DAP_CHAIN_TICKER_SIZE_MAX]; + uint256_t emission_rate; + + UT_hash_handle hh; +} dap_ledger_token_item_t; + +// ledger cache item - one of unspent outputs +typedef struct dap_ledger_tx_item { + dap_chain_hash_fast_t tx_hash_fast; + dap_chain_datum_tx_t *tx; + dap_nanotime_t ts_added; + struct { + dap_time_t ts_created; // Transation datum timestamp mirrored & cached + uint32_t n_outs; + uint32_t n_outs_used; + char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX]; + byte_t padding[6]; + byte_t multichannel; + dap_time_t ts_spent; + byte_t pad[7]; + dap_chain_srv_uid_t tag; //tag (or service this tx is belong to) + dap_chain_tx_tag_action_type_t action; + // TODO dynamically allocates the memory in order not to limit the number of outputs in transaction + dap_chain_hash_fast_t tx_hash_spent_fast[MAX_OUT_ITEMS]; // spent outs list + } DAP_ALIGN_PACKED cache_data; + UT_hash_handle hh; +} dap_ledger_tx_item_t; + +typedef struct dap_ledger_stake_lock_item { + dap_chain_hash_fast_t tx_for_stake_lock_hash; + dap_chain_hash_fast_t tx_used_out; + UT_hash_handle hh; +} dap_ledger_stake_lock_item_t; + +typedef struct dap_ledger_reward_key { + dap_hash_fast_t block_hash; + dap_hash_fast_t sign_pkey_hash; +} DAP_ALIGN_PACKED dap_ledger_reward_key_t; + +typedef struct dap_ledger_reward_item { + dap_ledger_reward_key_t key; + dap_hash_fast_t spender_tx; + UT_hash_handle hh; +} dap_ledger_reward_item_t; + +// in-memory wallet balance +typedef struct dap_ledger_wallet_balance { + char *key; + char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX]; + uint256_t balance; + UT_hash_handle hh; +} dap_ledger_wallet_balance_t; + +typedef struct dap_ledger_hal_item { + dap_chain_hash_fast_t hash; + UT_hash_handle hh; +} dap_ledger_hal_item_t; + +// private types definition +typedef struct dap_ledger_decree_item { + dap_hash_fast_t key; + bool wait_for_apply, is_applied; + dap_chain_datum_decree_t *decree; + UT_hash_handle hh; +} dap_ledger_decree_item_t; + +typedef struct dap_ledger_anchor_item { + dap_hash_fast_t anchor_hash; + dap_chain_datum_anchor_t *anchor; + UT_hash_handle hh; +} dap_ledger_anchor_item_t; + +// dap_ledger_t private section +typedef struct dap_ledger_private { + // separate access to transactions + pthread_rwlock_t ledger_rwlock; + dap_ledger_tx_item_t *ledger_items; + // separate access to tokens + pthread_rwlock_t tokens_rwlock; + dap_ledger_token_item_t *tokens; + // separate acces to stake items + pthread_rwlock_t stake_lock_rwlock; + dap_ledger_stake_lock_item_t *emissions_for_stake_lock; + // separate access to rewards + pthread_rwlock_t rewards_rwlock; + dap_ledger_reward_item_t *rewards; + // separate access to balances + pthread_rwlock_t balance_accounts_rwlock; + dap_ledger_wallet_balance_t *balance_accounts; + // separate access to threshold + pthread_rwlock_t threshold_txs_rwlock; + dap_ledger_tx_item_t *threshold_txs; + dap_interval_timer_t threshold_txs_free_timer; + // separate access to decrees storage & processing + pthread_rwlock_t decrees_rwlock; + dap_list_t *decree_owners_pkeys; + uint16_t decree_num_of_owners; + uint16_t decree_min_num_of_signers; + dap_ledger_decree_item_t *decrees; + dap_ledger_anchor_item_t *anchors; + + // Save/load operations condition + pthread_mutex_t load_mutex; + pthread_cond_t load_cond; + bool load_end; + // Ledger flags + bool check_ds, check_cells_ds, check_token_emission, cached, mapped, threshold_enabled; + //notifiers + dap_list_t *bridged_tx_notifiers; + dap_list_t *tx_add_notifiers; + dap_ledger_cache_tx_check_callback_t cache_tx_check_callback; + // White- and blacklist + dap_ledger_hal_item_t *hal_items, *hrl_items; +} dap_ledger_private_t; + +#define PVT(a) ( (dap_ledger_private_t *) a->_internal ) + +extern bool g_debug_ledger; + +bool dap_ledger_pvt_cache_gdb_load_tokens_callback(dap_global_db_instance_t *a_dbi, + int a_rc, const char *a_group, + const size_t a_values_total, const size_t a_values_count, + dap_global_db_obj_t *a_values, void *a_arg); +bool dap_ledger_pvt_cache_gdb_load_stake_lock_callback(dap_global_db_instance_t *a_dbi, + int a_rc, const char *a_group, + const size_t a_values_total, const size_t a_values_count, + dap_global_db_obj_t *a_values, void *a_arg); +int dap_ledger_pvt_threshold_txs_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash); +void dap_ledger_pvt_threshold_txs_proc(dap_ledger_t *a_ledger); +dap_ledger_token_item_t *dap_ledger_pvt_find_token(dap_ledger_t *a_ledger, const char *a_token_ticker); +bool dap_ledger_pvt_token_supply_check(dap_ledger_token_item_t *a_token_item, uint256_t a_value); +bool dap_ledger_pvt_token_supply_check_update(dap_ledger_t *a_ledger, dap_ledger_token_item_t *a_token_item, uint256_t a_value, bool a_for_removing); +dap_ledger_token_emission_item_t *dap_ledger_pvt_emission_item_find(dap_ledger_t *a_ledger, + const char *a_token_ticker, const dap_chain_hash_fast_t *a_token_emission_hash, dap_ledger_token_item_t **a_token_item); +dap_ledger_check_error_t dap_ledger_pvt_addr_check(dap_ledger_token_item_t *a_token_item, dap_chain_addr_t *a_addr, bool a_receive); +void dap_ledger_pvt_emission_cache_update(dap_ledger_t *a_ledger, dap_ledger_token_emission_item_t *a_emission_item); diff --git a/modules/ledger/include/dap_chain_net_anchor.h b/modules/ledger/include/dap_chain_net_anchor.h deleted file mode 100644 index cb19ca081108711038833d35e4c6ec31ee455580..0000000000000000000000000000000000000000 --- a/modules/ledger/include/dap_chain_net_anchor.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Authors: - * Frolov Daniil <daniil.frolov@demlabs.net> - * DeM Labs Inc. https://demlabs.net - * Copyright (c) 2020, All rights reserved. - - This file is part of CellFrame SDK the open source project - - CellFrame SDK 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. - - CellFrame SDK 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 CellFrame SDK based project. If not, see <http://www.gnu.org/licenses/>. -*/ -#pragma once - -#include "dap_chain_datum_anchor.h" -#include "dap_chain_net.h" - -int dap_chain_net_anchor_init(); - -int dap_chain_net_anchor_verify(dap_chain_net_t *a_net, dap_chain_datum_anchor_t * a_anchor, size_t a_data_size); -int dap_chain_net_anchor_load(dap_chain_datum_anchor_t * a_anchor, dap_chain_t *a_chain, dap_hash_fast_t *a_anchor_hash); -int dap_chain_net_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t *a_chain, dap_hash_fast_t *a_anchor_hash); \ No newline at end of file diff --git a/modules/ledger/include/dap_chain_net_decree.h b/modules/ledger/include/dap_chain_net_decree.h deleted file mode 100644 index 6cf2e6eff0b9f651d0826fb6b678d18a25ac5f2d..0000000000000000000000000000000000000000 --- a/modules/ledger/include/dap_chain_net_decree.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Authors: - * Frolov Daniil <daniil.frolov@demlabs.net> - * DeM Labs Inc. https://demlabs.net - * Copyright (c) 2020, All rights reserved. - - This file is part of CellFrame SDK the open source project - - CellFrame SDK 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. - - CellFrame SDK 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 CellFrame SDK based project. If not, see <http://www.gnu.org/licenses/>. -*/ -#pragma once - -#include "dap_chain_datum_decree.h" -#include "dap_list.h" -#include "dap_chain_net.h" - -typedef struct dap_chain_net_decree { - dap_list_t *pkeys; - uint16_t num_of_owners; - uint16_t min_num_of_owners; -} dap_chain_net_decree_t; - -int dap_chain_net_decree_init(dap_chain_net_t *a_net); -int dap_chain_net_decree_deinit(dap_chain_net_t *a_net); - -void dap_chain_net_decree_purge(dap_chain_net_t *a_net); - -int dap_chain_net_decree_apply(dap_hash_fast_t *a_decree_hash, dap_chain_datum_decree_t * a_decree, dap_chain_t *a_chain, bool a_anchored); -int dap_chain_net_decree_verify(dap_chain_net_t *a_net, dap_chain_datum_decree_t *a_decree, size_t a_data_size, dap_chain_hash_fast_t *a_decree_hash); -int dap_chain_net_decree_load(dap_chain_datum_decree_t * a_decree, dap_chain_t *a_chain, dap_chain_hash_fast_t *a_decree_hash); -dap_chain_datum_decree_t *dap_chain_net_decree_get_by_hash(dap_chain_net_t *a_net, dap_hash_fast_t *a_hash, bool *is_applied); -int dap_chain_net_decree_reset_applied(dap_chain_net_t *a_net, dap_chain_hash_fast_t *a_decree_hash); diff --git a/modules/net/dap_chain_net.c b/modules/net/dap_chain_net.c index 97aa3c97fd8ca0afc48fbd0ce2fbf6ddeeddb26d..17ea59dac744cd4b888af75358136227dd4f6443 100644 --- a/modules/net/dap_chain_net.c +++ b/modules/net/dap_chain_net.c @@ -84,8 +84,6 @@ #include "dap_chain_net.h" #include "dap_chain_net_node_list.h" #include "dap_chain_net_tx.h" -#include "dap_chain_net_anchor.h" -#include "dap_chain_net_decree.h" #include "dap_chain_net_balancer.h" #include "dap_notify_srv.h" #include "dap_chain_ledger.h" @@ -143,7 +141,7 @@ struct chain_sync_context { * @struct dap_chain_net_pvt * @details Private part of chain_net dap object */ -typedef struct dap_chain_net_pvt{ +typedef struct dap_chain_net_pvt { pthread_t proc_tid; dap_chain_node_role_t node_role; uint32_t flags; @@ -176,9 +174,6 @@ typedef struct dap_chain_net_pvt{ // Block sign rewards history struct block_reward *rewards; - dap_chain_net_decree_t *decree; - decree_table_t *decrees; - anchor_table_t *anchors; } dap_chain_net_pvt_t; #define PVT(a) ((dap_chain_net_pvt_t *)a->pvt) @@ -239,7 +234,6 @@ int dap_chain_net_init() { dap_ledger_init(); dap_chain_ch_init(); - dap_chain_net_anchor_init(); dap_chain_net_ch_init(); dap_chain_node_client_init(); dap_http_ban_list_client_init(); @@ -854,7 +848,6 @@ void dap_chain_net_purge(dap_chain_net_t *l_net) debug_if(s_debug_more, L_DEBUG, "Added atom from treshold"); } } - dap_chain_net_decree_init(l_net); } /** @@ -1971,8 +1964,8 @@ int s_net_init(const char *a_net_name, uint16_t a_acl_idx) } // Services register & configure - dap_chain_srv_start(l_net->pub.id, "eXchange", NULL); // Harcoded core service starting for exchange capability - dap_chain_srv_start(l_net->pub.id, "PoS-delegate", NULL); // Harcoded core service starting for delegated keys storage + dap_chain_srv_start(l_net->pub.id, DAP_CHAIN_NET_SRV_XCHANGE_LITERAL, NULL); // Harcoded core service starting for exchange capability + dap_chain_srv_start(l_net->pub.id, DAP_CHAIN_NET_SRV_STAKE_POS_DELEGATE_LITERAL, NULL); // Harcoded core service starting for delegated keys storage char *l_services_path = dap_strdup_printf("%s/network/%s/services", dap_config_path(), l_net->pub.name); DIR *l_service_cfg_dir = opendir(l_services_path); DAP_DEL_Z(l_services_path); @@ -2030,8 +2023,7 @@ int s_net_init(const char *a_net_name, uint16_t a_acl_idx) // init LEDGER model l_net->pub.ledger = dap_ledger_create(l_net, l_ledger_flags); - // Decrees initializing - dap_chain_net_decree_init(l_net); + return 0; } @@ -2591,9 +2583,9 @@ int dap_chain_net_verify_datum_for_add(dap_chain_t *a_chain, dap_chain_datum_t * case DAP_CHAIN_DATUM_TOKEN_EMISSION: return dap_ledger_token_emission_add_check(l_net->pub.ledger, a_datum->data, a_datum->header.data_size, a_datum_hash); case DAP_CHAIN_DATUM_DECREE: - return dap_chain_net_decree_verify(l_net, (dap_chain_datum_decree_t *)a_datum->data, a_datum->header.data_size, a_datum_hash); + return dap_ledger_decree_verify(l_net, (dap_chain_datum_decree_t *)a_datum->data, a_datum->header.data_size, a_datum_hash); case DAP_CHAIN_DATUM_ANCHOR: { - int l_result = dap_chain_net_anchor_verify(l_net, (dap_chain_datum_anchor_t *)a_datum->data, a_datum->header.data_size); + int l_result = dap_ledger_anchor_verify(l_net, (dap_chain_datum_anchor_t *)a_datum->data, a_datum->header.data_size); if (l_result) return l_result; } @@ -2791,7 +2783,7 @@ int dap_chain_datum_add(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t log_it(L_WARNING, "Corrupted decree, datum size %zd is not equal to size of decree %zd", l_datum_data_size, l_decree_size); return -102; } - return dap_chain_net_decree_load(l_decree, a_chain, a_datum_hash); + return dap_ledger_decree_load(l_decree, a_chain, a_datum_hash); } case DAP_CHAIN_DATUM_ANCHOR: { dap_chain_datum_anchor_t *l_anchor = (dap_chain_datum_anchor_t *)a_datum->data; @@ -2800,7 +2792,7 @@ int dap_chain_datum_add(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t log_it(L_WARNING, "Corrupted anchor, datum size %zd is not equal to size of anchor %zd", l_datum_data_size, l_anchor_size); return -102; } - return dap_chain_net_anchor_load(l_anchor, a_chain, a_datum_hash); + return dap_ledger_anchor_load(l_anchor, a_chain, a_datum_hash); } case DAP_CHAIN_DATUM_TOKEN: return dap_ledger_token_load(l_ledger, a_datum->data, a_datum->header.data_size); @@ -2856,7 +2848,7 @@ int dap_chain_datum_remove(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, siz log_it(L_WARNING, "Corrupted anchor, datum size %zd is not equal to size of anchor %zd", l_datum_data_size, l_anchor_size); return -102; } - return dap_chain_net_anchor_unload(l_anchor, a_chain, a_datum_hash); + return dap_ledger_anchor_unload(l_anchor, a_chain, a_datum_hash); } case DAP_CHAIN_DATUM_TOKEN: return 0; @@ -2944,26 +2936,6 @@ void dap_chain_net_announce_addr(dap_chain_net_t *a_net) } } -dap_chain_net_decree_t *dap_chain_net_get_net_decree(dap_chain_net_t *a_net) { - return a_net ? PVT(a_net)->decree : NULL; -} - -void dap_chain_net_set_net_decree(dap_chain_net_t *a_net, dap_chain_net_decree_t *a_decree) { - if (!a_net) { - log_it(L_ERROR, "Net is not initialized"); - return; - } - PVT(a_net)->decree = a_decree; -} - -decree_table_t **dap_chain_net_get_decrees(dap_chain_net_t *a_net) { - return a_net ? &(PVT(a_net)->decrees) : NULL; -} - -anchor_table_t **dap_chain_net_get_anchors(dap_chain_net_t *a_net) { - return a_net ? &(PVT(a_net)->anchors) : NULL; -} - /*------------------------------------State machine block------------------------------------*/ /** diff --git a/modules/net/include/dap_chain_net.h b/modules/net/include/dap_chain_net.h index da9d511f061786ea21fb5d91fa49accb46585605..470fde1d8c0ab29438ddafb476941c03acce7123 100644 --- a/modules/net/include/dap_chain_net.h +++ b/modules/net/include/dap_chain_net.h @@ -42,9 +42,7 @@ along with any CellFrame SDK based project. If not, see <http://www.gnu.org/lic typedef struct dap_chain_node_client dap_chain_node_client_t; typedef struct dap_ledger dap_ledger_t; -typedef struct dap_chain_net_decree dap_chain_net_decree_t; -typedef struct decree_table decree_table_t; -typedef struct anchor_table anchor_table_t; + typedef enum dap_chain_net_state { NET_STATE_OFFLINE = 0, NET_STATE_LINKS_PREPARE, @@ -230,7 +228,3 @@ enum dap_chain_net_json_rpc_error_list{ DAP_CHAIN_NET_JSON_RPC_NO_POA_CERTS_FOUND_POA_CERTS, DAP_CHAIN_NET_JSON_RPC_UNKNOWN_SUBCOMMANDS }; -dap_chain_net_decree_t *dap_chain_net_get_net_decree(dap_chain_net_t *a_net); -void dap_chain_net_set_net_decree(dap_chain_net_t *a_net, dap_chain_net_decree_t *a_decree); -decree_table_t **dap_chain_net_get_decrees(dap_chain_net_t *a_net); -anchor_table_t **dap_chain_net_get_anchors(dap_chain_net_t *a_net); diff --git a/modules/node-cli/dap_chain_node_cli_cmd_tx.c b/modules/node-cli/dap_chain_node_cli_cmd_tx.c index 42d3feeddea45baff8a0450c40ed1370c379071f..06fb0c8997146a847a11f0f07dd3d8169847b1ea 100644 --- a/modules/node-cli/dap_chain_node_cli_cmd_tx.c +++ b/modules/node-cli/dap_chain_node_cli_cmd_tx.c @@ -42,15 +42,13 @@ #include "dap_chain_datum_anchor.h" #include "dap_chain_node_cli_cmd_tx.h" #include "dap_chain_net_tx.h" -#include "dap_chain_net_decree.h" +#include "dap_chain_ledger.h" #include "dap_chain_mempool.h" #include "dap_math_convert.h" #include "dap_json_rpc_errors.h" #define LOG_TAG "chain_node_cli_cmd_tx" - - /** * @brief s_chain_tx_hash_processed_ht_free * free l_current_hash->hash, l_current_hash, l_hash_processed @@ -1547,7 +1545,7 @@ int cmd_decree(int a_argc, char **a_argv, void **a_str_reply) dap_cert_parse_str_list(l_param_value_str, &l_new_certs, &l_new_certs_count); dap_chain_net_t *l_net = dap_chain_net_by_name(l_net_str); - uint16_t l_min_signs = dap_chain_net_get_net_decree(l_net)->min_num_of_owners; + uint16_t l_min_signs = dap_ledger_decree_get_min_num_of_signers(l_net->pub.ledger); if (l_new_certs_count < l_min_signs) { log_it(L_WARNING,"Number of new certificates is less than minimum owner number."); return -106; @@ -1581,7 +1579,7 @@ int cmd_decree(int a_argc, char **a_argv, void **a_str_reply) return -112; } dap_chain_net_t *l_net = dap_chain_net_by_name(l_net_str); - uint256_t l_owners = GET_256_FROM_64(dap_chain_net_get_net_decree(l_net)->num_of_owners); + uint256_t l_owners = GET_256_FROM_64(dap_ledger_decree_get_num_of_owners(l_net->pub.ledger)); if (compare256(l_new_num_of_owners, l_owners) > 0) { log_it(L_WARNING, "The minimum number of owners is greater than the total number of owners."); dap_list_free_full(l_tsd_list, NULL); @@ -1877,22 +1875,19 @@ int cmd_decree(int a_argc, char **a_argv, void **a_str_reply) return -111; } bool l_applied = false; - dap_chain_datum_decree_t *l_decree = dap_chain_net_decree_get_by_hash(l_net, &l_datum_hash, &l_applied); + dap_chain_datum_decree_t *l_decree = dap_ledger_decree_get_by_hash(l_net, &l_datum_hash, &l_applied); dap_cli_server_cmd_set_reply_text(a_str_reply, "Specified decree is %s in decrees hash-table", l_decree ? (l_applied ? "applied" : "not applied") : "not found"); } break; case CMD_INFO: { dap_string_t *l_str_owner_pkey = dap_string_new(""); - dap_chain_net_decree_t *l_net_decree = dap_chain_net_get_net_decree(l_net); - int i = 1; - for (dap_list_t *l_current_pkey = l_net_decree->pkeys; l_current_pkey; l_current_pkey = l_current_pkey->next){ - dap_pkey_t *l_pkey = (dap_pkey_t*)(l_current_pkey->data); - dap_hash_fast_t l_pkey_hash = {0}; + const dap_list_t *l_decree_pkeys = dap_ledger_decree_get_owners_pkeys(l_net->pub.ledger); + int i = 0; + dap_hash_fast_t l_pkey_hash = {}; + for (const dap_list_t *it = l_decree_pkeys; it; it = it->next) { + dap_pkey_t *l_pkey = it->data; dap_pkey_get_hash(l_pkey, &l_pkey_hash); - char *l_pkey_hash_str = dap_hash_fast_to_str_new(&l_pkey_hash); - dap_string_append_printf(l_str_owner_pkey, "\t%d) %s\n", i, l_pkey_hash_str); - i++; - DAP_DELETE(l_pkey_hash_str); + dap_string_append_printf(l_str_owner_pkey, "\t%d) %s\n", ++i, dap_hash_fast_to_str_static(&l_pkey_hash)); } dap_cli_server_cmd_set_reply_text(a_str_reply, "Decree info:\n" "\tOwners: %d\n" @@ -1900,8 +1895,8 @@ int cmd_decree(int a_argc, char **a_argv, void **a_str_reply) "%s" "\t=====================================================================\n" "\tMin owners for apply decree: %d\n", - l_net_decree->num_of_owners, l_str_owner_pkey->str, - l_net_decree->min_num_of_owners); + dap_ledger_decree_get_num_of_owners(l_net->pub.ledger), l_str_owner_pkey->str, + dap_ledger_decree_get_min_num_of_signers(l_net->pub.ledger)); dap_string_free(l_str_owner_pkey, true); } break; default: diff --git a/modules/service/stake/dap_chain_net_srv_stake_pos_delegate.c b/modules/service/stake/dap_chain_net_srv_stake_pos_delegate.c index ce555f3e3441991b69b03d2a4391b54638f0e2d3..7863f172a9d599f8df162662a3b49cc32f77286f 100644 --- a/modules/service/stake/dap_chain_net_srv_stake_pos_delegate.c +++ b/modules/service/stake/dap_chain_net_srv_stake_pos_delegate.c @@ -25,12 +25,11 @@ #include <math.h> #include "dap_chain_wallet.h" #include "dap_config.h" -#include "dap_string.h" #include "dap_list.h" #include "dap_enc_base58.h" #include "dap_chain_common.h" #include "dap_chain_mempool.h" -#include "dap_chain_net_decree.h" +#include "dap_chain_ledger.h" #include "dap_chain_net_tx.h" #include "dap_chain_srv.h" #include "dap_chain_net_srv_stake_pos_delegate.h" @@ -94,8 +93,9 @@ static bool s_debug_more = false; static void *s_pos_delegate_start(dap_chain_net_id_t a_net_id, dap_config_t UNUSED_ARG *a_config); static void s_pos_delegate_delete(void *a_service_internal); -int s_pos_delegate_purge(dap_chain_net_id_t a_net_id); -json_object *s_pos_delegate_get_fee_validators_json(dap_chain_net_id_t a_net_id); +static int s_pos_delegate_purge(dap_chain_net_id_t a_net_id); +static json_object *s_pos_delegate_get_fee_validators_json(dap_chain_net_id_t a_net_id); +bool s_tax_callback(dap_chain_net_id_t a_net_id, dap_hash_fast_t *a_pkey_hash, dap_chain_addr_t *a_addr_out, uint256_t *a_value_out); static bool s_tag_check_key_delegation(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_chain_datum_tx_item_groups_t *a_items_grp, dap_chain_tx_tag_action_type_t *a_action) { @@ -189,8 +189,9 @@ int dap_chain_net_srv_stake_pos_delegate_init() .purge = s_pos_delegate_purge, .get_fee_descr = s_pos_delegate_get_fee_validators_json }; dap_chain_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_STAKE_POS_DELEGATE_ID }; - dap_chain_srv_add(l_uid, "PoS-delegate", &l_callbacks); - dap_ledger_service_add(l_uid, "pos_delegate", s_tag_check_key_delegation); + dap_chain_srv_add(l_uid, DAP_CHAIN_NET_SRV_STAKE_POS_DELEGATE_LITERAL, &l_callbacks); + dap_ledger_service_add(l_uid, DAP_CHAIN_NET_SRV_STAKE_POS_DELEGATE_LITERAL, s_tag_check_key_delegation); + dap_ledger_tax_callback_set(s_tax_callback); return 0; } @@ -369,8 +370,7 @@ static bool s_srv_stake_is_poa_cert(dap_chain_net_t *a_net, dap_enc_key_t *a_key { bool l_is_poa_cert = false; dap_pkey_t *l_pkey = dap_pkey_from_enc_key(a_key); - dap_list_t *l_pkeys = dap_chain_net_get_net_decree(a_net)->pkeys; - for (dap_list_t *it = l_pkeys; it; it = it->next) + for (dap_list_t *it = a_net->pub.keys; it; it = it->next) if (dap_pkey_compare(l_pkey, (dap_pkey_t *)it->data)) { l_is_poa_cert = true; break; @@ -705,7 +705,7 @@ int dap_chain_net_srv_stake_load_cache(dap_chain_net_t *a_net) return 0; } -int s_pos_delegate_purge(dap_chain_net_id_t a_net_id) +static int s_pos_delegate_purge(dap_chain_net_id_t a_net_id) { dap_ledger_t *l_ledger = dap_chain_net_by_id(a_net_id)->pub.ledger; char *l_gdb_group = dap_ledger_get_gdb_group(l_ledger, DAP_CHAIN_NET_SRV_STAKE_POS_DELEGATE_GDB_GROUP); @@ -3519,7 +3519,7 @@ bool dap_chain_net_srv_stake_get_fee_validators(dap_chain_net_t *a_net, return true; } -json_object *s_pos_delegate_get_fee_validators_json(dap_chain_net_id_t a_net_id) +static json_object *s_pos_delegate_get_fee_validators_json(dap_chain_net_id_t a_net_id) { dap_chain_net_t *l_net = dap_chain_net_by_id(a_net_id); uint256_t l_min = uint256_0, l_max = uint256_0, l_average = uint256_0, l_median = uint256_0; @@ -3592,6 +3592,18 @@ dap_chain_net_srv_stake_item_t *dap_chain_net_srv_stake_check_pkey_hash(dap_chai return NULL; } +bool s_tax_callback(dap_chain_net_id_t a_net_id, dap_hash_fast_t *a_pkey_hash, dap_chain_addr_t *a_addr_out, uint256_t *a_value_out) +{ + dap_chain_net_srv_stake_item_t *l_stake = dap_chain_net_srv_stake_check_pkey_hash(a_net_id, a_pkey_hash); + if (!l_stake || dap_chain_addr_is_blank(&l_stake->sovereign_addr) || IS_ZERO_256(l_stake->sovereign_tax)) + return false; + if (a_addr_out) + *a_addr_out = l_stake->sovereign_addr; + if (a_value_out) + *a_value_out = l_stake->sovereign_tax; + return true; +} + size_t dap_chain_net_srv_stake_get_total_keys(dap_chain_net_id_t a_net_id, size_t *a_in_active_count) { dap_chain_net_srv_stake_t *l_stake_rec = s_srv_stake_by_net_id(a_net_id); diff --git a/modules/service/stake/include/dap_chain_net_srv_stake_pos_delegate.h b/modules/service/stake/include/dap_chain_net_srv_stake_pos_delegate.h index 509667829ca8c17c9da3641502338d9755bc5136..3822df11d6f5ce29e20ed184997c6cfd2c1751e2 100644 --- a/modules/service/stake/include/dap_chain_net_srv_stake_pos_delegate.h +++ b/modules/service/stake/include/dap_chain_net_srv_stake_pos_delegate.h @@ -95,4 +95,4 @@ dap_chain_net_srv_stake_item_t *dap_chain_net_srv_stake_check_pkey_hash(dap_chai uint256_t dap_chain_net_srv_stake_get_total_weight(dap_chain_net_id_t a_net_id, uint256_t *a_locked_weight); size_t dap_chain_net_srv_stake_get_total_keys(dap_chain_net_id_t a_net_id, size_t *a_in_active_count); void dap_chain_net_srv_stake_add_approving_decree_info(dap_chain_datum_decree_t *a_decree, dap_chain_net_t *a_net); -void dap_chain_net_srv_stake_remove_approving_decree_info(dap_chain_net_t *a_net, dap_chain_addr_t *a_signing_addr); \ No newline at end of file +void dap_chain_net_srv_stake_remove_approving_decree_info(dap_chain_net_t *a_net, dap_chain_addr_t *a_signing_addr); diff --git a/modules/service/voting/dap_chain_net_srv_voting.c b/modules/service/voting/dap_chain_net_srv_voting.c index 80a5206dc87911ffe62f6fec6f80ed478e9c6d5b..552ac956659391f19f9ec0be2b87542251d285e7 100644 --- a/modules/service/voting/dap_chain_net_srv_voting.c +++ b/modules/service/voting/dap_chain_net_srv_voting.c @@ -112,7 +112,7 @@ static bool s_tag_check_voting(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_t int dap_chain_net_srv_voting_init() { pthread_rwlock_init(&s_votings_rwlock, NULL); - dap_chain_ledger_voting_verificator_add(s_datum_tx_voting_verification_callback, s_datum_tx_voting_verification_delete_callback); + dap_ledger_voting_verificator_add(s_datum_tx_voting_verification_callback, s_datum_tx_voting_verification_delete_callback); dap_cli_server_cmd_add("voting", s_cli_voting, "Voting commands.", "voting create -net <net_name> -question <\"Question_string\"> -options <\"Option0\", \"Option1\" ... \"OptionN\"> [-expire <voting_expire_time_in_RCF822>] [-max_votes_count <Votes_count>] [-delegated_key_required] [-vote_changing_allowed] -fee <value> -w <fee_wallet_name>\n" "voting vote -net <net_name> -hash <voting_hash> -option_idx <option_index> [-cert <delegate_cert_name>] -fee <value> -w <fee_wallet_name>\n" diff --git a/modules/service/xchange/dap_chain_net_srv_xchange.c b/modules/service/xchange/dap_chain_net_srv_xchange.c index 82cacde47d6b9b3256427a1a8581260396412c60..8c6ea5bd88017d5eff95e548c9a7028ce2ed8cc9 100644 --- a/modules/service/xchange/dap_chain_net_srv_xchange.c +++ b/modules/service/xchange/dap_chain_net_srv_xchange.c @@ -181,7 +181,7 @@ int dap_chain_net_srv_xchange_init() ); dap_chain_static_srv_callbacks_t l_srv_callbacks = { .start = s_callback_start, .decree = s_callback_decree, .get_fee_descr = s_print_fee_json }; - int ret = dap_chain_srv_add(c_dap_chain_net_srv_xchange_uid, "eXchange", &l_srv_callbacks); + int ret = dap_chain_srv_add(c_dap_chain_net_srv_xchange_uid, DAP_CHAIN_NET_SRV_XCHANGE_LITERAL, &l_srv_callbacks); if (ret) { log_it(L_ERROR, "Can't register eXchange service"); return ret; @@ -2561,7 +2561,7 @@ json_object *s_print_fee_json(dap_chain_net_id_t a_net_id) if (dap_chain_net_srv_xchange_get_fee(a_net_id, &l_fee, &l_addr, &l_type)) { const char *l_fee_coins, *l_fee_balance = dap_uint256_to_char(l_fee, &l_fee_coins); json_object *l_jobj_xchange = json_object_new_object(); - json_object_object_add(l_jobj_xchange, "service", json_object_new_string("eXchange")); + json_object_object_add(l_jobj_xchange, "service", json_object_new_string(DAP_CHAIN_NET_SRV_XCHANGE_LITERAL)); json_object_object_add(l_jobj_xchange, "coin", json_object_new_string(l_fee_coins)); json_object_object_add(l_jobj_xchange, "balance", json_object_new_string(l_fee_balance)); json_object_object_add(l_jobj_xchange, "addr", json_object_new_string(dap_chain_addr_to_str_static(&l_addr)));