From a502ba383c8699ed4e3b13260013154c85a6dd39 Mon Sep 17 00:00:00 2001
From: "Constantin P." <papizh.konstantin@demlabs.net>
Date: Thu, 20 Feb 2025 13:01:49 +0700
Subject: [PATCH 01/15] 15244

---
 dap-sdk                              | 2 +-
 modules/ledger/dap_chain_ledger_tx.c | 4 ----
 2 files changed, 1 insertion(+), 5 deletions(-)

diff --git a/dap-sdk b/dap-sdk
index 737ab3c966..d9b03ffb93 160000
--- a/dap-sdk
+++ b/dap-sdk
@@ -1 +1 @@
-Subproject commit 737ab3c9660c0e7133786ef256abfbd4bfec87fb
+Subproject commit d9b03ffb93d4003cb1a33c6470612a7f88ad6005
diff --git a/modules/ledger/dap_chain_ledger_tx.c b/modules/ledger/dap_chain_ledger_tx.c
index 9558151998..674d45ec29 100644
--- a/modules/ledger/dap_chain_ledger_tx.c
+++ b/modules/ledger/dap_chain_ledger_tx.c
@@ -875,10 +875,6 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
         } 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;
-- 
GitLab


From 145016b046e002255226d286ce7a4b0f4ecfb181 Mon Sep 17 00:00:00 2001
From: "daniil.frolov" <daniil.frolov@demlabs.net>
Date: Sat, 25 Jan 2025 06:39:20 +0000
Subject: [PATCH 02/15] 13980

---
 modules/type/blocks/dap_chain_cs_blocks.c     | 44 +++++++++++++-
 .../type/blocks/include/dap_chain_cs_blocks.h |  3 +
 .../type/blocks/tests/dap_chain_blocks_test.c | 57 ++++++++++++++-----
 3 files changed, 88 insertions(+), 16 deletions(-)

diff --git a/modules/type/blocks/dap_chain_cs_blocks.c b/modules/type/blocks/dap_chain_cs_blocks.c
index 253c1ad2dd..53f9415797 100644
--- a/modules/type/blocks/dap_chain_cs_blocks.c
+++ b/modules/type/blocks/dap_chain_cs_blocks.c
@@ -70,7 +70,7 @@ typedef struct dap_chain_cs_blocks_pvt {
 
     // All the blocks are here
     dap_chain_block_cache_t *blocks;
-     _Atomic uint64_t blocks_count;
+    _Atomic uint64_t blocks_count;
 
     // Brnches and forks
     size_t forked_br_cnt;
@@ -93,6 +93,11 @@ typedef struct dap_chain_cs_blocks_pvt {
     uint64_t block_confirm_cnt;
 } dap_chain_cs_blocks_pvt_t;
 
+typedef struct dap_chain_block_fork_resolved_notificator{
+    dap_chain_cs_blocks_callback_fork_resolved_t callback;
+    void *arg;
+} dap_chain_block_fork_resolved_notificator_t;
+
 #define PVT(a) ((dap_chain_cs_blocks_pvt_t *)(a)->_pvt )
 
 #define print_rdlock(blocks) log_it(L_DEBUG, "Try to rdlock, %s, %d, thread_id=%u", __FUNCTION__, __LINE__, dap_gettid());\
@@ -176,6 +181,7 @@ static int s_chain_cs_blocks_new(dap_chain_t * a_chain, dap_config_t * a_chain_c
 static bool s_seed_mode = false;
 static bool s_debug_more = false;
 
+static dap_list_t *s_fork_resolved_notificators = NULL;
 
 /**
  * @brief dap_chain_cs_blocks_init
@@ -391,6 +397,25 @@ dap_chain_block_cache_t * dap_chain_block_cache_get_by_hash(dap_chain_cs_blocks_
     return l_ret;
 }
 
+int dap_chain_block_add_fork_notificator(dap_chain_cs_blocks_callback_fork_resolved_t a_callback, void *a_arg)
+{
+    if (!a_callback)
+        return -100;
+
+    dap_chain_block_fork_resolved_notificator_t *l_notificator = DAP_NEW_Z(dap_chain_block_fork_resolved_notificator_t);
+    if (!l_notificator) {
+        log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+        return -1;
+    }
+
+    l_notificator->arg = a_arg;
+    l_notificator->callback = a_callback;
+
+    s_fork_resolved_notificators = dap_list_append(s_fork_resolved_notificators, l_notificator);
+
+    return 0;
+}
+
 static char *s_blocks_decree_set_reward(dap_chain_net_t *a_net, dap_chain_t *a_chain, uint256_t a_value, dap_cert_t *a_cert)
 {
     dap_return_val_if_fail(a_net && a_cert && a_cert->enc_key &&
@@ -1659,6 +1684,10 @@ static bool s_select_longest_branch(dap_chain_cs_blocks_t * a_blocks, dap_chain_
     }
 
     if (a_main_branch_length < l_longest_branch_length){
+        dap_list_t *l_reverted_blocks_list= NULL;
+        uint64_t l_reverted_blocks_cnt = 0;
+        uint64_t l_main_blocks_cnt = 0;
+
         log_it(L_INFO,"Found new longest branch. Start switching.");
         // Switch branches
         dap_chain_block_forked_branch_atoms_table_t *l_new_forked_branch = NULL;
@@ -1671,8 +1700,11 @@ static bool s_select_longest_branch(dap_chain_cs_blocks_t * a_blocks, dap_chain_
                 l_new_item->block_cache = l_atom;
                 l_new_item->block_hash = l_atom->block_hash;
                 HASH_ADD(hh, l_new_forked_branch, block_hash, sizeof(dap_hash_fast_t), l_new_item);
+                l_reverted_blocks_cnt++;
+                dap_hash_fast_t *l_reverted_block_hash = DAP_DUP_SIZE(&l_atom->block_hash, sizeof(l_atom->block_hash));
+                l_reverted_blocks_list = dap_list_prepend(l_reverted_blocks_list, l_reverted_block_hash);
                 l_atom = l_atom->hh.next;
-            }
+        }
         // Next we must to remove all blocks from main branch and delete all datums in this atoms from storages
         dap_chain_block_forked_branch_atoms_table_t *l_last_new_forked_item = HASH_LAST(l_new_forked_branch);
         for (l_curr_index = 0; l_last_new_forked_item && l_curr_index < a_main_branch_length; l_last_new_forked_item = l_last_new_forked_item->hh.prev, ++l_curr_index){
@@ -1694,7 +1726,15 @@ static bool s_select_longest_branch(dap_chain_cs_blocks_t * a_blocks, dap_chain_
             s_add_atom_datums(l_blocks, l_curr_atom);
             dap_chain_atom_notify(a_blocks->chain, l_curr_atom->block->hdr.cell_id, &l_curr_atom->block_hash, (byte_t*)l_curr_atom->block, l_curr_atom->block_size);
             HASH_DEL(new_main_branch, l_item);
+            l_main_blocks_cnt++;
         }
+        // Notify about branch switching
+        for (dap_list_t *l_temp = s_fork_resolved_notificators; l_temp; l_temp = l_temp->next){
+            dap_chain_block_fork_resolved_notificator_t *l_notificator = (dap_chain_block_fork_resolved_notificator_t*)l_temp->data;
+            l_notificator->callback(l_blocks->chain, a_bcache->block_hash, l_reverted_blocks_list, l_reverted_blocks_cnt, l_main_blocks_cnt, l_notificator->arg);
+        }
+
+        dap_list_free_full(l_reverted_blocks_list, NULL);
         // Next we save pointer to new forked branch (former main branch) instead of it
         l_longest_branch_cache_ptr->forked_branch_atoms = l_new_forked_branch;
         return true;
diff --git a/modules/type/blocks/include/dap_chain_cs_blocks.h b/modules/type/blocks/include/dap_chain_cs_blocks.h
index f04dafebc8..697270d7b3 100644
--- a/modules/type/blocks/include/dap_chain_cs_blocks.h
+++ b/modules/type/blocks/include/dap_chain_cs_blocks.h
@@ -35,6 +35,8 @@ typedef void (*dap_chain_cs_blocks_callback_op_results_t)(dap_chain_cs_blocks_t
 typedef int (*dap_chain_cs_blocks_callback_block_verify_t)(dap_chain_cs_blocks_t *a_cs_blocks, dap_chain_block_t *a_block, dap_hash_fast_t *a_block_hash, size_t a_block_size);
 typedef size_t (*dap_chain_cs_blocks_callback_block_sign_t)(dap_chain_cs_blocks_t *, dap_chain_block_t **, size_t);
 typedef dap_chain_block_t *(*dap_chain_cs_block_move_t)(dap_chain_cs_blocks_t *, size_t *);
+typedef void (*dap_chain_cs_blocks_callback_fork_resolved_t)(dap_chain_t *a_chain, dap_hash_fast_t a_block_before_fork_hash, dap_list_t *a_reverted_blocks, 
+                                                                uint64_t a_reverted_blocks_cnt, uint64_t a_main_blocks_cnt, void * a_arg);
 typedef dap_chain_block_t * (*dap_chain_cs_blocks_callback_block_create_t)(dap_chain_cs_blocks_t *,
                                                                                dap_chain_datum_t *,
                                                                                dap_chain_hash_fast_t *,
@@ -89,6 +91,7 @@ void dap_chain_cs_blocks_deinit();
 dap_chain_block_cache_t *dap_chain_block_cache_get_by_hash(dap_chain_cs_blocks_t *a_blocks, dap_chain_hash_fast_t *a_block_hash);
 
 dap_chain_cs_blocks_hardfork_fees_t *dap_chain_cs_blocks_fees_aggregate(dap_chain_t *a_chain);
+int dap_chain_block_add_fork_notificator(dap_chain_cs_blocks_callback_fork_resolved_t a_callback, void *a_arg);
 
 DAP_STATIC_INLINE char *dap_chain_cs_blocks_get_fee_group(const char *a_net_name)
 {
diff --git a/modules/type/blocks/tests/dap_chain_blocks_test.c b/modules/type/blocks/tests/dap_chain_blocks_test.c
index 1d1c7d7687..c515e951d4 100644
--- a/modules/type/blocks/tests/dap_chain_blocks_test.c
+++ b/modules/type/blocks/tests/dap_chain_blocks_test.c
@@ -6,12 +6,20 @@
 #include "dap_chain_cs_blocks.h"
 #include "dap_chain_cs_esbocs.h"
 #include "dap_chain_cs.h"
-// #include "dap_chain_cs_blocks.h"
 
-dap_hash_fast_t g_last_confirmed_block_hash = {};
-dap_hash_fast_t g_last_notified_block_hash = {};
-int g_confirmed_blocks_counter = 0;
-int g_custom_notify_counter = 0;
+typedef struct {
+    dap_hash_fast_t block_before_fork_hash;
+    dap_list_t *reverted_blocks;
+    uint64_t reverted_blocks_cnt;
+    uint64_t main_blocks_cnt;
+} last_fork_resolved_notification_data_t;
+
+static dap_hash_fast_t s_last_confirmed_block_hash = {};
+static dap_hash_fast_t s_last_notified_block_hash = {};
+static int s_confirmed_blocks_counter = 0;
+static int s_custom_notify_counter = 0;
+
+static last_fork_resolved_notification_data_t s_fork_resolved_arg = {};
 
 typedef struct {
     dap_hash_fast_t *last_notified_hash;
@@ -25,6 +33,21 @@ void callback_notify(void *a_arg, dap_chain_t *a_chain, dap_chain_cell_id_t a_id
     *l_arg->last_notified_hash = *a_atom_hash;
 }
 
+static void *s_callback_list_copy(const void *a_data, UNUSED_ARG void *a_usr_data)
+{
+    return DAP_DUP((dap_hash_fast_t *)a_data);
+}
+
+void callback_fork_resolved_notify(dap_chain_t *a_chain, dap_hash_fast_t a_block_before_fork_hash, dap_list_t *a_reverted_blocks, 
+                                                                uint64_t a_reverted_blocks_cnt, uint64_t a_main_blocks_cnt, void * a_arg)
+{
+    last_fork_resolved_notification_data_t *l_arg = (last_fork_resolved_notification_data_t*)a_arg;
+    
+    l_arg->reverted_blocks = dap_list_copy_deep(a_reverted_blocks, s_callback_list_copy, NULL);
+    l_arg->main_blocks_cnt = a_main_blocks_cnt;
+    l_arg->reverted_blocks_cnt = a_reverted_blocks_cnt;
+    l_arg->block_before_fork_hash = a_block_before_fork_hash;
+}
 
 dap_hash_fast_t dap_chain_block_test_add_new_block (dap_hash_fast_t *a_prev_block_hash, dap_chain_t *a_chain, dap_chain_block_t **a_block, size_t *a_block_size)
 {
@@ -85,14 +108,16 @@ void dap_chain_blocks_test()
 
 
     notify_arg_t *l_arg = DAP_NEW_Z(notify_arg_t);
-    l_arg->cnt = &g_confirmed_blocks_counter;
-    l_arg->last_notified_hash = &g_last_confirmed_block_hash;
+    l_arg->cnt = &s_confirmed_blocks_counter;
+    l_arg->last_notified_hash = &s_last_confirmed_block_hash;
     dap_chain_atom_confirmed_notify_add(l_chain, callback_notify, (void*)l_arg, 0);
     l_arg = DAP_NEW_Z(notify_arg_t);
-    l_arg->cnt = &g_custom_notify_counter;
-    l_arg->last_notified_hash = &g_last_notified_block_hash;
+    l_arg->cnt = &s_custom_notify_counter;
+    l_arg->last_notified_hash = &s_last_notified_block_hash;
     dap_chain_atom_confirmed_notify_add(l_chain, callback_notify, (void*)l_arg, 2);
 
+    dap_chain_block_add_fork_notificator(callback_fork_resolved_notify, &s_fork_resolved_arg);
+
     dap_hash_fast_t l_forked_block_hash = {};
     dap_hash_fast_t l_block_hash = {};
     dap_hash_fast_t l_genesis_block_hash = {};
@@ -138,14 +163,14 @@ void dap_chain_blocks_test()
     l_first_branch_atoms_list = dap_list_append(l_first_branch_atoms_list, l_block_hash_copy);
 
 
-    dap_assert_PIF((g_custom_notify_counter == 1 && dap_hash_fast_compare(&g_last_notified_block_hash, &l_genesis_block_hash)), "Check custom notify: ");
+    dap_assert_PIF((s_custom_notify_counter == 1 && dap_hash_fast_compare(&s_last_notified_block_hash, &l_genesis_block_hash)), "Check custom notify: ");
     
     l_block_hash = dap_chain_block_test_add_new_block (&l_block_hash, l_chain, &l_block_repeat_middle_forked, &l_block_repeat_middle_forked_size);
     l_block_repeat_middle_forked_hash = l_block_hash;
     l_block_hash_copy = DAP_DUP(&l_block_hash);
     l_first_branch_atoms_list = dap_list_append(l_first_branch_atoms_list, l_block_hash_copy);
 
-    dap_assert_PIF((g_custom_notify_counter == 2 && dap_hash_fast_compare(&g_last_notified_block_hash, &l_forked_block_hash)), "Check custom notify: ");
+    dap_assert_PIF((s_custom_notify_counter == 2 && dap_hash_fast_compare(&s_last_notified_block_hash, &l_forked_block_hash)), "Check custom notify: ");
 
     dap_chain_atom_verify_res_t ret_val = l_chain->callback_atom_add(l_chain, (dap_chain_atom_ptr_t)l_block_double_main_branch, l_block_double_main_branch_size, &l_block_double_main_branch_hash, false);
     dap_assert_PIF(ret_val == ATOM_PASS, "Add existing block into middle of main chain. Must be passed: ");
@@ -177,6 +202,8 @@ void dap_chain_blocks_test()
     l_second_branch_atoms_list = dap_list_append(l_second_branch_atoms_list, l_block_hash_copy);
 
     dap_assert_PIF(dap_chain_block_test_compare_chain_hash_lists(l_chain, l_second_branch_atoms_list), "Check branches is switched: ");
+    dap_assert_PIF(dap_hash_fast_compare(&s_fork_resolved_arg.block_before_fork_hash, &l_forked_block_hash) && 
+                    s_fork_resolved_arg.main_blocks_cnt == 3 && s_fork_resolved_arg.reverted_blocks_cnt == 2, "Check branches is switched notification: ");
 
     dap_test_msg("Add block to former main branch");
     l_block_hash = dap_chain_block_test_add_new_block ((dap_hash_fast_t*)dap_list_last(l_first_branch_atoms_list)->data, l_chain, NULL, NULL);
@@ -192,10 +219,12 @@ void dap_chain_blocks_test()
     l_first_branch_atoms_list = dap_list_append(l_first_branch_atoms_list, l_block_hash_copy);
 
     dap_assert_PIF(dap_chain_block_test_compare_chain_hash_lists(l_chain, l_first_branch_atoms_list), "Check branches is switched: ");
+
+
     dap_hash_fast_t l_last_former_main_branch_hash = l_block_hash;
 
     // genesis block must be confirmed, check counter and hash of confirmed block
-    dap_assert_PIF((g_confirmed_blocks_counter == 1 && dap_hash_fast_compare(&g_last_confirmed_block_hash, &l_genesis_block_hash)), "Check confirmed block: ");
+    dap_assert_PIF((s_confirmed_blocks_counter == 1 && dap_hash_fast_compare(&s_last_confirmed_block_hash, &l_genesis_block_hash)), "Check confirmed block: ");
 
     /* ========================== Add second forked branch ======================= */
     dap_test_msg("Add atom to second forked branch...");
@@ -221,7 +250,7 @@ void dap_chain_blocks_test()
     dap_assert_PIF(dap_chain_block_test_compare_chain_hash_lists(l_chain, l_third_branch_atoms_list), "Check branches is switched: ");
 
     // second block must be confirmed, check counter and hash of confirmed block
-    dap_assert_PIF((g_confirmed_blocks_counter == 2 && dap_hash_fast_compare(&g_last_confirmed_block_hash, &l_forked_block_hash)), "Check confirmed block: ");
+    dap_assert_PIF((s_confirmed_blocks_counter == 2 && dap_hash_fast_compare(&s_last_confirmed_block_hash, &l_forked_block_hash)), "Check confirmed block: ");
 
     ret_val = l_chain->callback_atom_add(l_chain, (dap_chain_atom_ptr_t)l_block_repeat_middle_forked, l_block_repeat_middle_forked_size, &l_block_repeat_middle_forked_hash, false);
     dap_assert_PIF(ret_val == ATOM_PASS, "Add existing block into middle of forked chain. Must be passed: ");
@@ -232,7 +261,7 @@ void dap_chain_blocks_test()
     l_third_branch_atoms_list = dap_list_append(l_third_branch_atoms_list, l_block_hash_copy);
 
      // third block must be confirmed, check counter and hash of confirmed block
-    dap_assert_PIF((g_confirmed_blocks_counter == 3 && dap_hash_fast_compare(&g_last_confirmed_block_hash, &l_third_confirmed_block)), "Check confirmed block: ");
+    dap_assert_PIF((s_confirmed_blocks_counter == 3 && dap_hash_fast_compare(&s_last_confirmed_block_hash, &l_third_confirmed_block)), "Check confirmed block: ");
 
     // dap_test_msg("Add new block into former main chain...");
     // size_t l_block_size = 0;
-- 
GitLab


From 93efa6e86fcd402a589571dc09593a87379ef09a Mon Sep 17 00:00:00 2001
From: Roman Khlopkov <roman.khlopkov@demlabs.net>
Date: Tue, 28 Jan 2025 11:27:40 +0000
Subject: [PATCH 03/15] hotfix-15313

---
 modules/node-cli/dap_chain_node_cli_cmd.c | 2 +-
 modules/wallet/dap_chain_wallet.c         | 3 ++-
 modules/wallet/include/dap_chain_wallet.h | 4 ++--
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/modules/node-cli/dap_chain_node_cli_cmd.c b/modules/node-cli/dap_chain_node_cli_cmd.c
index e4d2c80e44..182b76a940 100644
--- a/modules/node-cli/dap_chain_node_cli_cmd.c
+++ b/modules/node-cli/dap_chain_node_cli_cmd.c
@@ -1844,7 +1844,7 @@ int l_arg_index = 1, l_rc, cmd_num = CMD_NONE;
                     l_rc = l_ttl_str ? strtoul(l_ttl_str, NULL, 10) : 60;
 
                     l_rc = cmd_num == CMD_WALLET_ACTIVATE
-                            ? dap_chain_wallet_activate(l_wallet_name, strlen(l_wallet_name), l_pass_str, strlen(l_pass_str), l_rc)
+                            ? dap_chain_wallet_activate(l_wallet_name, strlen(l_wallet_name), NULL, l_pass_str, strlen(l_pass_str), l_rc)
                             : dap_chain_wallet_deactivate (l_wallet_name, strlen(l_wallet_name));
 
                     switch (l_rc) {
diff --git a/modules/wallet/dap_chain_wallet.c b/modules/wallet/dap_chain_wallet.c
index 76b4c24f88..3f55a5f9ec 100644
--- a/modules/wallet/dap_chain_wallet.c
+++ b/modules/wallet/dap_chain_wallet.c
@@ -141,6 +141,7 @@ dap_list_t* dap_chain_wallet_get_local_addr(){
 int     dap_chain_wallet_activate   (
                     const   char    *a_name,
                         ssize_t      a_name_len,
+                    const   char    *a_path,
                     const   char    *a_pass,
                         ssize_t      a_pass_len,
                         unsigned     a_ttl
@@ -198,7 +199,7 @@ char *c_wallets_path;
     /*
      * Check password by open/close BMF Wallet file
     */
-    if ( !(c_wallets_path = (char *) dap_chain_wallet_get_path(g_config)) ) /* No path to wallets - nothing to do */
+    if ( !(c_wallets_path = a_path ? (char *)a_path : (char *) dap_chain_wallet_get_path(g_config)) ) /* No path to wallets - nothing to do */
     {
         memset(l_prec->pass, 0, l_prec->pass_len), l_prec->pass_len = 0;
         return  log_it(L_ERROR, "Wallet's path has been not configured"), -EINVAL;
diff --git a/modules/wallet/include/dap_chain_wallet.h b/modules/wallet/include/dap_chain_wallet.h
index ddee0312db..2f227f550e 100644
--- a/modules/wallet/include/dap_chain_wallet.h
+++ b/modules/wallet/include/dap_chain_wallet.h
@@ -92,7 +92,7 @@ uint256_t dap_chain_wallet_get_balance(dap_chain_wallet_t *a_wallet, dap_chain_n
 
 int dap_chain_wallet_save_file( dap_chain_wallet_t * a_wallet);
 
-int dap_chain_wallet_activate   (const char *a_name, ssize_t a_name_len, const char *a_pass, ssize_t a_pass_len, unsigned a_ttl);
+int dap_chain_wallet_activate   (const char *a_name, ssize_t a_name_len, const char *a_path, const char *a_pass, ssize_t a_pass_len, unsigned a_ttl);
 int dap_chain_wallet_deactivate   (const char *a_name, ssize_t a_name_len);
 
 const char* dap_chain_wallet_check_sign(dap_chain_wallet_t *a_wallet);
@@ -109,4 +109,4 @@ dap_list_t* dap_chain_wallet_get_local_addr();
 
 #ifdef __cplusplus
 }
-#endif
\ No newline at end of file
+#endif
-- 
GitLab


From 72b3f747fe93d3317d4243404be927fbd6b34707 Mon Sep 17 00:00:00 2001
From: "Constantin P." <papizh.konstantin@demlabs.net>
Date: Thu, 30 Jan 2025 13:44:00 +0700
Subject: [PATCH 04/15] ...

---
 modules/wallet/dap_chain_wallet_cache.c | 13 +++----------
 1 file changed, 3 insertions(+), 10 deletions(-)

diff --git a/modules/wallet/dap_chain_wallet_cache.c b/modules/wallet/dap_chain_wallet_cache.c
index 0ba0056cd4..35f07bc4d7 100644
--- a/modules/wallet/dap_chain_wallet_cache.c
+++ b/modules/wallet/dap_chain_wallet_cache.c
@@ -150,16 +150,9 @@ int dap_chain_wallet_cache_init()
                     dap_atom_notify_arg_t *l_arg = DAP_NEW_Z(dap_atom_notify_arg_t);
                     l_arg->chain = l_chain;
                     l_arg->net = l_net;
-                    /* dap_chain_datum_iter_t *l_iter = l_chain->callback_datum_iter_create(l_chain);
-                    for (dap_chain_datum_t *l_datum = l_chain->callback_datum_iter_get_first(l_iter);
-                            l_datum;
-                            l_datum = l_chain->callback_datum_iter_get_next(l_iter)){
-
-                        s_callback_datum_notify(l_arg, l_iter->cur_hash, l_iter->cur_atom_hash, l_datum,  l_iter->cur_size, l_iter->ret_code, l_iter->action, l_iter->uid);
-                    }
-                    l_chain->callback_datum_iter_delete(l_iter); */
-                    dap_chain_add_callback_datum_index_notify(l_chain, s_callback_datum_notify, NULL, l_arg);
-                    dap_chain_add_callback_datum_removed_from_index_notify(l_chain, s_callback_datum_removed_notify, NULL, l_arg);
+                    dap_proc_thread_t *l_pt = dap_proc_thread_get_auto();
+                    dap_chain_add_callback_datum_index_notify(l_chain, s_callback_datum_notify, l_pt, l_arg);
+                    dap_chain_add_callback_datum_removed_from_index_notify(l_chain, s_callback_datum_removed_notify, l_pt, l_arg);
                 }
             }
             l_chain=l_chain->next;
-- 
GitLab


From fd5ddcff50d197fc649f84d967d8e7ea2e0e6ea8 Mon Sep 17 00:00:00 2001
From: "Constantin P." <papizh.konstantin@demlabs.net>
Date: Thu, 20 Feb 2025 17:56:35 +0700
Subject: [PATCH 05/15] Tax check fix

---
 modules/ledger/dap_chain_ledger_tx.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/modules/ledger/dap_chain_ledger_tx.c b/modules/ledger/dap_chain_ledger_tx.c
index 674d45ec29..a639855427 100644
--- a/modules/ledger/dap_chain_ledger_tx.c
+++ b/modules/ledger/dap_chain_ledger_tx.c
@@ -293,7 +293,7 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
     uint256_t l_taxed_value = {};
 
     if(a_tag) dap_ledger_deduct_tx_tag(a_ledger, a_tx, NULL, a_tag, a_action);
-
+    bool l_tax_check = false;
     // 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);
@@ -750,6 +750,7 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
                 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_tax_check = true;
                     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);
@@ -836,10 +837,10 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
             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;
+    dap_chain_addr_t l_sovereign_addr; uint256_t l_sovereign_tax;
+    l_tax_check = 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
     bool l_cross_network = false;
     uint256_t l_value = {}, l_fee_sum = {}, l_tax_sum = {};
-- 
GitLab


From 90adb23f14f65202b8f4dc01e6eab09c26df7d38 Mon Sep 17 00:00:00 2001
From: "Constantin P." <papizh.konstantin@demlabs.net>
Date: Thu, 20 Feb 2025 18:06:06 +0700
Subject: [PATCH 06/15] Wallet cache ported

---
 modules/wallet/dap_chain_wallet_cache.c | 856 ++++++++++--------------
 1 file changed, 351 insertions(+), 505 deletions(-)

diff --git a/modules/wallet/dap_chain_wallet_cache.c b/modules/wallet/dap_chain_wallet_cache.c
index 35f07bc4d7..dfe8a27a44 100644
--- a/modules/wallet/dap_chain_wallet_cache.c
+++ b/modules/wallet/dap_chain_wallet_cache.c
@@ -58,13 +58,13 @@ typedef enum dap_s_wallets_cache_type{
 
 typedef struct dap_wallet_tx_cache_input{
     dap_chain_hash_fast_t tx_prev_hash; 
-    uint32_t tx_out_prev_idx;
+    int tx_out_prev_idx;
     uint256_t value;
 } dap_wallet_tx_cache_input_t;
 
 typedef struct dap_wallet_tx_cache_output{
     void* tx_out;
-    uint32_t tx_out_idx;
+    int tx_out_idx;
 } dap_wallet_tx_cache_output_t;
 
 typedef struct dap_wallet_tx_cache {
@@ -74,16 +74,31 @@ typedef struct dap_wallet_tx_cache {
     char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX];
     bool multichannel;
     int ret_code;
-    dap_chain_srv_uid_t srv_uid; 
+    dap_chain_srv_uid_t srv_uid;
     dap_chain_tx_tag_action_type_t action;
     dap_list_t *tx_wallet_inputs;
     dap_list_t *tx_wallet_outputs;
     UT_hash_handle hh;
 } dap_wallet_tx_cache_t;
 
+typedef struct unspent_cache_hh_key {
+    dap_hash_fast_t tx_hash;
+    int out_idx;
+} DAP_ALIGN_PACKED unspent_cache_hh_key;
+
+typedef struct dap_wallet_cache_unspent_outs {
+    unspent_cache_hh_key key;
+    dap_wallet_tx_cache_output_t *output;
+    char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX];
+    
+    UT_hash_handle hh;
+} dap_wallet_cache_unspent_outs_t;
+
 typedef struct dap_s_wallets_cache {
     dap_chain_addr_t wallet_addr;
     dap_wallet_tx_cache_t *wallet_txs;
+    dap_wallet_cache_unspent_outs_t *unspent_outputs;
+
     _Atomic bool is_loading;
     UT_hash_handle hh;
 } dap_wallet_cache_t;
@@ -95,17 +110,17 @@ typedef struct dap_atom_notify_arg {
 
 static dap_s_wallets_cache_type_t s_wallets_cache_type = DAP_WALLET_CACHE_TYPE_LOCAL;
 static dap_wallet_cache_t *s_wallets_cache = NULL;
-static pthread_rwlock_t s_wallet_cache_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+static pthread_rwlock_t s_wallet_cache_rwlock;
+static bool s_debug_more = false;
 
-static int s_save_tx_into_wallet_cache(dap_chain_t *a_chain, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash, dap_hash_fast_t *a_atom_hash, int a_ret_code, char* a_main_token_ticker,
-                                                dap_chain_srv_uid_t a_srv_uid, dap_chain_tx_tag_action_type_t a_action);
-static int s_save_tx_cache_for_addr(dap_chain_t *a_chain, dap_chain_addr_t *a_addr, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash, dap_hash_fast_t *a_atom_hash, int a_ret_code, char* a_main_token_ticker,
-                                                dap_chain_srv_uid_t a_srv_uid, dap_chain_tx_tag_action_type_t a_action);
+static int s_save_tx_cache_for_addr(dap_chain_t *a_chain, dap_chain_addr_t *a_addr, dap_chain_datum_tx_t *a_tx,
+                                    dap_hash_fast_t *a_tx_hash, dap_hash_fast_t *a_atom_hash, int a_ret_code, char* a_main_token_ticker,
+                                    dap_chain_srv_uid_t a_srv_uid, dap_chain_tx_tag_action_type_t a_action, char a_cache_op);
 static int s_save_cache_for_addr_in_net(dap_chain_net_t *a_net, dap_chain_addr_t *a_addr);
 static void s_callback_datum_notify(void *a_arg, dap_chain_hash_fast_t *a_datum_hash, dap_hash_fast_t *a_atom_hash, void *a_datum, 
                                     size_t a_datum_size, int a_ret_code, uint32_t a_action, 
                                     dap_chain_srv_uid_t a_uid);
-static void s_callback_datum_removed_notify(void *a_arg, dap_chain_hash_fast_t *a_datum_hash);
+static void s_callback_datum_removed_notify(void *a_arg, dap_chain_hash_fast_t *a_datum_hash, dap_chain_datum_t *a_datum);
 static void s_wallet_opened_callback(dap_chain_wallet_t *a_wallet, void *a_arg);
 
 static char * s_wallet_cache_type_to_str(dap_s_wallets_cache_type_t a_type)
@@ -126,20 +141,28 @@ int dap_chain_wallet_cache_init()
 {
     const char *l_walet_cache_type_str = dap_config_get_item_str(g_config, "wallets", "wallets_cache");
     if (l_walet_cache_type_str){
-        if (!dap_strcmp(l_walet_cache_type_str, "disable")) {
+        if (!dap_strcmp(l_walet_cache_type_str, "disable")){
             s_wallets_cache_type = DAP_WALLET_CACHE_TYPE_DISABLED;
-            return log_it(L_WARNING, "Wallet cache is disabled"), 0;
         } else if (!dap_strcmp(l_walet_cache_type_str, "local")){
             s_wallets_cache_type = DAP_WALLET_CACHE_TYPE_LOCAL;
         } else if (!dap_strcmp(l_walet_cache_type_str, "all")){
             s_wallets_cache_type = DAP_WALLET_CACHE_TYPE_ALL;
         } else {
-            log_it( L_WARNING, "Unknown cache type in config. Remains default: %s", s_wallet_cache_type_to_str(s_wallets_cache_type));
+            log_it( L_WARNING, "Unknown cache type in config. Remain default: %s", s_wallet_cache_type_to_str(s_wallets_cache_type));
         }
     }
 
+    s_debug_more = dap_config_get_item_bool_default(g_config,"wallet","debug_more", s_debug_more);
+
+    if (s_wallets_cache_type == DAP_WALLET_CACHE_TYPE_DISABLED){
+        debug_if(s_debug_more, L_DEBUG, "Wallet cache is disabled.");
+        return 0;
+    }
+
     log_it(L_INFO, "Wallet cache type: %s", s_wallet_cache_type_to_str(s_wallets_cache_type));
 
+    pthread_rwlock_init(&s_wallet_cache_rwlock, NULL);
+
     // Add notify callback for all chain with transactions in all nets
     for(dap_chain_net_t *l_net = dap_chain_net_iter_start(); l_net; l_net=dap_chain_net_iter_next(l_net)){
         // Find chain with transactions
@@ -160,7 +183,6 @@ int dap_chain_wallet_cache_init()
         
     }
 
-    
     dap_list_t *l_local_addr_list = dap_chain_wallet_get_local_addr();
     pthread_rwlock_wrlock(&s_wallet_cache_rwlock);
     for(dap_list_t *it = l_local_addr_list; it; it=it->next){
@@ -170,17 +192,16 @@ int dap_chain_wallet_cache_init()
         HASH_FIND(hh, s_wallets_cache, l_addr, sizeof(dap_chain_addr_t), l_wallet_item);
         if (!l_wallet_item){
             l_wallet_item = DAP_NEW_Z(dap_wallet_cache_t);
-            l_wallet_item->wallet_addr = *l_addr;
+            memcpy (&l_wallet_item->wallet_addr, l_addr, sizeof(dap_chain_addr_t));
             HASH_ADD(hh, s_wallets_cache, wallet_addr, sizeof(dap_chain_addr_t), l_wallet_item);
-            log_it(L_DEBUG, "Wallet %s cached", dap_chain_addr_to_str_static(l_addr));
+            debug_if(s_debug_more, L_DEBUG, "Wallet %s saved.", dap_chain_addr_to_str_static(l_addr));
         }
     }
     pthread_rwlock_unlock(&s_wallet_cache_rwlock);
     dap_list_free_full(l_local_addr_list, NULL);
     dap_chain_wallet_add_wallet_opened_notify(s_wallet_opened_callback, NULL);
     dap_chain_wallet_add_wallet_created_notify(s_wallet_opened_callback, NULL);
-        
-    
+
     return 0;
 }
 
@@ -192,13 +213,17 @@ int dap_chain_wallet_cache_deinit()
 
 int dap_chain_wallet_cache_tx_find(dap_chain_addr_t *a_addr, char *a_token, dap_chain_datum_tx_t **a_datum, dap_hash_fast_t *a_tx_hash_curr, int* a_ret_code)
 {
-    if ( s_wallets_cache_type == DAP_WALLET_CACHE_TYPE_DISABLED )
-        return -101;
     dap_wallet_cache_t *l_wallet_item = NULL;
+
+    if (s_wallets_cache_type == DAP_WALLET_CACHE_TYPE_DISABLED){
+        debug_if(s_debug_more, L_DEBUG, "Wallet cache is disabled.");
+        return -101;
+    }
+
     pthread_rwlock_rdlock(&s_wallet_cache_rwlock);
     HASH_FIND(hh, s_wallets_cache, a_addr, sizeof(dap_chain_addr_t), l_wallet_item);
     if (!l_wallet_item || l_wallet_item->is_loading){
-        log_it(L_ERROR, "Can't find wallet with address %s", dap_chain_addr_to_str_static(a_addr));
+        log_it(L_INFO, "Can't find wallet with address %s", dap_chain_addr_to_str_static(a_addr));
         pthread_rwlock_unlock(&s_wallet_cache_rwlock);
         return -101;
     }
@@ -207,7 +232,7 @@ int dap_chain_wallet_cache_tx_find(dap_chain_addr_t *a_addr, char *a_token, dap_
         // find start transaction
         HASH_FIND(hh, l_wallet_item->wallet_txs, a_tx_hash_curr, sizeof(dap_chain_hash_fast_t), l_current_wallet_tx);
         if (!l_current_wallet_tx){
-            log_it(L_ERROR, "Can't find tx %s for address %s", dap_hash_fast_to_str_static(a_tx_hash_curr), dap_chain_addr_to_str_static(a_addr));
+            debug_if(s_debug_more, L_DEBUG, "Can't find tx %s for address %s", dap_hash_fast_to_str_static(a_tx_hash_curr), dap_chain_addr_to_str_static(a_addr));
             pthread_rwlock_unlock(&s_wallet_cache_rwlock);
             return 0;
         }
@@ -274,17 +299,20 @@ int dap_chain_wallet_cache_tx_find(dap_chain_addr_t *a_addr, char *a_token, dap_
 int dap_chain_wallet_cache_tx_find_in_history(dap_chain_addr_t *a_addr, char **a_token, int* a_ret_code, dap_chain_tx_tag_action_type_t *a_action,
                                     dap_chain_srv_uid_t *a_uid, dap_chain_datum_tx_t **a_datum, dap_hash_fast_t *a_tx_hash_curr)
 {
-    if ( s_wallets_cache_type == DAP_WALLET_CACHE_TYPE_DISABLED )
-        return -101;
     dap_wallet_cache_t *l_wallet_item = NULL;
 
+    if (s_wallets_cache_type == DAP_WALLET_CACHE_TYPE_DISABLED){
+        debug_if(s_debug_more, L_DEBUG, "Wallet cache is disabled.");
+        return -101;
+    }
+
     if (!a_tx_hash_curr)
         return -100;
 
     pthread_rwlock_rdlock(&s_wallet_cache_rwlock);
     HASH_FIND(hh, s_wallets_cache, a_addr, sizeof(dap_chain_addr_t), l_wallet_item);
     if (!l_wallet_item || l_wallet_item->is_loading){
-        log_it(L_ERROR, "Can't find wallet with address %s", dap_chain_addr_to_str_static(a_addr));
+        debug_if(s_debug_more, L_DEBUG, "Can't find wallet with address %s", dap_chain_addr_to_str_static(a_addr));
         pthread_rwlock_unlock(&s_wallet_cache_rwlock);
         return -101;
     }
@@ -303,6 +331,7 @@ int dap_chain_wallet_cache_tx_find_in_history(dap_chain_addr_t *a_addr, char **a
         l_current_wallet_tx = l_wallet_item->wallet_txs;
     }
 
+
     // Go iterate wallet txs
     if (l_current_wallet_tx){
         // Now work with it
@@ -320,9 +349,10 @@ int dap_chain_wallet_cache_tx_find_in_history(dap_chain_addr_t *a_addr, char **a
         pthread_rwlock_unlock(&s_wallet_cache_rwlock);
         return 0;
     }
-        
+    
+    
     if (a_tx_hash_curr)
-        *a_tx_hash_curr = (dap_hash_fast_t) { };
+        memset(a_tx_hash_curr, 0, sizeof(*a_tx_hash_curr));
     if (a_datum)
         *a_datum = NULL;
     if(a_ret_code)
@@ -337,11 +367,10 @@ int dap_chain_wallet_cache_tx_find_in_history(dap_chain_addr_t *a_addr, char **a
     return 0;
 }
 
-int dap_chain_wallet_cache_tx_find_outs_with_val(dap_chain_net_t *a_net, const char *a_token_ticker, const dap_chain_addr_t *a_addr, 
-                                                    dap_list_t **a_outs_list, uint256_t a_value_need, uint256_t *a_value_transfer)
+int dap_chain_wallet_cache_tx_find_outs(dap_chain_net_t *a_net, const char *a_token_ticker, const dap_chain_addr_t *a_addr, 
+                                                    dap_list_t **a_outs_list, uint256_t *a_value_transfer)
 {
-    if ( s_wallets_cache_type == DAP_WALLET_CACHE_TYPE_DISABLED )
-        return -101;
+
     dap_list_t *l_list_used_out = NULL; // list of transaction with 'out' items
     uint256_t l_value_transfer = { };
     dap_chain_datum_tx_t *l_tx;
@@ -356,11 +385,6 @@ int dap_chain_wallet_cache_tx_find_outs_with_val(dap_chain_net_t *a_net, const c
         return -100;
     }
 
-    if (IS_ZERO_256(a_value_need)){
-        log_it(L_ERROR, "Needed value is zero.");
-        return -100;
-    }
-
     if (a_outs_list == NULL){
         log_it(L_ERROR, "a_outs_list is NULL");
         return -100;
@@ -369,129 +393,74 @@ int dap_chain_wallet_cache_tx_find_outs_with_val(dap_chain_net_t *a_net, const c
     dap_wallet_cache_t *l_wallet_item = NULL;
     pthread_rwlock_rdlock(&s_wallet_cache_rwlock);
     HASH_FIND(hh, s_wallets_cache, a_addr, sizeof(dap_chain_addr_t), l_wallet_item);
-    if (!l_wallet_item || l_wallet_item->is_loading){
-        log_it(L_ERROR, "Can't find wallet with address %s", dap_chain_addr_to_str_static(a_addr));
+    if (!l_wallet_item|| l_wallet_item->is_loading){
+        debug_if(s_debug_more, L_DEBUG, "Can't find wallet with address %s", dap_chain_addr_to_str_static(a_addr));
         pthread_rwlock_unlock(&s_wallet_cache_rwlock);
         return -101;
     }
-    dap_wallet_tx_cache_t *l_current_wallet_tx = l_wallet_item->wallet_txs;
 
-    // Go iterate wallet txs
-    dap_wallet_tx_cache_t *l_current_wallet_tx_iter = NULL, *l_tmp = NULL;
-    HASH_ITER(hh, l_current_wallet_tx, l_current_wallet_tx_iter, l_tmp) {
-        if (l_current_wallet_tx_iter->ret_code != DAP_LEDGER_CHECK_OK)
-            continue;
+    dap_wallet_cache_unspent_outs_t *l_item_cur = NULL, *l_tmp = NULL;
+    HASH_ITER(hh, l_wallet_item->unspent_outputs, l_item_cur, l_tmp){
 
-        
-        if (*l_current_wallet_tx_iter->token_ticker &&
-            dap_strcmp(l_current_wallet_tx_iter->token_ticker, a_token_ticker) &&
-            !l_current_wallet_tx_iter->multichannel)
+        if (dap_strcmp(l_item_cur->token_ticker, a_token_ticker))
             continue;
-        else if (*l_current_wallet_tx_iter->token_ticker && dap_strcmp(l_current_wallet_tx_iter->token_ticker, a_token_ticker) &&
-                    l_current_wallet_tx_iter->multichannel){
-
-            bool skip = true;
-            for (dap_list_t *l_temp = l_current_wallet_tx_iter->tx_wallet_outputs; l_temp; l_temp=l_temp->next){
-                dap_wallet_tx_cache_output_t *l_out_cur = (dap_wallet_tx_cache_output_t*)l_temp->data;
-                dap_chain_tx_item_type_t l_type = *(dap_chain_tx_item_type_t*)l_out_cur->tx_out;
-                uint256_t l_value = { };
-                switch (l_type) {
-                case TX_ITEM_TYPE_OUT_EXT: {
-                    dap_chain_tx_out_ext_t *l_out_ext = (dap_chain_tx_out_ext_t*)l_out_cur->tx_out;
-                    if (dap_strcmp(l_out_ext->token, a_token_ticker))
-                        continue;
-                    if (IS_ZERO_256(l_out_ext->header.value) )
-                        continue;
-                    l_value = l_out_ext->header.value;
-                } break;
-                default:
+        else {
+            dap_wallet_tx_cache_output_t *l_out_cur = (dap_wallet_tx_cache_output_t*)l_item_cur->output;
+            dap_chain_tx_item_type_t l_type = *(dap_chain_tx_item_type_t*)l_out_cur->tx_out;
+            uint256_t l_value = { };
+            switch (l_type) {
+            case TX_ITEM_TYPE_OUT_OLD: {
+                dap_chain_tx_out_old_t *l_out = (dap_chain_tx_out_old_t*)l_out_cur->tx_out;
+                if (!l_out->header.value)
                     continue;
-                }
-                // Check whether used 'out' items
-                if ( !dap_ledger_tx_hash_is_used_out_item (a_net->pub.ledger, &l_current_wallet_tx_iter->tx_hash, l_out_cur->tx_out_idx, NULL) ) {
-                    dap_chain_tx_used_out_item_t *l_item = DAP_NEW_Z(dap_chain_tx_used_out_item_t);
-                    *l_item = (dap_chain_tx_used_out_item_t) { l_current_wallet_tx_iter->tx_hash, (uint32_t)l_out_cur->tx_out_idx, l_value };
-                    l_list_used_out = dap_list_append(l_list_used_out, l_item);
-                    SUM_256_256(l_value_transfer, l_item->value, &l_value_transfer);
-                    // already accumulated the required value, finish the search for 'out' items
-                    if ( compare256(l_value_transfer, a_value_need) >= 0 ) {
-                        break;
-                    }
-                }
-            }
-        } else {
-            bool skip = true;
-            for (dap_list_t *l_temp = l_current_wallet_tx_iter->tx_wallet_outputs; l_temp; l_temp=l_temp->next){
-                dap_wallet_tx_cache_output_t *l_out_cur = (dap_wallet_tx_cache_output_t*)l_temp->data;
-                dap_chain_tx_item_type_t l_type = *(dap_chain_tx_item_type_t*)l_out_cur->tx_out;
-                uint256_t l_value = { };
-                switch (l_type) {
-                case TX_ITEM_TYPE_OUT_OLD: {
-                    dap_chain_tx_out_old_t *l_out = (dap_chain_tx_out_old_t*)l_out_cur->tx_out;
-                    if (!l_out->header.value)
-                        continue;
-                    l_value = GET_256_FROM_64(l_out->header.value);
-                } break;
-                case TX_ITEM_TYPE_OUT: {
-                    dap_chain_tx_out_t *l_out = (dap_chain_tx_out_t*)l_out_cur->tx_out;
-                    if (IS_ZERO_256(l_out->header.value) )
-                        continue;
-                    l_value = l_out->header.value;
-                } break;
-                case TX_ITEM_TYPE_OUT_EXT: {
-                    // TODO: check ticker
-                    dap_chain_tx_out_ext_t *l_out_ext = (dap_chain_tx_out_ext_t*)l_out_cur->tx_out;
-                    if (dap_strcmp(l_out_ext->token, a_token_ticker))
-                        continue;
-                    if (IS_ZERO_256(l_out_ext->header.value) )
-                        continue;
-                    l_value = l_out_ext->header.value;
-                } break;
-                default:
+                l_value = GET_256_FROM_64(l_out->header.value);
+            } break;
+            case TX_ITEM_TYPE_OUT: {
+                dap_chain_tx_out_t *l_out = (dap_chain_tx_out_t*)l_out_cur->tx_out;
+                if (IS_ZERO_256(l_out->header.value) )
                     continue;
-                }
-                // Check whether used 'out' items
-
-                if ( !dap_ledger_tx_hash_is_used_out_item (a_net->pub.ledger, &l_current_wallet_tx_iter->tx_hash, l_out_cur->tx_out_idx, NULL) ) {
-                    dap_chain_tx_used_out_item_t *l_item = DAP_NEW_Z(dap_chain_tx_used_out_item_t);
-                    *l_item = (dap_chain_tx_used_out_item_t) { l_current_wallet_tx_iter->tx_hash, (uint32_t)l_out_cur->tx_out_idx, l_value };
-                    l_list_used_out = dap_list_append(l_list_used_out, l_item);
-                    SUM_256_256(l_value_transfer, l_item->value, &l_value_transfer);
-                    // already accumulated the required value, finish the search for 'out' items
-                    if ( compare256(l_value_transfer, a_value_need) >= 0 ) {
-                        break;
-                    }
-                }
+                l_value = l_out->header.value;
+            } break;
+            case TX_ITEM_TYPE_OUT_EXT: {
+                dap_chain_tx_out_ext_t *l_out_ext = (dap_chain_tx_out_ext_t*)l_out_cur->tx_out;
+                if (dap_strcmp(l_out_ext->token, a_token_ticker))
+                    continue;
+                if (IS_ZERO_256(l_out_ext->header.value) )
+                    continue;
+                l_value = l_out_ext->header.value;
+            } break;
+            default:
+                continue;
             }
-        }
-        if ( compare256(l_value_transfer, a_value_need) >= 0 ) {
-            break;
-        }
+
+            dap_chain_tx_used_out_item_t *l_item = DAP_NEW_Z(dap_chain_tx_used_out_item_t);
+            *l_item = (dap_chain_tx_used_out_item_t) { l_item_cur->key.tx_hash, (uint32_t)l_item_cur->key.out_idx, l_value};
+            l_list_used_out = dap_list_append(l_list_used_out, l_item);
+            SUM_256_256(l_value_transfer, l_item->value, &l_value_transfer);
+        } 
     }
     pthread_rwlock_unlock(&s_wallet_cache_rwlock);
 
-    if (compare256(l_value_transfer, a_value_need) >= 0 && l_list_used_out){
-        *a_outs_list = l_list_used_out;
-        if (a_value_transfer)
-            *a_value_transfer = l_value_transfer;
-    } else {
-        *a_outs_list = NULL;
-        dap_list_free(l_list_used_out);
-        if (a_value_transfer)
-            *a_value_transfer = uint256_0;
-    }
+    *a_outs_list = l_list_used_out;
+    if (a_value_transfer)
+        *a_value_transfer = l_value_transfer;
    
     return 0;
 }
 
-int dap_chain_wallet_cache_tx_find_outs(dap_chain_net_t *a_net, const char *a_token_ticker, const dap_chain_addr_t *a_addr, 
-                                                    dap_list_t **a_outs_list, uint256_t *a_value_transfer)
+int dap_chain_wallet_cache_tx_find_outs_with_val(dap_chain_net_t *a_net, const char *a_token_ticker, const dap_chain_addr_t *a_addr, 
+                                                    dap_list_t **a_outs_list, uint256_t a_value_need, uint256_t *a_value_transfer)
 {
 
     dap_list_t *l_list_used_out = NULL; // list of transaction with 'out' items
     uint256_t l_value_transfer = { };
     dap_chain_datum_tx_t *l_tx;
 
+    if (s_wallets_cache_type == DAP_WALLET_CACHE_TYPE_DISABLED){
+        debug_if(s_debug_more, L_DEBUG, "Wallet cache is disabled.");
+        return -101;
+    }
+
     if (!a_token_ticker){
         log_it(L_ERROR, "Token ticker is not specified.");
         return -100;
@@ -502,6 +471,11 @@ int dap_chain_wallet_cache_tx_find_outs(dap_chain_net_t *a_net, const char *a_to
         return -100;
     }
 
+    if (IS_ZERO_256(a_value_need)){
+        log_it(L_ERROR, "Needed value is zero.");
+        return -100;
+    }
+
     if (a_outs_list == NULL){
         log_it(L_ERROR, "a_outs_list is NULL");
         return -100;
@@ -511,242 +485,68 @@ int dap_chain_wallet_cache_tx_find_outs(dap_chain_net_t *a_net, const char *a_to
     pthread_rwlock_rdlock(&s_wallet_cache_rwlock);
     HASH_FIND(hh, s_wallets_cache, a_addr, sizeof(dap_chain_addr_t), l_wallet_item);
     if (!l_wallet_item || l_wallet_item->is_loading){
-        log_it(L_ERROR, "Can't find wallet with address %s", dap_chain_addr_to_str_static(a_addr));
+        debug_if(s_debug_more, L_DEBUG, "Can't find wallet with address %s", dap_chain_addr_to_str_static(a_addr));
         pthread_rwlock_unlock(&s_wallet_cache_rwlock);
         return -101;
     }
-    dap_wallet_tx_cache_t *l_current_wallet_tx = l_wallet_item->wallet_txs;
 
-    // Go iterate wallet txs
-    dap_wallet_tx_cache_t *l_current_wallet_tx_iter = NULL, *l_tmp = NULL;
-    HASH_ITER(hh, l_current_wallet_tx, l_current_wallet_tx_iter, l_tmp) {
-        if (l_current_wallet_tx_iter->ret_code != DAP_LEDGER_CHECK_OK)
-            continue;
+    dap_wallet_cache_unspent_outs_t *l_item_cur = NULL, *l_tmp = NULL;
+    HASH_ITER(hh, l_wallet_item->unspent_outputs, l_item_cur, l_tmp){
 
-        
-        if (*l_current_wallet_tx_iter->token_ticker &&
-            dap_strcmp(l_current_wallet_tx_iter->token_ticker, a_token_ticker) &&
-            !l_current_wallet_tx_iter->multichannel)
+        if (dap_strcmp(l_item_cur->token_ticker, a_token_ticker))
             continue;
-        else if (*l_current_wallet_tx_iter->token_ticker && dap_strcmp(l_current_wallet_tx_iter->token_ticker, a_token_ticker) &&
-                    l_current_wallet_tx_iter->multichannel){
-
-            bool skip = true;
-            for (dap_list_t *l_temp = l_current_wallet_tx_iter->tx_wallet_outputs; l_temp; l_temp=l_temp->next){
-                dap_wallet_tx_cache_output_t *l_out_cur = (dap_wallet_tx_cache_output_t*)l_temp->data;
-                dap_chain_tx_item_type_t l_type = *(dap_chain_tx_item_type_t*)l_out_cur->tx_out;
-                uint256_t l_value = { };
-                switch (l_type) {
-                case TX_ITEM_TYPE_OUT_EXT: {
-                    dap_chain_tx_out_ext_t *l_out_ext = (dap_chain_tx_out_ext_t*)l_out_cur->tx_out;
-                    if (dap_strcmp(l_out_ext->token, a_token_ticker))
-                        continue;
-                    if (IS_ZERO_256(l_out_ext->header.value) )
-                        continue;
-                    l_value = l_out_ext->header.value;
-                } break;
-                default:
-                    continue;
-                }
-                // Check whether used 'out' items
-                if ( !dap_ledger_tx_hash_is_used_out_item (a_net->pub.ledger, &l_current_wallet_tx_iter->tx_hash, l_out_cur->tx_out_idx, NULL) ) {
-                    dap_chain_tx_used_out_item_t *l_item = DAP_NEW_Z(dap_chain_tx_used_out_item_t);
-                    *l_item = (dap_chain_tx_used_out_item_t) { l_current_wallet_tx_iter->tx_hash, (uint32_t)l_out_cur->tx_out_idx, l_value };
-                    l_list_used_out = dap_list_append(l_list_used_out, l_item);
-                    SUM_256_256(l_value_transfer, l_item->value, &l_value_transfer);
-                }
-            }
-        } else {
-            bool skip = true;
-            for (dap_list_t *l_temp = l_current_wallet_tx_iter->tx_wallet_outputs; l_temp; l_temp=l_temp->next){
-                dap_wallet_tx_cache_output_t *l_out_cur = (dap_wallet_tx_cache_output_t*)l_temp->data;
-                dap_chain_tx_item_type_t l_type = *(dap_chain_tx_item_type_t*)l_out_cur->tx_out;
-                uint256_t l_value = { };
-                switch (l_type) {
-                case TX_ITEM_TYPE_OUT_OLD: {
-                    dap_chain_tx_out_old_t *l_out = (dap_chain_tx_out_old_t*)l_out_cur->tx_out;
-                    if (!l_out->header.value)
-                        continue;
-                    l_value = GET_256_FROM_64(l_out->header.value);
-                } break;
-                case TX_ITEM_TYPE_OUT: {
-                    dap_chain_tx_out_t *l_out = (dap_chain_tx_out_t*)l_out_cur->tx_out;
-                    if (IS_ZERO_256(l_out->header.value) )
-                        continue;
-                    l_value = l_out->header.value;
-                } break;
-                case TX_ITEM_TYPE_OUT_EXT: {
-                    // TODO: check ticker
-                    dap_chain_tx_out_ext_t *l_out_ext = (dap_chain_tx_out_ext_t*)l_out_cur->tx_out;
-                    if (dap_strcmp(l_out_ext->token, a_token_ticker))
-                        continue;
-                    if (IS_ZERO_256(l_out_ext->header.value) )
-                        continue;
-                    l_value = l_out_ext->header.value;
-                } break;
-                default:
-                    continue;
-                }
-                // Check whether used 'out' items
-
-                if ( !dap_ledger_tx_hash_is_used_out_item (a_net->pub.ledger, &l_current_wallet_tx_iter->tx_hash, l_out_cur->tx_out_idx, NULL) ) {
-                    dap_chain_tx_used_out_item_t *l_item = DAP_NEW_Z(dap_chain_tx_used_out_item_t);
-                    *l_item = (dap_chain_tx_used_out_item_t) { l_current_wallet_tx_iter->tx_hash, (uint32_t)l_out_cur->tx_out_idx, l_value };
-                    l_list_used_out = dap_list_append(l_list_used_out, l_item);
-                    SUM_256_256(l_value_transfer, l_item->value, &l_value_transfer);
-                }
-            }
-        }
-    }
-    pthread_rwlock_unlock(&s_wallet_cache_rwlock);
-
-    *a_outs_list = l_list_used_out;
-    if (a_value_transfer)
-        *a_value_transfer = l_value_transfer;
-   
-    return 0;
-}
-
-static int s_save_tx_into_wallet_cache(dap_chain_t *a_chain, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash,
-                                       dap_hash_fast_t *a_atom_hash, int a_ret_code, char* a_main_token_ticker,
-                                       dap_chain_srv_uid_t a_srv_uid, dap_chain_tx_tag_action_type_t a_action)
-{
-    int l_ret_val = 0;
-    int l_items_cnt = 0;
-
-    dap_list_t *l_out_list = dap_chain_datum_tx_items_get(a_tx, TX_ITEM_TYPE_OUT_ALL, &l_items_cnt);
-    bool l_multichannel = false;
-    int l_out_idx = 0;
-    for (dap_list_t *it=l_out_list; it; it=it->next, l_out_idx++){
-        uint8_t l_out_type = *(uint8_t *)it->data;
-        dap_chain_addr_t l_addr = {};
-        switch(l_out_type){
+        else {
+            dap_wallet_tx_cache_output_t *l_out_cur = (dap_wallet_tx_cache_output_t*)l_item_cur->output;
+            dap_chain_tx_item_type_t l_type = *(dap_chain_tx_item_type_t*)l_out_cur->tx_out;
+            uint256_t l_value = { };
+            switch (l_type) {
             case TX_ITEM_TYPE_OUT_OLD: {
-                l_addr = ((dap_chain_tx_out_old_t*)it->data)->addr;
+                dap_chain_tx_out_old_t *l_out = (dap_chain_tx_out_old_t*)l_out_cur->tx_out;
+                if (!l_out->header.value)
+                    continue;
+                l_value = GET_256_FROM_64(l_out->header.value);
             } break;
             case TX_ITEM_TYPE_OUT: {
-                l_addr = ((dap_chain_tx_out_t*)it->data)->addr;
+                dap_chain_tx_out_t *l_out = (dap_chain_tx_out_t*)l_out_cur->tx_out;
+                if (IS_ZERO_256(l_out->header.value) )
+                    continue;
+                l_value = l_out->header.value;
             } break;
             case TX_ITEM_TYPE_OUT_EXT: {
-                l_addr = ((dap_chain_tx_out_ext_t*)it->data)->addr;
-                l_multichannel = true;
+                dap_chain_tx_out_ext_t *l_out_ext = (dap_chain_tx_out_ext_t*)l_out_cur->tx_out;
+                if (dap_strcmp(l_out_ext->token, a_token_ticker))
+                    continue;
+                if (IS_ZERO_256(l_out_ext->header.value) )
+                    continue;
+                l_value = l_out_ext->header.value;
             } break;
             default:
                 continue;
-        }
-
-        if(!dap_chain_addr_is_blank(&l_addr) && ((s_wallets_cache_type == DAP_WALLET_CACHE_TYPE_LOCAL &&
-                dap_chain_wallet_addr_cache_get_name(&l_addr) != NULL) || s_wallets_cache_type == DAP_WALLET_CACHE_TYPE_ALL)  &&
-                l_addr.net_id.uint64 == a_chain->net_id.uint64){
-            pthread_rwlock_wrlock(&s_wallet_cache_rwlock);
-            dap_wallet_cache_t *l_wallet_item = NULL;
-            HASH_FIND(hh, s_wallets_cache, &l_addr, sizeof(dap_chain_addr_t), l_wallet_item);
-            if (!l_wallet_item){
-                l_wallet_item = DAP_NEW_Z(dap_wallet_cache_t);
-                memcpy (&l_wallet_item->wallet_addr, &l_addr, sizeof(dap_chain_addr_t));
-                HASH_ADD(hh, s_wallets_cache, wallet_addr, sizeof(dap_chain_addr_t), l_wallet_item);
-            }
-            dap_wallet_tx_cache_t *l_wallet_tx_item = NULL;
-            HASH_FIND(hh, l_wallet_item->wallet_txs, a_tx_hash, sizeof(dap_hash_fast_t), l_wallet_tx_item);
-            if (!l_wallet_tx_item){
-                l_wallet_tx_item = DAP_NEW_Z(dap_wallet_tx_cache_t);
-                l_wallet_tx_item->tx_hash = *a_tx_hash;
-                 l_wallet_tx_item->atom_hash = *a_atom_hash;
-                l_wallet_tx_item->tx = a_tx;
-                dap_strncpy(l_wallet_tx_item->token_ticker, a_main_token_ticker ? a_main_token_ticker : "0", DAP_CHAIN_TICKER_SIZE_MAX);
-                l_wallet_tx_item->ret_code = a_ret_code;
-                l_wallet_tx_item->srv_uid = a_srv_uid;
-                l_wallet_tx_item->action = a_action;
-                HASH_ADD(hh, l_wallet_item->wallet_txs, tx_hash, sizeof(dap_hash_fast_t), l_wallet_tx_item);
-            } 
-            l_wallet_tx_item->multichannel = l_multichannel;
-            dap_wallet_tx_cache_output_t *l_out = DAP_NEW_Z(dap_wallet_tx_cache_output_t);
-            l_out->tx_out = it->data;
-            l_out->tx_out_idx = l_out_idx;
-            l_wallet_tx_item->tx_wallet_outputs = dap_list_append(l_wallet_tx_item->tx_wallet_outputs, l_out);
-            pthread_rwlock_unlock(&s_wallet_cache_rwlock);
-        }
-    }
-
-    dap_list_t *l_in_list = dap_chain_datum_tx_items_get(a_tx, TX_ITEM_TYPE_IN_ALL, &l_items_cnt);
-    for (dap_list_t *it=l_in_list; it; it=it->next ){
-        uint8_t l_cond_type = *(uint8_t *)it->data;
-        uint256_t l_value = {};
-        dap_chain_addr_t l_addr_from = {};
-        if(l_cond_type == TX_ITEM_TYPE_IN){
-            dap_hash_fast_t l_prev_tx_hash = ((dap_chain_tx_in_t*)it->data)->header.tx_prev_hash;
-            int l_prev_idx = ((dap_chain_tx_in_t*)it->data)->header.tx_out_prev_idx;
-            if (dap_hash_fast_is_blank(&l_prev_tx_hash))
-                continue;              
-            dap_chain_datum_t *l_prev_datum = a_chain->callback_datum_find_by_hash(a_chain, &l_prev_tx_hash, NULL, NULL);
-            dap_chain_datum_tx_t *l_tx_prev = l_prev_datum ? (dap_chain_datum_tx_t *)(l_prev_datum->data) : NULL;
-            if (!l_tx_prev){
-                log_it(L_ERROR, "Can't find previous transactions (hash=%s)", dap_hash_fast_to_str_static(&l_prev_tx_hash));
-                continue;
-            }
-            uint8_t* l_prev_item = dap_chain_datum_tx_item_get_nth(l_tx_prev, TX_ITEM_TYPE_OUT_ALL, l_prev_idx);
-            if (!l_prev_item){
-                log_it(L_ERROR, "Can't find out with index %d in transaction %s", l_prev_idx, dap_hash_fast_to_str_static(&l_prev_tx_hash));
-                continue;
-            }
-            uint8_t l_out_type = *(uint8_t *)l_prev_item;
-            switch(l_out_type){
-                case TX_ITEM_TYPE_OUT_OLD: {
-                    l_value = GET_256_FROM_64(((dap_chain_tx_out_old_t*)l_prev_item)->header.value);
-                    l_addr_from = ((dap_chain_tx_out_old_t*)l_prev_item)->addr;
-                } break;
-                case TX_ITEM_TYPE_OUT:
-                case TX_ITEM_TYPE_OUT_EXT: {
-                    l_value = ((dap_chain_tx_out_ext_t*)l_prev_item)->header.value;
-                    l_addr_from = ((dap_chain_tx_out_ext_t*)l_prev_item)->addr;
-                } break;
-                default:
-                    continue;
-            }
-
-            if(!dap_chain_addr_is_blank(&l_addr_from) && ((s_wallets_cache_type == DAP_WALLET_CACHE_TYPE_LOCAL &&
-                dap_chain_wallet_addr_cache_get_name(&l_addr_from) != NULL) || s_wallets_cache_type == DAP_WALLET_CACHE_TYPE_ALL)&&
-                l_addr_from.net_id.uint64 == a_chain->net_id.uint64){
-                pthread_rwlock_wrlock(&s_wallet_cache_rwlock);
-                dap_wallet_cache_t *l_wallet_item = NULL;
-                HASH_FIND(hh, s_wallets_cache, &l_addr_from, sizeof(dap_chain_addr_t), l_wallet_item);
-                if (!l_wallet_item){
-                    l_wallet_item = DAP_NEW_Z(dap_wallet_cache_t);
-                    memcpy (&l_wallet_item->wallet_addr, &l_addr_from, sizeof(dap_chain_addr_t));
-                    HASH_ADD(hh, s_wallets_cache, wallet_addr, sizeof(dap_chain_addr_t), l_wallet_item);
-                }
-                dap_wallet_tx_cache_t *l_wallet_tx_item = NULL;
-                HASH_FIND(hh, l_wallet_item->wallet_txs, a_tx_hash, sizeof(dap_hash_fast_t), l_wallet_tx_item);
-                if (!l_wallet_tx_item){
-                    l_wallet_tx_item = DAP_NEW_Z(dap_wallet_tx_cache_t);
-                    l_wallet_tx_item->tx_hash = *a_tx_hash;
-                    l_wallet_tx_item->atom_hash = *a_atom_hash;
-                    l_wallet_tx_item->tx = a_tx;
-                    dap_strncpy(l_wallet_tx_item->token_ticker, a_main_token_ticker ? a_main_token_ticker : "0", DAP_CHAIN_TICKER_SIZE_MAX);
-                    l_wallet_tx_item->multichannel = l_multichannel;
-                    l_wallet_tx_item->ret_code = a_ret_code;
-                    l_wallet_tx_item->srv_uid = a_srv_uid;
-                    l_wallet_tx_item->action = a_action;
-                    HASH_ADD(hh, l_wallet_item->wallet_txs, tx_hash, sizeof(dap_hash_fast_t), l_wallet_tx_item);
-                }
-                dap_wallet_tx_cache_input_t *l_tx_in = DAP_NEW_Z(dap_wallet_tx_cache_input_t);
-                l_tx_in->tx_prev_hash = l_prev_tx_hash;
-                l_tx_in->tx_out_prev_idx = l_prev_idx;
-                l_tx_in->value = l_value;
-                l_wallet_tx_item->tx_wallet_inputs = dap_list_append(l_wallet_tx_item->tx_wallet_inputs, l_tx_in);
-                pthread_rwlock_unlock(&s_wallet_cache_rwlock);
             }
 
+            dap_chain_tx_used_out_item_t *l_item = DAP_NEW_Z(dap_chain_tx_used_out_item_t);
+            *l_item = (dap_chain_tx_used_out_item_t) { l_item_cur->key.tx_hash, (uint32_t)l_item_cur->key.out_idx, l_value};
+            l_list_used_out = dap_list_append(l_list_used_out, l_item);
+            SUM_256_256(l_value_transfer, l_item->value, &l_value_transfer);
+        } 
+        if ( compare256(l_value_transfer, a_value_need) >= 0 ) {
+            break;
         }
     }
+    pthread_rwlock_unlock(&s_wallet_cache_rwlock);
 
-    if (l_out_list)
-        dap_list_free(l_out_list);
-
-    if (l_in_list)
-        dap_list_free(l_in_list);
-
-    return l_ret_val;
+    if (compare256(l_value_transfer, a_value_need) >= 0 && l_list_used_out){
+        *a_outs_list = l_list_used_out;
+        if (a_value_transfer)
+            *a_value_transfer = l_value_transfer;
+    } else {
+        *a_outs_list = NULL;
+        dap_list_free_full(l_list_used_out, NULL);
+        if (a_value_transfer)
+            *a_value_transfer = uint256_0;
+    }
+   
+    return 0;
 }
 
 
@@ -763,7 +563,8 @@ static int s_save_cache_for_addr_in_net(dap_chain_net_t *a_net, dap_chain_addr_t
                         l_datum = l_chain->callback_datum_iter_get_next(l_iter)){
 
                     if (l_datum->header.type_id == DAP_CHAIN_DATUM_TX)
-                        s_save_tx_cache_for_addr(l_chain, a_addr, (dap_chain_datum_tx_t*)l_datum->data, l_iter->cur_hash, l_iter->cur_atom_hash, l_iter->ret_code, l_iter->token_ticker, l_iter->uid, l_iter->action);
+                        s_save_tx_cache_for_addr(l_chain, a_addr, (dap_chain_datum_tx_t*)l_datum->data, l_iter->cur_hash,l_iter->cur_atom_hash,
+                                                 l_iter->ret_code, l_iter->token_ticker, l_iter->uid, l_iter->action, 'a');
                 }
                 l_chain->callback_datum_iter_delete(l_iter);
                 break;
@@ -775,22 +576,27 @@ static int s_save_cache_for_addr_in_net(dap_chain_net_t *a_net, dap_chain_addr_t
     return 0;
 }
 
-static void s_callback_datum_notify(void *a_arg, dap_chain_hash_fast_t *a_datum_hash, dap_chain_hash_fast_t *a_atom_hash,
-                                    void *a_datum, size_t a_datum_size, int a_ret_code, uint32_t a_action, 
-                                    dap_chain_srv_uid_t a_uid)
+static void s_callback_datum_notify(void *a_arg, dap_chain_hash_fast_t *a_datum_hash, dap_chain_hash_fast_t *a_atom_hash, void *a_datum, 
+                                    size_t a_datum_size, int a_ret_code, uint32_t a_action, dap_chain_srv_uid_t a_uid)
 {
     dap_atom_notify_arg_t *l_arg = (dap_atom_notify_arg_t*)a_arg;
-
     dap_chain_datum_t *l_datum = (dap_chain_datum_t*)a_datum;
     if (!l_datum || l_datum->header.type_id != DAP_CHAIN_DATUM_TX)
         return;
 
-    dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*)l_datum->data;
-    
-    const char* l_main_token_ticker = NULL;
+    s_save_tx_cache_for_addr(l_arg->chain, NULL, (dap_chain_datum_tx_t*)l_datum->data, a_datum_hash, a_atom_hash, a_ret_code,
+                             (char*)dap_ledger_tx_get_token_ticker_by_hash(l_arg->net->pub.ledger, a_datum_hash),
+                             a_uid, a_action, 'a');
+}
+
+static void s_callback_datum_removed_notify(void *a_arg, dap_chain_hash_fast_t *a_datum_hash, dap_chain_datum_t *a_datum)
+{
+    if (!a_datum_hash || !a_datum || a_datum->header.type_id != DAP_CHAIN_DATUM_TX)
+        return;
 
-    l_main_token_ticker = dap_ledger_tx_get_token_ticker_by_hash(l_arg->net->pub.ledger, a_datum_hash);
-    s_save_tx_into_wallet_cache(l_arg->chain, l_tx, a_datum_hash, a_atom_hash, a_ret_code, (char*)l_main_token_ticker, a_uid, a_action);
+    dap_atom_notify_arg_t *l_arg = (dap_atom_notify_arg_t*)a_arg;
+    s_save_tx_cache_for_addr(l_arg->chain, NULL, (dap_chain_datum_tx_t*)a_datum->data, a_datum_hash, NULL, 0,
+                             NULL, (dap_chain_srv_uid_t){ }, DAP_CHAIN_TX_TAG_ACTION_UNKNOWN, 'd');
 }
 
 typedef struct wallet_cache_load_args {
@@ -806,11 +612,10 @@ static void *s_wallet_load(void *a_arg)
     s_save_cache_for_addr_in_net(l_args->net, &l_args->addr);
 
     l_args->wallet_item->is_loading = false;
-    DAP_DEL_Z(a_arg);
-
-    return NULL;
+    return DAP_DELETE(a_arg), NULL;
 }
 
+
 static void s_wallet_opened_callback(dap_chain_wallet_t *a_wallet, void *a_arg)
 {
     for(dap_chain_net_t *l_net = dap_chain_net_iter_start(); l_net; l_net=dap_chain_net_iter_next(l_net)){
@@ -844,172 +649,210 @@ static void s_wallet_opened_callback(dap_chain_wallet_t *a_wallet, void *a_arg)
         pthread_create(&l_tid, &attr, s_wallet_load, l_args);
         
         // s_save_cache_for_addr_in_net(l_net, l_addr); 
-        DAP_DEL_Z(l_addr);
     }
 }
 
+static int s_out_idx_cmp(dap_list_t *a_l1, dap_list_t *a_l2) {
+    dap_wallet_tx_cache_output_t *o1 = a_l1->data,
+                                 *o2 = a_l2->data;
+    return o1->tx_out_idx != o2->tx_out_idx;
+}
 
-static int s_save_tx_cache_for_addr(dap_chain_t *a_chain, dap_chain_addr_t *a_addr, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash, 
-                                    dap_hash_fast_t *a_atom_hash, int a_ret_code, char* a_main_token_ticker,
-                                    dap_chain_srv_uid_t a_srv_uid, dap_chain_tx_tag_action_type_t a_action)
+static int s_save_tx_cache_for_addr(dap_chain_t *a_chain, dap_chain_addr_t *a_addr, dap_chain_datum_tx_t *a_tx, 
+                                    dap_hash_fast_t *a_tx_hash, dap_hash_fast_t *a_atom_hash, int a_ret_code, char* a_main_token_ticker,
+                                    dap_chain_srv_uid_t a_srv_uid, dap_chain_tx_tag_action_type_t a_action, char a_cache_op)
 {
-    int l_ret_val = 0;
-    int l_items_cnt = 0;
-
+    int l_ret_val = 0, l_items_cnt = 0, l_out_idx = 0, l_prev_idx;
     bool l_multichannel = false;
-    int l_out_idx = 0;
-    uint8_t *l_tx_item = NULL;
-    size_t l_size; int i;
-    TX_ITEM_ITER_TX_TYPE(l_tx_item, TX_ITEM_TYPE_OUT_ALL, l_size, i, a_tx) {
-        uint8_t l_out_type = *(uint8_t *)l_tx_item;
-        dap_chain_addr_t l_addr = {};
-        switch(l_out_type){
-            case TX_ITEM_TYPE_OUT_OLD: {
-                l_addr = ((dap_chain_tx_out_old_t*)l_tx_item)->addr;
-            } break;
-            case TX_ITEM_TYPE_OUT: {
-                l_addr = ((dap_chain_tx_out_t*)l_tx_item)->addr;
-            } break;
-            case TX_ITEM_TYPE_OUT_EXT: {
-                l_addr = ((dap_chain_tx_out_ext_t*)l_tx_item)->addr;
-                l_multichannel = true;
-            } break;
-            default:{
-                l_out_idx++;
+#define m_check_addr(addr) (                                                                                    \
+    !dap_chain_addr_is_blank(&addr) && (                                                                        \
+        a_addr ? dap_chain_addr_compare(&addr, a_addr) :                                                        \
+        ( (s_wallets_cache_type == DAP_WALLET_CACHE_TYPE_LOCAL && dap_chain_wallet_addr_cache_get_name(&addr))  \
+            || s_wallets_cache_type == DAP_WALLET_CACHE_TYPE_ALL ) )                                            \
+    && addr.net_id.uint64 == a_chain->net_id.uint64                                                             \
+)
+    uint8_t *l_tx_item; size_t l_size;
+    TX_ITEM_ITER_TX(l_tx_item, l_size, a_tx) {
+        dap_hash_fast_t l_prev_tx_hash;
+        dap_chain_addr_t l_addr;
+        uint256_t l_value;
+        uint8_t *l_prev_item = NULL;
+        int l_prev_idx;
+        uint8_t l_item_type = TX_ITEM_TYPE_ANY;
+
+        switch(*l_tx_item) {
+        case TX_ITEM_TYPE_IN: {
+            l_prev_tx_hash = ((dap_chain_tx_in_t*)l_tx_item)->header.tx_prev_hash;
+            l_prev_idx = ((dap_chain_tx_in_t*)l_tx_item)->header.tx_out_prev_idx;
+            if ( dap_hash_fast_is_blank(&l_prev_tx_hash) )
+                continue;
+            dap_chain_datum_t *l_prev_datum = a_chain->callback_datum_find_by_hash(a_chain, &l_prev_tx_hash, NULL, NULL);
+            dap_chain_datum_tx_t *l_tx_prev = l_prev_datum ? (dap_chain_datum_tx_t *)(l_prev_datum->data) : NULL;
+            if (!l_tx_prev) {
+                log_it(L_ERROR, "Can't find previous transaction by hash \"%s\"", dap_hash_fast_to_str_static(&l_prev_tx_hash));
+                continue;
+            }
+            l_prev_item = dap_chain_datum_tx_item_get_nth(l_tx_prev, TX_ITEM_TYPE_OUT_ALL, l_prev_idx);
+            if (!l_prev_item) {
+                log_it(L_ERROR, "Can't find output %d in tx \"%s\"", l_prev_idx, dap_hash_fast_to_str_static(&l_prev_tx_hash));
                 continue;
-            } 
+            }
+            switch (*l_prev_item) {
+            case TX_ITEM_TYPE_OUT_OLD:
+                l_value = GET_256_FROM_64(((dap_chain_tx_out_old_t*)l_prev_item)->header.value);
+                l_addr = ((dap_chain_tx_out_old_t*)l_prev_item)->addr;
+                break;
+            case TX_ITEM_TYPE_OUT:
+            case TX_ITEM_TYPE_OUT_EXT:
+                l_value = ((dap_chain_tx_out_ext_t*)l_prev_item)->header.value;
+                l_addr = ((dap_chain_tx_out_ext_t*)l_prev_item)->addr;
+                break;
+            default:
+                continue;
+            }
+            l_item_type = TX_ITEM_TYPE_IN;
+        } break;
+        case TX_ITEM_TYPE_OUT_OLD:
+            l_addr = ((dap_chain_tx_out_old_t*)l_tx_item)->addr;
+            l_item_type = TX_ITEM_TYPE_OUT_ALL;
+            break;
+        case TX_ITEM_TYPE_OUT:
+            l_addr = ((dap_chain_tx_out_t*)l_tx_item)->addr;
+            l_item_type = TX_ITEM_TYPE_OUT_ALL;
+            break;
+        case TX_ITEM_TYPE_OUT_EXT:
+            l_addr = ((dap_chain_tx_out_ext_t*)l_tx_item)->addr;
+            l_multichannel = true;
+            l_item_type = TX_ITEM_TYPE_OUT_ALL;
+            break;
+        case TX_ITEM_TYPE_OUT_COND:
+        /* Make it explicit for possible future STAKE_LOCK adoption */
+        // TODO
+            ++l_out_idx;
+        default:
+            continue;
         }
 
-        if(!dap_chain_addr_is_blank(&l_addr) && dap_chain_addr_compare(&l_addr, a_addr) &&
-                l_addr.net_id.uint64 == a_chain->net_id.uint64){
-            pthread_rwlock_wrlock(&s_wallet_cache_rwlock);
-            dap_wallet_cache_t *l_wallet_item = NULL;
-            HASH_FIND(hh, s_wallets_cache, &l_addr, sizeof(dap_chain_addr_t), l_wallet_item);
-            if (!l_wallet_item){
+        if ( !m_check_addr(l_addr) ) {
+            l_out_idx += (int)(l_item_type == TX_ITEM_TYPE_OUT_ALL);
+            continue;
+        }
+
+        pthread_rwlock_wrlock(&s_wallet_cache_rwlock);
+        dap_wallet_cache_t *l_wallet_item = NULL;
+        dap_wallet_tx_cache_t *l_wallet_tx_item = NULL;
+        HASH_FIND(hh, s_wallets_cache, &l_addr, sizeof(dap_chain_addr_t), l_wallet_item);
+        switch (a_cache_op) {
+        case 'a':
+            if (!l_wallet_item) {
                 l_wallet_item = DAP_NEW_Z(dap_wallet_cache_t);
-                memcpy (&l_wallet_item->wallet_addr, &l_addr, sizeof(dap_chain_addr_t));
+                l_wallet_item->wallet_addr = l_addr;
                 HASH_ADD(hh, s_wallets_cache, wallet_addr, sizeof(dap_chain_addr_t), l_wallet_item);
             }
-            dap_wallet_tx_cache_t *l_wallet_tx_item = NULL;
+            
             HASH_FIND(hh, l_wallet_item->wallet_txs, a_tx_hash, sizeof(dap_hash_fast_t), l_wallet_tx_item);
-            if (!l_wallet_tx_item){
-                l_wallet_tx_item = DAP_NEW_Z(dap_wallet_tx_cache_t);
-                l_wallet_tx_item->tx_hash = *a_tx_hash;
-                l_wallet_tx_item->atom_hash = *a_atom_hash;
-                l_wallet_tx_item->tx = a_tx;
+            if (!l_wallet_tx_item) {
+                l_wallet_tx_item = DAP_NEW(dap_wallet_tx_cache_t);
+                *l_wallet_tx_item = (dap_wallet_tx_cache_t){ .tx_hash = *a_tx_hash, .atom_hash = *a_atom_hash, .tx = a_tx,
+                    .multichannel = l_multichannel, .ret_code = a_ret_code, .srv_uid = a_srv_uid, .action = a_action };
                 dap_strncpy(l_wallet_tx_item->token_ticker, a_main_token_ticker ? a_main_token_ticker : "0", DAP_CHAIN_TICKER_SIZE_MAX);
-                l_wallet_tx_item->ret_code = a_ret_code;
-                l_wallet_tx_item->srv_uid = a_srv_uid;
-                l_wallet_tx_item->action = a_action;
                 HASH_ADD(hh, l_wallet_item->wallet_txs, tx_hash, sizeof(dap_hash_fast_t), l_wallet_tx_item);
-            } 
-            l_wallet_tx_item->multichannel = l_multichannel;
-            dap_wallet_tx_cache_output_t *l_out = DAP_NEW_Z(dap_wallet_tx_cache_output_t);
-            l_out->tx_out = l_tx_item;
-            l_out->tx_out_idx = l_out_idx;
-            l_wallet_tx_item->tx_wallet_outputs = dap_list_append(l_wallet_tx_item->tx_wallet_outputs, l_out);
-            pthread_rwlock_unlock(&s_wallet_cache_rwlock);
-        }
-    }
-
-    l_tx_item = NULL;
-    TX_ITEM_ITER_TX_TYPE(l_tx_item, TX_ITEM_TYPE_IN_ALL, l_size, i, a_tx) {
-        uint8_t l_cond_type = *l_tx_item;
-        uint256_t l_value = {};
-        dap_chain_addr_t l_addr_from = {};
-        if(l_cond_type == TX_ITEM_TYPE_IN){
-            dap_hash_fast_t l_prev_tx_hash = ((dap_chain_tx_in_t*)l_tx_item)->header.tx_prev_hash;
-            int l_prev_idx = ((dap_chain_tx_in_t*)l_tx_item)->header.tx_out_prev_idx;
-            if (dap_hash_fast_is_blank(&l_prev_tx_hash))
-                continue;
-            dap_chain_datum_t *l_prev_datum = a_chain->callback_datum_find_by_hash(a_chain, &l_prev_tx_hash, NULL, NULL);
-            dap_chain_datum_tx_t *l_tx_prev = l_prev_datum ? (dap_chain_datum_tx_t *)(l_prev_datum->data) : NULL;
-            if (!l_tx_prev) {
-                log_it(L_ERROR, "Can't find previous transactions (hash=%s)", dap_hash_fast_to_str_static(&l_prev_tx_hash));
-                continue;
-            }
-            uint8_t* l_prev_item = dap_chain_datum_tx_item_get_nth(l_tx_prev, TX_ITEM_TYPE_OUT_ALL, l_prev_idx);
-            if (!l_prev_item) {
-                log_it(L_ERROR, "Can't find out with index %d in transaction %s", l_prev_idx, dap_hash_fast_to_str_static(&l_prev_tx_hash));
-                continue;
             }
-            uint8_t l_out_type = *(uint8_t *)l_prev_item;
-            switch(l_out_type){
-                case TX_ITEM_TYPE_OUT_OLD: {
-                    l_value = GET_256_FROM_64(((dap_chain_tx_out_old_t*)l_prev_item)->header.value);
-                    l_addr_from = ((dap_chain_tx_out_old_t*)l_prev_item)->addr;
-                } break;
-                case TX_ITEM_TYPE_OUT:
-                case TX_ITEM_TYPE_OUT_EXT: {
-                    l_value = ((dap_chain_tx_out_ext_t*)l_prev_item)->header.value;
-                    l_addr_from = ((dap_chain_tx_out_ext_t*)l_prev_item)->addr;
-                } break;
-                default:
-                    continue;
+            break;
+        case 'd': {
+            if (l_wallet_item) {
+                HASH_FIND(hh, l_wallet_item->wallet_txs, a_tx_hash, sizeof(dap_hash_fast_t), l_wallet_tx_item);
+                if (l_wallet_tx_item){
+                    HASH_DEL(l_wallet_item->wallet_txs, l_wallet_tx_item);
+                    dap_list_free_full(l_wallet_tx_item->tx_wallet_inputs, NULL);
+                    dap_list_free_full(l_wallet_tx_item->tx_wallet_outputs, NULL);
+                    DAP_DELETE(l_wallet_tx_item);
+                }
             }
+        }
+        default: break;
+        }
+        
 
-            if(!dap_chain_addr_is_blank(&l_addr_from)  && dap_chain_addr_compare(&l_addr_from, a_addr) &&
-                l_addr_from.net_id.uint64 == a_chain->net_id.uint64){
-                pthread_rwlock_wrlock(&s_wallet_cache_rwlock);
-                dap_wallet_cache_t *l_wallet_item = NULL;
-                HASH_FIND(hh, s_wallets_cache, &l_addr_from, sizeof(dap_chain_addr_t), l_wallet_item);
-                if (!l_wallet_item){
-                    l_wallet_item = DAP_NEW_Z(dap_wallet_cache_t);
-                    memcpy (&l_wallet_item->wallet_addr, &l_addr_from, sizeof(dap_chain_addr_t));
-                    HASH_ADD(hh, s_wallets_cache, wallet_addr, sizeof(dap_chain_addr_t), l_wallet_item);
+        switch (l_item_type) {
+        case TX_ITEM_TYPE_OUT_ALL: {
+            switch (a_cache_op) {
+            case 'a': {
+                dap_wallet_tx_cache_output_t *l_out = DAP_NEW(dap_wallet_tx_cache_output_t);
+                *l_out = (dap_wallet_tx_cache_output_t){ .tx_out = l_tx_item, .tx_out_idx = l_out_idx };
+                l_wallet_tx_item->tx_wallet_outputs = dap_list_append(l_wallet_tx_item->tx_wallet_outputs, l_out);
+                /* Add unspent out to cache */ 
+                if (!a_ret_code) {
+                    dap_wallet_cache_unspent_outs_t *l_unspent_out = DAP_NEW(dap_wallet_cache_unspent_outs_t);
+                    *l_unspent_out = (dap_wallet_cache_unspent_outs_t) {
+                        .key = { .tx_hash = *a_tx_hash, .out_idx = l_out_idx },
+                        .output = l_out
+                    };
+                    dap_strncpy(l_unspent_out->token_ticker, *l_tx_item == TX_ITEM_TYPE_OUT_EXT ? ((dap_chain_tx_out_ext_t*)l_tx_item)->token
+                                : a_main_token_ticker ? a_main_token_ticker : "0", DAP_CHAIN_TICKER_SIZE_MAX);                   
+                    HASH_ADD(hh, l_wallet_item->unspent_outputs, key, sizeof(unspent_cache_hh_key), l_unspent_out);
                 }
-                dap_wallet_tx_cache_t *l_wallet_tx_item = NULL;
-                HASH_FIND(hh, l_wallet_item->wallet_txs, a_tx_hash, sizeof(dap_hash_fast_t), l_wallet_tx_item);
-                if (!l_wallet_tx_item){
-                    l_wallet_tx_item = DAP_NEW_Z(dap_wallet_tx_cache_t);
-                    l_wallet_tx_item->tx_hash = *a_tx_hash;
-                    l_wallet_tx_item->atom_hash = *a_atom_hash;
-                    l_wallet_tx_item->tx = a_tx;
-                    dap_strncpy(l_wallet_tx_item->token_ticker, a_main_token_ticker ? a_main_token_ticker : "0", DAP_CHAIN_TICKER_SIZE_MAX);
-                    l_wallet_tx_item->multichannel = l_multichannel;
-                    l_wallet_tx_item->ret_code = a_ret_code;
-                    l_wallet_tx_item->srv_uid = a_srv_uid;
-                    l_wallet_tx_item->action = a_action;
-                    HASH_ADD(hh, l_wallet_item->wallet_txs, tx_hash, sizeof(dap_hash_fast_t), l_wallet_tx_item);
+                ++l_out_idx;
+            } break;
+            case 'd': {
+                if ( !l_wallet_item->wallet_txs ) {
+                    HASH_DEL(s_wallets_cache, l_wallet_item);
+                    DAP_DELETE(l_wallet_item);
+                }            
+                unspent_cache_hh_key key = { .tx_hash = *a_tx_hash, .out_idx = l_out_idx };
+                dap_wallet_cache_unspent_outs_t *l_item = NULL;
+                HASH_FIND(hh, l_wallet_item->unspent_outputs, &key, sizeof(unspent_cache_hh_key), l_item);
+                if (l_item) {
+                    HASH_DEL(l_wallet_item->unspent_outputs, l_item);
+                    DAP_DELETE(l_item);
                 }
-                dap_wallet_tx_cache_input_t *l_tx_in = DAP_NEW_Z(dap_wallet_tx_cache_input_t);
-                l_tx_in->tx_prev_hash = l_prev_tx_hash;
-                l_tx_in->tx_out_prev_idx = l_prev_idx;
-                l_tx_in->value = l_value;
+            } break;
+            default: break;
+            }
+        } break;
+        case TX_ITEM_TYPE_IN: {
+            switch (a_cache_op) {
+            case 'a': {
+                dap_wallet_tx_cache_input_t *l_tx_in = DAP_NEW(dap_wallet_tx_cache_input_t);
+                *l_tx_in = (dap_wallet_tx_cache_input_t) { .tx_prev_hash = l_prev_tx_hash, .tx_out_prev_idx = l_prev_idx, .value = l_value };
                 l_wallet_tx_item->tx_wallet_inputs = dap_list_append(l_wallet_tx_item->tx_wallet_inputs, l_tx_in);
-                pthread_rwlock_unlock(&s_wallet_cache_rwlock);
+                /* Delete unspent out from cache */
+                if (!a_ret_code) {
+                    unspent_cache_hh_key key = { .tx_hash = l_prev_tx_hash, .out_idx = l_prev_idx };
+                    dap_wallet_cache_unspent_outs_t *l_item = NULL;
+                    HASH_FIND(hh, l_wallet_item->unspent_outputs, &key, sizeof(unspent_cache_hh_key), l_item);
+                    if (l_item) {
+                        HASH_DEL(l_wallet_item->unspent_outputs, l_item);
+                        DAP_DELETE(l_item);
+                    }
+                }
+            } break;
+            case 'd': {
+                dap_wallet_tx_cache_t *l_wallet_prev_tx_item = NULL;
+                HASH_FIND(hh, l_wallet_item->wallet_txs, &l_prev_tx_hash, sizeof(dap_hash_fast_t), l_wallet_prev_tx_item);
+                if ( l_wallet_prev_tx_item && !l_wallet_prev_tx_item->ret_code ) {
+                    dap_wallet_tx_cache_output_t l_sought_out = { .tx_out_idx = l_prev_idx };
+                    void *l_out = dap_list_find(l_wallet_prev_tx_item->tx_wallet_outputs, &l_sought_out, s_out_idx_cmp);
+                    if (l_out) {
+                        dap_wallet_cache_unspent_outs_t *l_unspent_out = DAP_NEW_Z(dap_wallet_cache_unspent_outs_t);
+                        *l_unspent_out = (dap_wallet_cache_unspent_outs_t) { .key = { .tx_hash = l_prev_tx_hash, .out_idx = l_prev_idx },
+                            .output = l_out };
+                        dap_strncpy(l_unspent_out->token_ticker, *l_prev_item == TX_ITEM_TYPE_OUT_EXT ? ((dap_chain_tx_out_ext_t*)l_tx_item)->token
+                                    : l_wallet_prev_tx_item->token_ticker, DAP_CHAIN_TICKER_SIZE_MAX);
+                        HASH_ADD(hh, l_wallet_item->unspent_outputs, key, sizeof(unspent_cache_hh_key), l_unspent_out);
+                    }
+                }
+            } break;
+            default: break;
             }
-
+        } break;
+        default: break;
         }
+        pthread_rwlock_unlock(&s_wallet_cache_rwlock);
     }
-
     return l_ret_val;
 }
 
-static void s_callback_datum_removed_notify(void *a_arg, dap_chain_hash_fast_t *a_datum_hash)
-{
-    if (!a_datum_hash)
-        return;
-
-    pthread_rwlock_wrlock(&s_wallet_cache_rwlock);
-    dap_wallet_cache_t *l_wallet_item = NULL, *l_tmp;
-    HASH_ITER(hh, s_wallets_cache, l_wallet_item, l_tmp){
-        dap_wallet_tx_cache_t *l_wallet_tx_item = NULL;
-        HASH_FIND(hh, l_wallet_item->wallet_txs, a_datum_hash, sizeof(dap_hash_fast_t), l_wallet_tx_item);
-        if (l_wallet_tx_item){
-            HASH_DEL(l_wallet_item->wallet_txs, l_wallet_tx_item);
-            dap_list_free_full(l_wallet_tx_item->tx_wallet_inputs, NULL);
-            dap_list_free_full(l_wallet_tx_item->tx_wallet_outputs, NULL);
-            DAP_DELETE(l_wallet_tx_item);
-        }
-        if (!l_wallet_item->wallet_txs){
-            HASH_DEL(s_wallets_cache, l_wallet_item);
-            DAP_DELETE(l_wallet_item);
-        }            
-    }
-    pthread_rwlock_unlock(&s_wallet_cache_rwlock);
-}
-
 static void s_wallet_cache_iter_fill(dap_chain_wallet_cache_iter_t *a_cache_iter, dap_wallet_tx_cache_t *a_cache_index)
 {
     a_cache_iter->cur_item = (void*)a_cache_index;
@@ -1064,6 +907,9 @@ void dap_chain_wallet_cache_iter_delete(dap_chain_wallet_cache_iter_t *a_iter)
 
 dap_chain_datum_tx_t *dap_chain_wallet_cache_iter_get(dap_chain_wallet_cache_iter_t *a_iter, dap_chain_wallet_getting_type_t a_type)
 {
+    if (!a_iter)
+        return NULL;
+        
     switch (a_type){
         case DAP_CHAIN_WALLET_CACHE_GET_FIRST:{
             pthread_rwlock_wrlock(&s_wallet_cache_rwlock);
-- 
GitLab


From 4db8c7c08e6bcee1a097eedcc1e5d4ed08c7afbe Mon Sep 17 00:00:00 2001
From: "Constantin P." <papizh.konstantin@demlabs.net>
Date: Thu, 20 Feb 2025 18:11:43 +0700
Subject: [PATCH 07/15] Block dump fix

---
 modules/type/blocks/dap_chain_cs_blocks.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/type/blocks/dap_chain_cs_blocks.c b/modules/type/blocks/dap_chain_cs_blocks.c
index 53f9415797..1e9c27fb9d 100644
--- a/modules/type/blocks/dap_chain_cs_blocks.c
+++ b/modules/type/blocks/dap_chain_cs_blocks.c
@@ -786,7 +786,7 @@ static int s_cli_blocks(int a_argc, char ** a_argv, void **a_str_reply)
             // Header
             json_object* json_obj_inf = json_object_new_object();
             json_object_object_add(json_obj_inf, "Block number", json_object_new_uint64(l_block_cache->block_number));
-            json_object_object_add(json_obj_inf, "hash", json_object_new_string(l_subcmd_str_arg));
+            json_object_object_add(json_obj_inf, "hash", json_object_new_string(l_hash_str));
             snprintf(l_hexbuf, sizeof(l_hexbuf), "0x%04X",l_block->hdr.version);
             
             json_object_object_add(json_obj_inf, "version", json_object_new_string(l_hexbuf));
-- 
GitLab


From 0bc1c8c00aee195f70d9a9aca7c26bc8b2dbb283 Mon Sep 17 00:00:00 2001
From: "daniil.frolov" <daniil.frolov@demlabs.net>
Date: Wed, 19 Feb 2025 06:16:52 +0000
Subject: [PATCH 08/15] Conflict resolved

---
 modules/net-srv/dap_chain_net_srv_order.c | 24 ++++++++++++-----------
 modules/wallet/dap_chain_wallet.c         |  8 ++++++--
 2 files changed, 19 insertions(+), 13 deletions(-)

diff --git a/modules/net-srv/dap_chain_net_srv_order.c b/modules/net-srv/dap_chain_net_srv_order.c
index b3cda71447..c60143ca73 100644
--- a/modules/net-srv/dap_chain_net_srv_order.c
+++ b/modules/net-srv/dap_chain_net_srv_order.c
@@ -642,18 +642,20 @@ void dap_chain_net_srv_order_dump_to_json(const dap_chain_net_srv_order_t *a_ord
         json_object_object_add(a_json_obj_out, "node_location", json_object_new_string(buf_location));
         DAP_DELETE(l_region);
 
-        l_hash_str = dap_strcmp(a_hash_out_type, "hex")
+        if (!dap_hash_fast_is_blank(&a_order->tx_cond_hash)){
+            l_hash_str = dap_strcmp(a_hash_out_type, "hex")
                 ? dap_enc_base58_encode_hash_to_str_static(&a_order->tx_cond_hash)
-                : dap_chain_hash_fast_to_str_static(&a_order->tx_cond_hash);
-        json_object_object_add(a_json_obj_out, "tx_cond_hash", json_object_new_string(l_hash_str));
-        if (a_order->ext_size) {
-            char *l_ext_out = DAP_NEW_Z_SIZE(char, a_order->ext_size * 2 + 3);
-            memcpy(l_ext_out, "0x", 2);
-            dap_bin2hex(l_ext_out + 2, a_order->ext_n_sign, a_order->ext_size);
-            json_object_object_add(a_json_obj_out, "ext", json_object_new_string(l_ext_out));
-            DAP_DELETE(l_ext_out);
-        } else
-            json_object_object_add(a_json_obj_out, "ext", json_object_new_string("0x0"));
+                : dap_chain_hash_fast_to_str_static(&a_order->tx_cond_hash);      
+            json_object_object_add(a_json_obj_out, "tx_cond_hash", json_object_new_string(l_hash_str));
+        }
+        json_object_object_add(a_json_obj_out, "ext_size", json_object_new_uint64(a_order->ext_size));
+        // if (a_order->ext_size) {
+        //     char *l_ext_out = DAP_NEW_Z_SIZE(char, a_order->ext_size * 2 + 3);
+        //     dap_strncpy(l_ext_out, "0x", 2);
+        //     dap_bin2hex(l_ext_out + 2, a_order->ext_n_sign, a_order->ext_size);
+        //     
+        //     DAP_DELETE(l_ext_out);
+        // }
         dap_sign_t *l_sign = (dap_sign_t*)((byte_t*)a_order->ext_n_sign + a_order->ext_size);
         dap_hash_fast_t l_sign_pkey = {0};
         dap_sign_get_pkey_hash(l_sign, &l_sign_pkey);
diff --git a/modules/wallet/dap_chain_wallet.c b/modules/wallet/dap_chain_wallet.c
index 3f55a5f9ec..36af7b3f33 100644
--- a/modules/wallet/dap_chain_wallet.c
+++ b/modules/wallet/dap_chain_wallet.c
@@ -82,6 +82,8 @@ static const mode_t s_fileprot =  ( S_IREAD | S_IWRITE) | (S_IREAD >> 3) | (S_IR
 #endif
 static char const s_wallet_ext [] = ".dwallet", *s_wallets_path = NULL;
 
+static bool s_debug_more = false;
+
 static  pthread_rwlock_t s_wallet_n_pass_lock = PTHREAD_RWLOCK_INITIALIZER; /* Coordinate access to the hash-table */
 static  dap_chain_wallet_n_pass_t   *s_wallet_n_pass;                       /* A hash table to keep passwords for wallets */
 static  dap_list_t *s_wallet_open_notificators = NULL;
@@ -357,6 +359,8 @@ int dap_chain_wallet_init()
     dap_chain_wallet_t *l_wallet = NULL;
     size_t l_len = 0;
 
+    s_debug_more = dap_config_get_item_bool_default(g_config,"wallet","debug_more", s_debug_more);
+
     if ( !(c_wallets_path = (char *) dap_chain_wallet_get_path(g_config)) ) /* No path to wallets - nothing to do */
         return -1;
 
@@ -820,7 +824,7 @@ uint32_t    l_csum = CRC32C_INIT, l_csum2 = CRC32C_INIT;
     }
 
     if ( (l_file_hdr.version == DAP_WALLET$K_VER_2) && (!l_pass) ) {
-        log_it(L_DEBUG, "Wallet (%s) version 2 cannot be processed w/o password", a_file_name);
+        debug_if(s_debug_more, L_DEBUG, "Wallet (%s) version 2 cannot be processed w/o password", a_file_name);
         dap_fileclose(l_fh);
         if ( a_out_stat )
             *a_out_stat = 4;
@@ -851,7 +855,7 @@ uint32_t    l_csum = CRC32C_INIT, l_csum2 = CRC32C_INIT;
     l_csum = crc32c(l_csum, &l_file_hdr, sizeof(l_file_hdr) );           /* Compute check sum of the Wallet file header */
     l_csum = crc32c(l_csum, l_wallet_name,  l_file_hdr.wallet_len);
 
-    log_it(L_DEBUG, "Wallet file: %s, Wallet[Version: %d, type: %d, name: '%.*s']",
+    debug_if(s_debug_more, L_DEBUG, "Wallet file: %s, Wallet[Version: %d, type: %d, name: '%.*s']",
            a_file_name, l_file_hdr.version, l_file_hdr.type, l_file_hdr.wallet_len, l_wallet_name);
 
     /* First run - count certs in file */
-- 
GitLab


From 3e80228fe022e834a41809558cdf54e94df33099 Mon Sep 17 00:00:00 2001
From: "Constantin P." <papizh.konstantin@demlabs.net>
Date: Fri, 21 Feb 2025 15:29:03 +0700
Subject: [PATCH 09/15] 15663

---
 modules/chain/tests/dap_chain_ledger_tests.c |  25 ++---
 modules/ledger/dap_chain_ledger.c            |  10 +-
 modules/ledger/dap_chain_ledger_token.c      |  35 ++----
 modules/ledger/dap_chain_ledger_tx.c         | 111 ++++++++-----------
 modules/ledger/include/dap_chain_ledger.h    |   2 +-
 modules/net/dap_chain_net.c                  |   4 +-
 modules/type/dag/dap_chain_cs_dag.c          |   2 +-
 7 files changed, 76 insertions(+), 113 deletions(-)

diff --git a/modules/chain/tests/dap_chain_ledger_tests.c b/modules/chain/tests/dap_chain_ledger_tests.c
index 6c284f7f95..0b762718ae 100644
--- a/modules/chain/tests/dap_chain_ledger_tests.c
+++ b/modules/chain/tests/dap_chain_ledger_tests.c
@@ -100,22 +100,17 @@ dap_chain_datum_tx_t *dap_ledger_test_create_datum_base_tx(
     uint256_t l_value_need = a_emi->hdr.value;
     dap_chain_datum_tx_t *l_tx = DAP_NEW_Z_SIZE(dap_chain_datum_tx_t, sizeof(dap_chain_datum_tx_t));
     l_tx->header.ts_created = time(NULL);
-    dap_chain_tx_in_ems_t *l_in_ems = DAP_NEW_Z(dap_chain_tx_in_ems_t);
-    l_in_ems->header.type = TX_ITEM_TYPE_IN_EMS;
-    l_in_ems->header.token_emission_chain_id.uint64 = 0;
-    l_in_ems->header.token_emission_hash = *l_emi_hash;
-    strcpy(l_in_ems->header.ticker, a_emi->hdr.ticker);
-	SUBTRACT_256_256(l_value_need, l_value_fee, &l_value_need);
-    dap_chain_tx_out_t *l_out = dap_chain_datum_tx_item_out_create(&a_addr_to, l_value_need);
-	dap_chain_tx_out_cond_t *l_tx_out_fee = dap_chain_datum_tx_item_out_cond_create_fee(l_value_fee);
-    dap_chain_datum_tx_add_item(&l_tx, (const uint8_t*) l_in_ems);
-    dap_chain_datum_tx_add_item(&l_tx, (const uint8_t*) l_out);
-	dap_chain_datum_tx_add_item(&l_tx, (const uint8_t*) l_tx_out_fee);
+    dap_chain_tx_in_ems_t l_in_ems = { .header.type = TX_ITEM_TYPE_IN_EMS, .header.token_emission_chain_id.uint64 = 0, .header.token_emission_hash = *l_emi_hash};
+    strcpy(l_in_ems.header.ticker, a_emi->hdr.ticker);
+    dap_chain_datum_tx_add_item(&l_tx, (const uint8_t*) &l_in_ems);
+    if ( !strcmp(l_in_ems.header.ticker, s_token_ticker) ) {
+        SUBTRACT_256_256(l_value_need, l_value_fee, &l_value_need);
+        dap_chain_datum_tx_add_out_item(&l_tx, &a_addr_to, l_value_need);
+        dap_chain_datum_tx_add_fee_item(&l_tx, l_value_fee);
+    } else {
+        dap_chain_datum_tx_add_out_ext_item(&l_tx, &a_addr_to, l_value_need, l_in_ems.header.ticker);
+    }
     dap_chain_datum_tx_add_sign_item(&l_tx, a_cert->enc_key);
-    DAP_DEL_Z(l_in_ems);
-    DAP_DEL_Z(l_out);
-	DAP_DEL_Z(l_tx_out_fee);
-
     return l_tx;
 }
 
diff --git a/modules/ledger/dap_chain_ledger.c b/modules/ledger/dap_chain_ledger.c
index d4f132c1d6..9a11c0d655 100644
--- a/modules/ledger/dap_chain_ledger.c
+++ b/modules/ledger/dap_chain_ledger.c
@@ -276,13 +276,13 @@ void dap_ledger_handle_free(dap_ledger_t *a_ledger)
 
 }
 
-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(g_debug_ledger && ret, L_MSG, "Datum %s is blacklisted", dap_hash_fast_to_str_static(&a_hash)), !!ret;
+bool dap_ledger_datum_is_enforced(dap_ledger_t *a_ledger, dap_hash_fast_t *a_hash, bool a_accept) {
+    dap_ledger_hal_item_t *l_wanted = NULL;
+    HASH_FIND(hh, a_accept ? PVT(a_ledger)->hal_items : PVT(a_ledger)->hrl_items, a_hash, sizeof(dap_hash_fast_t), l_wanted);
+    debug_if(g_debug_ledger && l_wanted, L_DEBUG, "Datum %s is %slisted", dap_hash_fast_to_str_static(&a_hash), a_accept ? "white" : "black");
+    return !!l_wanted;
 }
 
-
 /**
  * @brief s_tx_header_print
  * prepare data for print, add time
diff --git a/modules/ledger/dap_chain_ledger_token.c b/modules/ledger/dap_chain_ledger_token.c
index 4a3e5a3278..251edc83aa 100644
--- a/modules/ledger/dap_chain_ledger_token.c
+++ b/modules/ledger/dap_chain_ledger_token.c
@@ -37,14 +37,6 @@ dap_ledger_token_item_t *dap_ledger_pvt_find_token(dap_ledger_t *a_ledger, const
     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
@@ -1020,17 +1012,15 @@ int s_token_add_check(dap_ledger_t *a_ledger, byte_t *a_token, size_t a_token_si
     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;
+    bool l_is_whitelisted = false;
     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_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);
+            
+        if (!( l_is_whitelisted = dap_ledger_datum_is_enforced(a_ledger, &l_token_hash, true) )) {
             DAP_DELETE(l_token);
             return ret;
         }
@@ -1045,7 +1035,7 @@ int s_token_add_check(dap_ledger_t *a_ledger, byte_t *a_token, size_t a_token_si
         *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;
+    return l_is_whitelisted ? 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)
@@ -1424,7 +1414,7 @@ int s_emission_add_check(dap_ledger_t *a_ledger, byte_t *a_token_emission, size_
         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)) {
+            if ( !dap_ledger_datum_is_enforced(a_ledger, a_emission_hash, true) ) {
                 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;
@@ -1445,7 +1435,7 @@ int s_emission_add_check(dap_ledger_t *a_ledger, byte_t *a_token_emission, size_
 
             DAP_DELETE(l_signs);
 
-            if (!s_check_hal(a_ledger, a_emission_hash)) {
+            if ( !dap_ledger_datum_is_enforced(a_ledger, a_emission_hash, true) ) {
 
                 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);
@@ -1479,8 +1469,7 @@ int s_emission_add_check(dap_ledger_t *a_ledger, byte_t *a_token_emission, size_
             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)) {
+        if (l_aproves < l_token_item->auth_signs_valid && !dap_ledger_datum_is_enforced(a_ledger, a_emission_hash, true) ) {
             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);
diff --git a/modules/ledger/dap_chain_ledger_tx.c b/modules/ledger/dap_chain_ledger_tx.c
index a639855427..17368e14cf 100644
--- a/modules/ledger/dap_chain_ledger_tx.c
+++ b/modules/ledger/dap_chain_ledger_tx.c
@@ -807,36 +807,27 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
         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) {
+    // 6. Compare sum of values in 'out' items
+    if ( !l_main_ticker )
+        switch ( HASH_COUNT(l_values_from_prev_tx) ) {
+        case 1:
+            l_main_ticker = l_value_cur->token_ticker;
+        case 2:
             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);
+            break;
+        default:
+            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;
+            return DAP_LEDGER_TX_CHECK_NO_MAIN_TICKER;
         }
-        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;
     l_tax_check = 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)
@@ -852,7 +843,7 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
         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
+            if (!( l_token = l_main_ticker )) {
                 l_err_num = DAP_LEDGER_TX_CHECK_NO_MAIN_TICKER;
                 break;
             }
@@ -862,13 +853,9 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
         } 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;
-                }
+            if (!( l_token = l_main_ticker )) {
+                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;
@@ -883,20 +870,9 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
         } 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_tx_out->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE_STACK)
-                    l_token = (char *)a_ledger->net->pub.native_ticker;
-                else if (l_main_ticker || (a_main_ticker && *a_main_ticker)) {
-                    if (!l_main_ticker)
-                        l_main_ticker = a_main_ticker; // It should be only in hardfork state
-                    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;
-                }
+            if (!( l_token = l_tx_out->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE ? a_ledger->net->pub.native_ticker : l_main_ticker )) {
+                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);
@@ -939,18 +915,16 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
 
         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);
+        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;
             }
+            dap_strncpy(l_value_cur->token_ticker, l_token, sizeof(l_value_cur->token_ticker));
+            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",
@@ -982,20 +956,25 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
     }
 
     // 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);
+    if ( !l_err_num && !dap_ledger_datum_is_enforced(a_ledger, a_tx_hash, true) ) {
+        if ( HASH_COUNT(l_values_from_prev_tx) != HASH_COUNT(l_values_from_cur_tx) ) {
+            log_it(L_ERROR, "Token tickers IN and OUT mismatch: %u != %u",
+                            HASH_COUNT(l_values_from_prev_tx), HASH_COUNT(l_values_from_cur_tx));
+            l_err_num = DAP_LEDGER_TX_CHECK_SUM_INS_NOT_EQUAL_SUM_OUTS;
+        } else {
+            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), 
+                             *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_DEL_MULTY(l_balance, l_balance_cur);
+                    }
+                    l_err_num = DAP_LEDGER_TX_CHECK_SUM_INS_NOT_EQUAL_SUM_OUTS;
+                    break;
                 }
-                l_err_num = DAP_LEDGER_TX_CHECK_SUM_INS_NOT_EQUAL_SUM_OUTS;
-                break;
             }
         }
     }
diff --git a/modules/ledger/include/dap_chain_ledger.h b/modules/ledger/include/dap_chain_ledger.h
index 0a1d3a1205..933b248621 100644
--- a/modules/ledger/include/dap_chain_ledger.h
+++ b/modules/ledger/include/dap_chain_ledger.h
@@ -486,7 +486,7 @@ dap_chain_datum_tx_t *dap_ledger_datum_iter_get_last(dap_ledger_datum_iter_t *a_
 void dap_ledger_tx_add_notify(dap_ledger_t *a_ledger, dap_ledger_tx_add_notify_t a_callback, void *a_arg);
 void dap_ledger_bridged_tx_notify_add(dap_ledger_t *a_ledger, dap_ledger_bridged_tx_notify_t a_callback, void *a_arg);
 
-bool dap_ledger_datum_is_blacklisted(dap_ledger_t *a_ledger, dap_hash_fast_t a_hash);
+bool dap_ledger_datum_is_enforced(dap_ledger_t *a_ledger, dap_hash_fast_t *a_hash, bool a_accept);
 
 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);
diff --git a/modules/net/dap_chain_net.c b/modules/net/dap_chain_net.c
index 0b04a97d0f..0755a1b868 100644
--- a/modules/net/dap_chain_net.c
+++ b/modules/net/dap_chain_net.c
@@ -2675,8 +2675,8 @@ int dap_chain_datum_add(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t
         return -101;
     }
     dap_ledger_t *l_ledger = dap_chain_net_by_id(a_chain->net_id)->pub.ledger;
-    if ( dap_ledger_datum_is_blacklisted(l_ledger, *a_datum_hash) )
-        return log_it(L_ERROR, "Datum is blackilsted"), -100;
+    if ( dap_ledger_datum_is_enforced(l_ledger, a_datum_hash, false) )
+        return log_it(L_ERROR, "Datum is blacklisted"), -100;
     switch (a_datum->header.type_id) {
         case DAP_CHAIN_DATUM_DECREE: {
             dap_chain_datum_decree_t *l_decree = (dap_chain_datum_decree_t *)a_datum->data;
diff --git a/modules/type/dag/dap_chain_cs_dag.c b/modules/type/dag/dap_chain_cs_dag.c
index cc05a77946..31a20b53bf 100644
--- a/modules/type/dag/dap_chain_cs_dag.c
+++ b/modules/type/dag/dap_chain_cs_dag.c
@@ -879,7 +879,7 @@ void s_dag_events_lasts_process_new_last_event(dap_chain_cs_dag_t *a_dag, dap_ch
     s_dag_events_lasts_delete_linked_with_event(a_dag, a_event_item->event);
     //add self
     dap_chain_cs_dag_event_item_t *l_event_last = DAP_DUP_RET_IF_FAIL(a_event_item);
-    HASH_ADD(hh,PVT(a_dag)->events_lasts_unlinked,hash, sizeof(l_event_last->hash),l_event_last);
+    HASH_ADD(hh, PVT(a_dag)->events_lasts_unlinked, hash, sizeof(l_event_last->hash), l_event_last);
 }
 
 
-- 
GitLab


From d6ac2548d7786bc1cd9bb2144967072bd9e4c68c Mon Sep 17 00:00:00 2001
From: "Constantin P." <papizh.konstantin@demlabs.net>
Date: Fri, 21 Feb 2025 16:17:47 +0700
Subject: [PATCH 10/15] ...

---
 dap-sdk                                      | 2 +-
 modules/chain/tests/dap_chain_ledger_tests.c | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/dap-sdk b/dap-sdk
index d9b03ffb93..c5e865f2c9 160000
--- a/dap-sdk
+++ b/dap-sdk
@@ -1 +1 @@
-Subproject commit d9b03ffb93d4003cb1a33c6470612a7f88ad6005
+Subproject commit c5e865f2c9bfbc6a8770525d40bd1834c142e190
diff --git a/modules/chain/tests/dap_chain_ledger_tests.c b/modules/chain/tests/dap_chain_ledger_tests.c
index 0b762718ae..5c49802de3 100644
--- a/modules/chain/tests/dap_chain_ledger_tests.c
+++ b/modules/chain/tests/dap_chain_ledger_tests.c
@@ -866,12 +866,12 @@ void dap_ledger_test_write_back_list(dap_ledger_t *a_ledger, dap_cert_t *a_cert,
         DAP_DELETE(l_ledger_tx_add_str);
         dap_hash_fast_t l_tx_addr4_hash = {0};
         dap_chain_datum_tx_t *l_tx_to_addr4 = dap_ledger_test_create_tx(l_addr_1->enc_key, &l_btx_addr1_hash,
-                                                                              l_addr_4->addr, dap_chain_uint256_from(s_total_supply-s_fee));
+                                                                              l_addr_4->addr, dap_chain_uint256_from(s_total_supply/*-s_fee*/));
         dap_hash_fast(l_tx_to_addr4, dap_chain_datum_tx_get_size(l_tx_to_addr4), &l_tx_addr4_hash);
         dap_assert_PIF(!dap_ledger_tx_add(a_ledger, l_tx_to_addr4, &l_tx_addr4_hash, false, NULL),
                        "Can't add transaction to address from white list in ledger");
         dap_chain_datum_tx_t *l_tx_to_addr3 = dap_ledger_test_create_tx(l_addr_4->enc_key, &l_tx_addr4_hash,
-                                                                              l_addr_3->addr, dap_chain_uint256_from(s_total_supply-s_fee));
+                                                                              l_addr_3->addr, dap_chain_uint256_from(s_total_supply/*-s_fee*/));
         dap_hash_fast_t l_tx_addr3_hash = {0};
         dap_hash_fast(l_tx_to_addr3, dap_chain_datum_tx_get_size(l_tx_to_addr3), &l_tx_addr3_hash);
         int res_add_tx = dap_ledger_tx_add(a_ledger, l_tx_to_addr3, &l_tx_addr3_hash, false, NULL);
-- 
GitLab


From 31ccb48a175b69a6b3aef51c6a8bc0856f44e063 Mon Sep 17 00:00:00 2001
From: "Constantin P." <papizh.konstantin@demlabs.net>
Date: Fri, 21 Feb 2025 17:08:22 +0700
Subject: [PATCH 11/15] More ports

---
 modules/chain/dap_chain.c                    |  9 +++++----
 modules/chain/include/dap_chain.h            |  4 ++--
 modules/node-cli/dap_chain_node_cli.c        |  2 +-
 modules/node-cli/dap_chain_node_cli_cmd_tx.c | 18 ++++++++----------
 modules/type/blocks/dap_chain_cs_blocks.c    |  2 +-
 5 files changed, 17 insertions(+), 18 deletions(-)

diff --git a/modules/chain/dap_chain.c b/modules/chain/dap_chain.c
index d85b8c0bc1..29bc7d426a 100644
--- a/modules/chain/dap_chain.c
+++ b/modules/chain/dap_chain.c
@@ -751,6 +751,7 @@ struct chain_thread_datum_removed_notifier {
     dap_chain_t *chain;
     dap_chain_cell_id_t cell_id;
     dap_hash_fast_t hash;
+    dap_chain_datum_t *datum;
     int ret_code;
 };
 
@@ -781,7 +782,7 @@ static bool s_notify_datum_removed_on_thread(void *a_arg)
 {
     struct chain_thread_datum_removed_notifier *l_arg = a_arg;
     assert(l_arg->callback);
-    l_arg->callback(l_arg->callback_arg, &l_arg->hash);
+    l_arg->callback(l_arg->callback_arg, &l_arg->hash, l_arg->datum);
     DAP_DELETE(l_arg);
     return false;
 }
@@ -891,7 +892,7 @@ void dap_chain_datum_notify(dap_chain_t *a_chain, dap_chain_cell_id_t a_cell_id,
     }
 }
 
-void dap_chain_datum_removed_notify(dap_chain_t *a_chain, dap_chain_cell_id_t a_cell_id, dap_hash_fast_t *a_hash) {
+void dap_chain_datum_removed_notify(dap_chain_t *a_chain, dap_chain_cell_id_t a_cell_id, dap_hash_fast_t *a_hash, dap_chain_datum_t *a_datum) {
 #ifdef DAP_CHAIN_BLOCKS_TEST
     return;
 #endif
@@ -908,8 +909,8 @@ void dap_chain_datum_removed_notify(dap_chain_t *a_chain, dap_chain_cell_id_t a_
         }
         *l_arg = (struct chain_thread_datum_removed_notifier) {
             .callback = l_notifier->callback, .callback_arg = l_notifier->arg,
-            .chain = a_chain,     .cell_id = a_cell_id,
-            .hash = *a_hash};
+            .chain = a_chain,   .cell_id = a_cell_id,
+            .hash = *a_hash,    .datum = a_datum };
         dap_proc_thread_callback_add_pri(l_notifier->proc_thread, s_notify_datum_removed_on_thread, l_arg, DAP_QUEUE_MSG_PRIORITY_LOW);
     }
 }
diff --git a/modules/chain/include/dap_chain.h b/modules/chain/include/dap_chain.h
index 62882bda5a..0fd5a0b9c5 100644
--- a/modules/chain/include/dap_chain.h
+++ b/modules/chain/include/dap_chain.h
@@ -130,7 +130,7 @@ typedef size_t (*dap_chain_callback_add_datums_t)(dap_chain_t * , dap_chain_datu
 typedef void (*dap_chain_callback_notify_t)(void *a_arg, dap_chain_t *a_chain, dap_chain_cell_id_t a_id, dap_chain_hash_fast_t *a_atom_hash, void *a_atom, size_t a_atom_size); //change in chain happened
 typedef void (*dap_chain_callback_datum_notify_t)(void *a_arg, dap_chain_hash_fast_t *a_datum_hash, dap_chain_hash_fast_t *a_atom_hash, void *a_datum, 
                                     size_t a_datum_size, int a_ret_code, uint32_t a_action, dap_chain_srv_uid_t a_uid); //change in chain happened
-typedef void (*dap_chain_callback_datum_removed_notify_t)(void *a_arg, dap_chain_hash_fast_t *a_datum_hash); //change in chain happened
+typedef void (*dap_chain_callback_datum_removed_notify_t)(void *a_arg, dap_chain_hash_fast_t *a_datum_hash, dap_chain_datum_t *a_datum); //change in chain happened
 
 typedef uint64_t (*dap_chain_callback_get_count)(dap_chain_t *a_chain);
 typedef dap_list_t *(*dap_chain_callback_get_list)(dap_chain_t *a_chain, size_t a_count, size_t a_page, bool a_reverse);
@@ -316,7 +316,7 @@ void dap_chain_add_callback_datum_removed_from_index_notify(dap_chain_t *a_chain
 void dap_chain_atom_confirmed_notify_add(dap_chain_t *a_chain, dap_chain_callback_notify_t a_callback, void *a_arg, uint64_t a_conf_cnt);
 void dap_chain_atom_notify(dap_chain_t *a_chain, dap_chain_cell_id_t a_cell_id, dap_hash_fast_t *a_hash, const uint8_t *a_atom, size_t a_atom_size);
 void dap_chain_datum_notify(dap_chain_t *a_chain, dap_chain_cell_id_t a_cell_id,  dap_hash_fast_t *a_hash, dap_chain_hash_fast_t *a_atom_hash, const uint8_t *a_datum, size_t a_datum_size, int a_ret_code, uint32_t a_action, dap_chain_srv_uid_t a_uid);
-void dap_chain_datum_removed_notify(dap_chain_t *a_chain, dap_chain_cell_id_t a_cell_id, dap_hash_fast_t *a_hash);
+void dap_chain_datum_removed_notify(dap_chain_t *a_chain, dap_chain_cell_id_t a_cell_id, dap_hash_fast_t *a_hash, dap_chain_datum_t *a_datum);
 void dap_chain_atom_add_from_threshold(dap_chain_t *a_chain);
 dap_chain_atom_ptr_t dap_chain_get_atom_by_hash(dap_chain_t * a_chain, dap_chain_hash_fast_t * a_atom_hash, size_t * a_atom_size);
 bool dap_chain_get_atom_last_hash_num_ts(dap_chain_t *a_chain, dap_chain_cell_id_t a_cell_id, dap_hash_fast_t *a_atom_hash, uint64_t *a_atom_num, dap_time_t *a_atom_timestamp);
diff --git a/modules/node-cli/dap_chain_node_cli.c b/modules/node-cli/dap_chain_node_cli.c
index 41b2d87d23..39ad250158 100644
--- a/modules/node-cli/dap_chain_node_cli.c
+++ b/modules/node-cli/dap_chain_node_cli.c
@@ -229,7 +229,7 @@ int dap_chain_node_cli_init(dap_config_t * g_config)
                             "wallet activate -w <wallet_name> -password <password> [-ttl <password_ttl_in_minutes>]\n"
                             "wallet deactivate -w <wallet_name> -password <password>\n"
                             "wallet convert -w <wallet_name> -password <password>\n"
-                            "wallet outputs {-addr <addr> | -w <wallet_name>} -net <net_name> -token <token_tiker> [-value <uint256_value>]");
+                            "wallet outputs {-addr <addr> | -w <wallet_name>} -net <net_name> -token <token_tiker> [-value <uint256_value>]\n");
 
     dap_cli_server_cmd_add("mempool", com_mempool, "Command for working with mempool",
                            "mempool list -net <net_name> [-chain <chain_name>] [-addr <addr>] [-brief] [-limit] [-offset]\n"
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 598d41fa35..535e5608e8 100644
--- a/modules/node-cli/dap_chain_node_cli_cmd_tx.c
+++ b/modules/node-cli/dap_chain_node_cli_cmd_tx.c
@@ -414,10 +414,6 @@ json_object* dap_db_history_addr(json_object* a_json_arr_reply, dap_chain_addr_t
         uint256_t l_corr_value = {}, l_cond_value = {};
         bool l_recv_from_cond = false, l_send_to_same_cond = false;
         json_object *l_corr_object = NULL, *l_cond_recv_object = NULL, *l_cond_send_object = NULL;
-        dap_list_t *l_list_in_items = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN_ALL, NULL);
-        if (!l_list_in_items) // a bad tx
-            goto next_step;
-        // all in items should be from the same address
         
         dap_chain_addr_t *l_src_addr = NULL;
         bool l_base_tx = false, l_reward_collect = false;
@@ -431,23 +427,26 @@ json_object* dap_db_history_addr(json_object* a_json_arr_reply, dap_chain_addr_t
         dap_chain_srv_uid_t l_uid = l_from_cache ? l_wallet_cache_iter->uid : l_datum_iter->uid;
 
         int l_src_subtype = DAP_CHAIN_TX_OUT_COND_SUBTYPE_UNDEFINED;
-        for (dap_list_t *it = l_list_in_items; it; it = it->next) {
+        uint8_t *l_tx_item = NULL;
+        size_t l_size; int i, q = 0;
+        // Проход по входам
+        TX_ITEM_ITER_TX_TYPE(l_tx_item, TX_ITEM_TYPE_IN_ALL, l_size, i, l_tx) {
             dap_chain_hash_fast_t *l_tx_prev_hash = NULL;
             int l_tx_prev_out_idx;
             dap_chain_datum_tx_t *l_tx_prev = NULL;
-            switch (*(byte_t *)it->data) {
+            switch (*l_tx_item) {
             case TX_ITEM_TYPE_IN: {
-                dap_chain_tx_in_t *l_tx_in = (dap_chain_tx_in_t *)it->data;
+                dap_chain_tx_in_t *l_tx_in = (dap_chain_tx_in_t *)l_tx_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 *)it->data;
+                dap_chain_tx_in_cond_t *l_tx_in_cond = (dap_chain_tx_in_cond_t *)l_tx_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;
             case TX_ITEM_TYPE_IN_EMS: {
-                dap_chain_tx_in_ems_t *l_tx_in_ems = (dap_chain_tx_in_ems_t *)it->data;
+                dap_chain_tx_in_ems_t *l_tx_in_ems = (dap_chain_tx_in_ems_t *)l_tx_item;
                 l_base_tx = true;
                 l_noaddr_token = l_tx_in_ems->header.ticker;
             } break;
@@ -492,7 +491,6 @@ json_object* dap_db_history_addr(json_object* a_json_arr_reply, dap_chain_addr_t
                 break;  //it's not our addr
             
         }        
-        dap_list_free(l_list_in_items);
 
         // find OUT items
         bool l_header_printed = false;
diff --git a/modules/type/blocks/dap_chain_cs_blocks.c b/modules/type/blocks/dap_chain_cs_blocks.c
index 571a4385ff..2f87bdb0f5 100644
--- a/modules/type/blocks/dap_chain_cs_blocks.c
+++ b/modules/type/blocks/dap_chain_cs_blocks.c
@@ -1631,7 +1631,7 @@ static int s_delete_atom_datums(dap_chain_cs_blocks_t *a_blocks, dap_chain_block
             l_ret++;
             HASH_DEL(PVT(a_blocks)->datum_index, l_datum_index);
             // notify datum removed
-            dap_chain_datum_removed_notify(a_blocks->chain, a_block_cache->block->hdr.cell_id, l_datum_hash);
+            dap_chain_datum_removed_notify(a_blocks->chain, a_block_cache->block->hdr.cell_id, l_datum_hash, l_datum);
         }
     }
     debug_if(s_debug_more, L_DEBUG, "Block %s checked, %s", a_block_cache->block_hash_str,
-- 
GitLab


From a4db8329bce1d31cfff8285360437522a4454df7 Mon Sep 17 00:00:00 2001
From: "Constantin P." <papizh.konstantin@demlabs.net>
Date: Fri, 21 Feb 2025 17:24:47 +0700
Subject: [PATCH 12/15] ...

---
 modules/ledger/dap_chain_ledger_tx.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/modules/ledger/dap_chain_ledger_tx.c b/modules/ledger/dap_chain_ledger_tx.c
index 17368e14cf..631a9690f3 100644
--- a/modules/ledger/dap_chain_ledger_tx.c
+++ b/modules/ledger/dap_chain_ledger_tx.c
@@ -812,6 +812,7 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
         switch ( HASH_COUNT(l_values_from_prev_tx) ) {
         case 1:
             l_main_ticker = l_value_cur->token_ticker;
+            break;
         case 2:
             HASH_FIND_STR(l_values_from_prev_tx, a_ledger->net->pub.native_ticker, l_value_cur);
             if (l_value_cur) {
-- 
GitLab


From 2ff7d7c5ab16c927ebbaae01eddddc57984dae45 Mon Sep 17 00:00:00 2001
From: "Constantin P." <papizh.konstantin@demlabs.net>
Date: Fri, 21 Feb 2025 19:14:18 +0700
Subject: [PATCH 13/15] ...

---
 modules/ledger/dap_chain_ledger.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/ledger/dap_chain_ledger.c b/modules/ledger/dap_chain_ledger.c
index 9a11c0d655..1781764b02 100644
--- a/modules/ledger/dap_chain_ledger.c
+++ b/modules/ledger/dap_chain_ledger.c
@@ -279,7 +279,7 @@ void dap_ledger_handle_free(dap_ledger_t *a_ledger)
 bool dap_ledger_datum_is_enforced(dap_ledger_t *a_ledger, dap_hash_fast_t *a_hash, bool a_accept) {
     dap_ledger_hal_item_t *l_wanted = NULL;
     HASH_FIND(hh, a_accept ? PVT(a_ledger)->hal_items : PVT(a_ledger)->hrl_items, a_hash, sizeof(dap_hash_fast_t), l_wanted);
-    debug_if(g_debug_ledger && l_wanted, L_DEBUG, "Datum %s is %slisted", dap_hash_fast_to_str_static(&a_hash), a_accept ? "white" : "black");
+    debug_if(g_debug_ledger && l_wanted, L_DEBUG, "Datum %s is %slisted", dap_hash_fast_to_str_static(a_hash), a_accept ? "white" : "black");
     return !!l_wanted;
 }
 
-- 
GitLab


From 2373335edb1dbb2daae349a8d6db28a3ea6e87a0 Mon Sep 17 00:00:00 2001
From: "Constantin P." <papizh.konstantin@demlabs.net>
Date: Mon, 24 Feb 2025 14:31:27 +0700
Subject: [PATCH 14/15] Improvements + segv fix

---
 modules/chain/dap_chain_cell.c         | 62 +++++++++++++++-----------
 modules/chain/include/dap_chain_cell.h |  1 +
 2 files changed, 37 insertions(+), 26 deletions(-)

diff --git a/modules/chain/dap_chain_cell.c b/modules/chain/dap_chain_cell.c
index cf8255d136..9e905f80ff 100644
--- a/modules/chain/dap_chain_cell.c
+++ b/modules/chain/dap_chain_cell.c
@@ -380,31 +380,14 @@ DAP_STATIC_INLINE int s_cell_load_from_file(dap_chain_cell_t *a_cell)
     return l_ret;
 }
 
-DAP_STATIC_INLINE int s_cell_open(dap_chain_t *a_chain, const char *a_filename, const char a_mode) {
-    dap_chain_cell_id_t l_cell_id = { };
-    { /* Check filename */
-        char l_fmt[20] = "", l_ext[ sizeof(CELL_FILE_EXT) ] = "", l_ext2 = '\0';
-        snprintf(l_fmt, sizeof(l_fmt), "%s%lu%s", "%"DAP_UINT64_FORMAT_x".%", sizeof(CELL_FILE_EXT) - 1, "[^.].%c");
-
-        switch ( sscanf(a_filename, l_fmt, &l_cell_id.uint64, l_ext, &l_ext2) ) {
-        case 3:
-            // TODO: X.dchaincell.*
-        case 2:
-            if ( !dap_strncmp(l_ext, CELL_FILE_EXT, sizeof(l_ext)) )
-                break;
-        default:
-            return log_it(L_ERROR, "Invalid cell file name \"%s\"", a_filename), EINVAL;
-        }
-    }
-    char file_storage_path[MAX_PATH], mode[] = { a_mode, '+', 'b', '\0' };
-    snprintf(file_storage_path, MAX_PATH, "%s/%s", DAP_CHAIN_PVT(a_chain)->file_storage_dir, a_filename);
+DAP_STATIC_INLINE int s_cell_open(dap_chain_t *a_chain, const char *a_filepath, dap_chain_cell_id_t a_cell_id, const char a_mode) {
+    char mode[] = { a_mode, '+', 'b', '\0' }, *const a_filename = strrchr(a_filepath, '/') + 1;
     dap_chain_cell_t *l_cell = NULL;
 
 #define m_ret_err(err, ...) return ({ if (l_cell->file_storage) fclose(l_cell->file_storage); \
                                       DAP_DELETE(l_cell); log_it(L_ERROR, ##__VA_ARGS__), err; })
 
-    dap_chain_cell_mmap_data_t l_cell_map_data = { };
-    HASH_FIND(hh, a_chain->cells, &l_cell_id, sizeof(dap_chain_cell_id_t), l_cell);
+    HASH_FIND(hh, a_chain->cells, &a_cell_id, sizeof(dap_chain_cell_id_t), l_cell);
     if (l_cell) {
         if (a_mode == 'w') {
             /* Attention! File rewriting requires that ledger was already purged */
@@ -415,19 +398,20 @@ DAP_STATIC_INLINE int s_cell_open(dap_chain_t *a_chain, const char *a_filename,
             m_ret_err(EEXIST, "Cell \"%s\" is already loaded in chain \"%s : %s\"",
                               a_filename, a_chain->net_name, a_chain->name);
     }
-    FILE *l_file = fopen(file_storage_path, mode);
+    FILE *l_file = fopen(a_filepath, mode);
     if ( !l_file )
         m_ret_err(errno, "Cell \"%s : %s / \"%s\" cannot be opened, error %d",
                          a_chain->net_name, a_chain->name, a_filename, errno);
 
     l_cell = DAP_NEW_Z(dap_chain_cell_t);
     *l_cell = (dap_chain_cell_t) {
-        .id             = l_cell_id,
+        .id             = a_cell_id,
         .chain          = a_chain,
+        .mapping        = a_chain->is_mapped ? DAP_NEW_Z(dap_chain_cell_mmap_data_t) : NULL,
         .file_storage   = l_file,
         //.storage_rwlock = PTHREAD_RWLOCK_INITIALIZER
     };
-    dap_strncpy(l_cell->file_storage_path, file_storage_path, MAX_PATH);
+    dap_strncpy(l_cell->file_storage_path, a_filepath, MAX_PATH);
 
     switch (*mode) {
     case 'a': {
@@ -448,12 +432,12 @@ DAP_STATIC_INLINE int s_cell_open(dap_chain_t *a_chain, const char *a_filename,
             .type           = DAP_CHAIN_CELL_FILE_TYPE_RAW,
             .chain_id       = a_chain->id,
             .chain_net_id   = a_chain->net_id,
-            .cell_id        = l_cell_id
+            .cell_id        = a_cell_id
         };
         if ( !fwrite(&l_hdr, sizeof(l_hdr), 1, l_cell->file_storage) )
             m_ret_err(errno, "fwrite() error %d", errno);
         fflush(l_cell->file_storage);
-        l_cell->file_storage = freopen(file_storage_path, "a+b", l_cell->file_storage);
+        l_cell->file_storage = freopen(a_filepath, "a+b", l_cell->file_storage);
         if ( a_chain->is_mapped && s_cell_map_new_volume(l_cell, 0, false) )
             m_ret_err(EINVAL, "Error on mapping the first volume");
     }
@@ -468,8 +452,34 @@ DAP_STATIC_INLINE int s_cell_open(dap_chain_t *a_chain, const char *a_filename,
 }
 
 int dap_chain_cell_open(dap_chain_t *a_chain, const char *a_filename, const char a_mode) {
+    dap_chain_cell_id_t l_cell_id = { };
+    { /* Check filename */
+        char l_fmt[32] = "", l_ext[ sizeof(CELL_FILE_EXT) ] = "", l_ext2 = '\0';
+        snprintf(l_fmt, sizeof(l_fmt), "%s%lu%s", "%"DAP_UINT64_FORMAT_x".%", sizeof(CELL_FILE_EXT) - 1, "[^.].%c");
+
+        switch ( sscanf(a_filename, l_fmt, &l_cell_id.uint64, l_ext, &l_ext2) ) {
+        case 3:
+            // TODO: X.dchaincell.*
+        case 2:
+            if ( !dap_strncmp(l_ext, CELL_FILE_EXT, sizeof(l_ext)) )
+                break;
+        default:
+            return log_it(L_ERROR, "Invalid cell file name \"%s\"", a_filename), EINVAL;
+        }
+    }
+    const char l_full_path[MAX_PATH];
+    snprintf(l_full_path, MAX_PATH, "%s/%s", DAP_CHAIN_PVT(a_chain)->file_storage_dir, a_filename);
+    pthread_rwlock_wrlock(&a_chain->cell_rwlock);
+    int l_ret = s_cell_open(a_chain, l_full_path, l_cell_id, a_mode);
+    pthread_rwlock_unlock(&a_chain->cell_rwlock);
+    return l_ret;
+}
+
+int dap_chain_cell_open_by_id(dap_chain_t *a_chain, const dap_chain_cell_id_t a_cell_id, const char a_mode) {
+    const char l_full_path[MAX_PATH];
+    snprintf(l_full_path, MAX_PATH, "%s/%"DAP_UINT64_FORMAT_x"."CELL_FILE_EXT, DAP_CHAIN_PVT(a_chain)->file_storage_dir, a_cell_id.uint64);
     pthread_rwlock_wrlock(&a_chain->cell_rwlock);
-    int l_ret = s_cell_open(a_chain, a_filename, a_mode);
+    int l_ret = s_cell_open(a_chain, l_full_path, a_cell_id, a_mode);
     pthread_rwlock_unlock(&a_chain->cell_rwlock);
     return l_ret;
 }
diff --git a/modules/chain/include/dap_chain_cell.h b/modules/chain/include/dap_chain_cell.h
index 74efe15fdb..93df116932 100644
--- a/modules/chain/include/dap_chain_cell.h
+++ b/modules/chain/include/dap_chain_cell.h
@@ -77,6 +77,7 @@ typedef struct dap_chain_cell_decl{
 
 int dap_chain_cell_init(void);
 int dap_chain_cell_open(dap_chain_t *a_chain, const char *a_filename, const char a_mode);
+int dap_chain_cell_open_by_id(dap_chain_t *a_chain, const dap_chain_cell_id_t a_cell_id, const char a_mode);
 
 DAP_INLINE dap_chain_cell_t *dap_chain_cell_find_by_id(dap_chain_t *a_chain, dap_chain_cell_id_t a_cell_id) {
     dap_chain_cell_t *l_cell = NULL;
-- 
GitLab


From 58585afbb9346ecb9a4b5d176ecba8dac17fb23a Mon Sep 17 00:00:00 2001
From: "Constantin P." <papizh.konstantin@demlabs.net>
Date: Mon, 24 Feb 2025 15:23:52 +0700
Subject: [PATCH 15/15] Warnings fix

---
 modules/chain/dap_chain_cell.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/modules/chain/dap_chain_cell.c b/modules/chain/dap_chain_cell.c
index 9e905f80ff..f2b61e19c8 100644
--- a/modules/chain/dap_chain_cell.c
+++ b/modules/chain/dap_chain_cell.c
@@ -467,7 +467,7 @@ int dap_chain_cell_open(dap_chain_t *a_chain, const char *a_filename, const char
             return log_it(L_ERROR, "Invalid cell file name \"%s\"", a_filename), EINVAL;
         }
     }
-    const char l_full_path[MAX_PATH];
+    char l_full_path[MAX_PATH];
     snprintf(l_full_path, MAX_PATH, "%s/%s", DAP_CHAIN_PVT(a_chain)->file_storage_dir, a_filename);
     pthread_rwlock_wrlock(&a_chain->cell_rwlock);
     int l_ret = s_cell_open(a_chain, l_full_path, l_cell_id, a_mode);
@@ -476,7 +476,7 @@ int dap_chain_cell_open(dap_chain_t *a_chain, const char *a_filename, const char
 }
 
 int dap_chain_cell_open_by_id(dap_chain_t *a_chain, const dap_chain_cell_id_t a_cell_id, const char a_mode) {
-    const char l_full_path[MAX_PATH];
+    char l_full_path[MAX_PATH];
     snprintf(l_full_path, MAX_PATH, "%s/%"DAP_UINT64_FORMAT_x"."CELL_FILE_EXT, DAP_CHAIN_PVT(a_chain)->file_storage_dir, a_cell_id.uint64);
     pthread_rwlock_wrlock(&a_chain->cell_rwlock);
     int l_ret = s_cell_open(a_chain, l_full_path, a_cell_id, a_mode);
-- 
GitLab