From 4a028c94fc1b62d37ef6f520809663213bbc0505 Mon Sep 17 00:00:00 2001
From: Roman Khlopkov <roman.khlopkov@demlabs.net>
Date: Wed, 23 Oct 2024 09:59:00 +0000
Subject: [PATCH] feature-13119

---
 modules/ledger/dap_chain_ledger.c             |  20 ---
 modules/ledger/dap_chain_ledger_anchor.c      |  35 +++--
 modules/ledger/dap_chain_ledger_decree.c      |  54 +++++++-
 modules/ledger/dap_chain_ledger_tx.c          | 121 ++++++++++++++++--
 modules/ledger/include/dap_chain_ledger.h     |  30 ++++-
 modules/ledger/include/dap_chain_ledger_pvt.h |   3 +-
 modules/node-cli/dap_chain_node_cli_cmd.c     |   7 +-
 .../xchange/dap_chain_net_srv_xchange.c       |   2 +-
 8 files changed, 215 insertions(+), 57 deletions(-)

diff --git a/modules/ledger/dap_chain_ledger.c b/modules/ledger/dap_chain_ledger.c
index 45727c0653..0dfed46074 100644
--- a/modules/ledger/dap_chain_ledger.c
+++ b/modules/ledger/dap_chain_ledger.c
@@ -1003,12 +1003,6 @@ void dap_ledger_purge(dap_ledger_t *a_ledger, bool a_preserve_db)
         DAP_DELETE(l_gdb_group);
     }
 
-    if (!a_preserve_db) {
-        l_gdb_group = dap_ledger_get_gdb_group(a_ledger, DAP_LEDGER_SPENT_TXS_STR);
-        dap_global_db_erase_table(l_gdb_group, NULL, NULL);
-        DAP_DELETE(l_gdb_group);
-    }
-
     /* Delete balances */
     dap_ledger_wallet_balance_t *l_balance_current, *l_balance_tmp;
     HASH_ITER(hh, l_ledger_pvt->balance_accounts, l_balance_current, l_balance_tmp) {
@@ -1713,17 +1707,3 @@ bool dap_ledger_cache_enabled(dap_ledger_t *a_ledger)
 {
     return PVT(a_ledger)->cached;
 }
-
-void dap_ledger_set_cache_tx_check_callback(dap_ledger_t *a_ledger, dap_ledger_cache_tx_check_callback_t a_callback)
-{
-    PVT(a_ledger)->cache_tx_check_callback = a_callback;
-}
-
-dap_list_t *dap_ledger_states_aggregate(dap_ledger_t *a_ledger)
-{
-    dap_list_t *ret = NULL;
-    dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger);
-    pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock);
-    pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock);
-    return ret;
-}
diff --git a/modules/ledger/dap_chain_ledger_anchor.c b/modules/ledger/dap_chain_ledger_anchor.c
index 8a517fa28e..4165b24a66 100644
--- a/modules/ledger/dap_chain_ledger_anchor.c
+++ b/modules/ledger/dap_chain_ledger_anchor.c
@@ -147,7 +147,7 @@ int dap_ledger_anchor_load(dap_chain_datum_anchor_t *a_anchor, dap_chain_t *a_ch
         return -109;
     }
 
-    if ((ret_val = dap_ledger_decree_apply(&l_hash, NULL, a_chain, true)) != 0){
+    if ((ret_val = dap_ledger_decree_apply(&l_hash, NULL, a_chain, a_anchor_hash)) != 0){
         debug_if(g_debug_ledger, L_WARNING, "Decree applying failed");
         return ret_val;
     }
@@ -173,16 +173,28 @@ int dap_ledger_anchor_load(dap_chain_datum_anchor_t *a_anchor, dap_chain_t *a_ch
     return ret_val;
 }
 
-dap_chain_datum_anchor_t * s_find_previous_anchor(dap_hash_fast_t *a_old_anchor_hash, dap_chain_net_t *a_net)
+static dap_ledger_anchor_item_t *s_find_anchor(dap_ledger_t *a_ledger, dap_hash_fast_t *a_anchor_hash)
 {
-    dap_chain_datum_anchor_t * l_ret_anchor = NULL;
-    dap_return_val_if_fail(a_old_anchor_hash && a_net, NULL);
-
-    dap_ledger_private_t *l_ledger_pvt = PVT(a_net->pub.ledger);
     dap_ledger_anchor_item_t *l_anchor = NULL;
+    dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger);
     pthread_rwlock_rdlock(&l_ledger_pvt->decrees_rwlock);
-    HASH_FIND(hh, l_ledger_pvt->anchors, a_old_anchor_hash, sizeof(dap_hash_fast_t), l_anchor);
+    HASH_FIND(hh, l_ledger_pvt->anchors, a_anchor_hash, sizeof(dap_hash_fast_t), l_anchor);
     pthread_rwlock_unlock(&l_ledger_pvt->decrees_rwlock);
+    return l_anchor;
+}
+
+dap_chain_datum_anchor_t *dap_ledger_anchor_find(dap_ledger_t *a_ledger, dap_hash_fast_t *a_anchor_hash)
+{
+    dap_ledger_anchor_item_t *l_anchor = s_find_anchor(a_ledger, a_anchor_hash);
+    return l_anchor ? l_anchor->anchor : NULL;
+}
+
+static dap_chain_datum_anchor_t *s_find_previous_anchor(dap_hash_fast_t *a_old_anchor_hash, dap_chain_net_t *a_net)
+{
+    dap_chain_datum_anchor_t * l_ret_anchor = NULL;
+    dap_return_val_if_fail(a_old_anchor_hash && a_net, NULL);
+
+    dap_ledger_anchor_item_t *l_anchor = s_find_anchor(a_net->pub.ledger, a_old_anchor_hash);
     if (!l_anchor) {
         log_it(L_WARNING, "Can not find anchor");
         return NULL;
@@ -196,6 +208,7 @@ dap_chain_datum_anchor_t * s_find_previous_anchor(dap_hash_fast_t *a_old_anchor_
     uint16_t l_old_decree_type = l_old_decree->header.type;
     uint16_t l_old_decree_subtype = l_old_decree->header.sub_type;
 
+    dap_ledger_private_t *l_ledger_pvt = PVT(a_net->pub.ledger);
     dap_ledger_anchor_item_t *l_anchors = HASH_LAST(l_ledger_pvt->anchors);
     for(; l_anchors; l_anchors = l_anchors->hh.prev){
         size_t l_datums_count = 0;
@@ -296,7 +309,7 @@ int dap_ledger_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t *a
                         return -109;
                     }
 
-                    if((ret_val = dap_ledger_decree_apply(&l_hash, NULL, a_chain, true))!=0){
+                    if((ret_val = dap_ledger_decree_apply(&l_hash, NULL, a_chain, a_anchor_hash))!=0){
                         log_it(L_WARNING,"Decree applying failed");
                         return ret_val;
                     }
@@ -332,7 +345,7 @@ int dap_ledger_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t *a
                         log_it(L_WARNING,"Can not find datum hash in anchor data");
                         return -109;
                     }
-                    if((ret_val = dap_ledger_decree_apply(&l_hash, NULL, a_chain, true))!=0){
+                    if((ret_val = dap_ledger_decree_apply(&l_hash, NULL, a_chain, a_anchor_hash))!=0){
                         log_it(L_WARNING,"Decree applying failed");
                         return ret_val;
                     }
@@ -349,7 +362,7 @@ int dap_ledger_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t *a
                         log_it(L_WARNING,"Can not find datum hash in anchor data");
                         return -109;
                     }
-                    if((ret_val = dap_ledger_decree_apply(&l_hash, NULL, a_chain, true))!=0){
+                    if((ret_val = dap_ledger_decree_apply(&l_hash, NULL, a_chain, a_anchor_hash))!=0){
                         log_it(L_WARNING,"Decree applying failed");
                         return ret_val;
                     }
@@ -370,7 +383,7 @@ int dap_ledger_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t *a
                         return -109;
                     }
 
-                    if((ret_val = dap_ledger_decree_apply(&l_hash, NULL, a_chain, true))!=0){
+                    if((ret_val = dap_ledger_decree_apply(&l_hash, NULL, a_chain, a_anchor_hash))!=0){
                         log_it(L_WARNING,"Decree applying failed");
                         return ret_val;
                     }
diff --git a/modules/ledger/dap_chain_ledger_decree.c b/modules/ledger/dap_chain_ledger_decree.c
index 6e662fcfac..04cefb9a0a 100644
--- a/modules/ledger/dap_chain_ledger_decree.c
+++ b/modules/ledger/dap_chain_ledger_decree.c
@@ -200,7 +200,7 @@ int dap_ledger_decree_verify(dap_chain_net_t *a_net, dap_chain_datum_decree_t *a
     return s_decree_verify(a_net, a_decree, a_data_size, a_decree_hash, false);
 }
 
-int dap_ledger_decree_apply(dap_hash_fast_t *a_decree_hash, dap_chain_datum_decree_t *a_decree, dap_chain_t *a_chain, bool a_anchored)
+int dap_ledger_decree_apply(dap_hash_fast_t *a_decree_hash, dap_chain_datum_decree_t *a_decree, dap_chain_t *a_chain, dap_hash_fast_t *a_anchor_hash)
 {
     dap_return_val_if_fail(a_decree_hash && a_chain, -107);
     int ret_val = 0;
@@ -223,8 +223,8 @@ int dap_ledger_decree_apply(dap_hash_fast_t *a_decree_hash, dap_chain_datum_decr
             pthread_rwlock_unlock(&l_ledger_pvt->decrees_rwlock);
             return -1;
         }
-        l_new_decree->key = *a_decree_hash;
-        HASH_ADD_BYHASHVALUE(hh, l_ledger_pvt->decrees, key, sizeof(dap_hash_fast_t), l_hash_value, l_new_decree);
+        l_new_decree->decree_hash = *a_decree_hash;
+        HASH_ADD_BYHASHVALUE(hh, l_ledger_pvt->decrees, decree_hash, sizeof(dap_hash_fast_t), l_hash_value, l_new_decree);
     }
     pthread_rwlock_unlock(&l_ledger_pvt->decrees_rwlock);
 
@@ -252,7 +252,7 @@ int dap_ledger_decree_apply(dap_hash_fast_t *a_decree_hash, dap_chain_datum_decr
     // Process decree
     switch(l_new_decree->decree->header.type) {
     case DAP_CHAIN_DATUM_DECREE_TYPE_COMMON:
-        ret_val = s_common_decree_handler(l_new_decree->decree, l_net, true, a_anchored);
+        ret_val = s_common_decree_handler(l_new_decree->decree, l_net, true, a_anchor_hash);
         break;
     case DAP_CHAIN_DATUM_DECREE_TYPE_SERVICE:
         ret_val = s_service_decree_handler(l_new_decree->decree, l_net, true);
@@ -265,6 +265,8 @@ int dap_ledger_decree_apply(dap_hash_fast_t *a_decree_hash, dap_chain_datum_decr
     if (!ret_val) {
         l_new_decree->is_applied = true;
         l_new_decree->wait_for_apply = false;
+        if (a_anchor_hash)
+            l_new_decree->anchor_hash = *a_anchor_hash;
     } else
         debug_if(g_debug_ledger, L_ERROR,"Decree applying failed!");
 
@@ -620,3 +622,47 @@ const dap_list_t *dap_ledger_decree_get_owners_pkeys(dap_ledger_t *a_ledger)
 {
     return PVT(a_ledger)->decree_owners_pkeys;
 }
+
+static int s_compare_anchors(dap_ledger_hardfork_anchors_t *a_list1, dap_ledger_hardfork_anchors_t *a_list2)
+{
+    return a_list1->decree_subtype != a_list2->decree_subtype;
+}
+
+
+int s_aggregate_anchor(dap_ledger_hardfork_anchors_t **a_out_list, uint16_t a_subtype, dap_chain_datum_anchor_t *a_anchor)
+{
+    dap_ledger_hardfork_anchors_t l_new_anchor = { .anchor = a_anchor, .decree_subtype = a_subtype };
+    dap_ledger_hardfork_anchors_t *l_exist = NULL;
+    DL_SEARCH(*a_out_list, l_exist, &l_new_anchor, s_compare_anchors);
+    if (!l_exist) {
+        l_exist = DAP_DUP(&l_new_anchor);
+        if (!l_exist) {
+            log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+            return -1;
+        }
+        DL_APPEND(*a_out_list, l_exist);
+    } else
+        l_exist->anchor = a_anchor;
+    return 0;
+}
+
+dap_ledger_hardfork_anchors_t *dap_ledger_anchors_aggregate(dap_ledger_t *a_ledger)
+{
+    dap_ledger_hardfork_anchors_t *ret = NULL;
+    dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger);
+    pthread_rwlock_rdlock(&l_ledger_pvt->decrees_rwlock);
+    for (dap_ledger_decree_item_t *it = l_ledger_pvt->decrees; it; it = it->hh.next)
+        if (it->is_applied && !dap_hash_fast_is_blank(&it->anchor_hash)) {
+            dap_chain_datum_anchor_t *l_anchor = dap_ledger_anchor_find(a_ledger, &it->anchor_hash);
+            if (!l_anchor) {
+                char l_anchor_hash_str[DAP_HASH_FAST_STR_SIZE];
+                dap_hash_fast_to_str(&it->anchor_hash, l_anchor_hash_str, DAP_HASH_FAST_STR_SIZE);
+                log_it(L_ERROR, "Can't find anchor %s for decree %s, skip it",
+                                            l_anchor_hash_str, dap_hash_fast_to_str_static(&it->decree_hash));
+                continue;
+            }
+            s_aggregate_anchor(&ret, it->decree->header.sub_type, l_anchor);
+        }
+    pthread_rwlock_unlock(&l_ledger_pvt->decrees_rwlock);
+    return ret;
+}
diff --git a/modules/ledger/dap_chain_ledger_tx.c b/modules/ledger/dap_chain_ledger_tx.c
index 9e3aa79afd..326df777e4 100644
--- a/modules/ledger/dap_chain_ledger_tx.c
+++ b/modules/ledger/dap_chain_ledger_tx.c
@@ -707,7 +707,7 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger,
 
                 // 5a. Check for condition owner
                 // Get owner tx
-                dap_hash_fast_t l_owner_tx_hash = dap_ledger_get_first_chain_tx_hash(a_ledger, l_tx_prev, l_tx_prev_out_cond);
+                dap_hash_fast_t l_owner_tx_hash = dap_ledger_get_first_chain_tx_hash(a_ledger, l_tx_prev_out_cond->header.subtype, l_tx_prev_hash);
                 dap_chain_datum_tx_t *l_owner_tx = dap_hash_fast_is_blank(&l_owner_tx_hash)
                     ? l_tx_prev
                     : dap_ledger_tx_find_by_hash(a_ledger, &l_owner_tx_hash);
@@ -1911,21 +1911,18 @@ dap_chain_datum_tx_t *dap_ledger_tx_unspent_find_by_hash(dap_ledger_t *a_ledger,
     return s_tx_find_by_hash(a_ledger, a_tx_hash, NULL, true);
 }
 
-dap_hash_fast_t dap_ledger_get_first_chain_tx_hash(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_chain_tx_out_cond_t *a_cond_out)
+dap_hash_fast_t dap_ledger_get_first_chain_tx_hash(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_cond_type, dap_chain_hash_fast_t *a_tx_hash)
 {
-    dap_hash_fast_t l_hash = { }, l_hash_tmp;
-    if (!a_ledger || !a_cond_out || !a_tx){
-        log_it(L_ERROR, "Argument is NULL in dap_ledger_get_first_chain_tx_hash()");
-        return l_hash;
-    }
-    dap_chain_datum_tx_t *l_prev_tx = a_tx;
-    byte_t *l_iter = a_tx->tx_items;
+    dap_return_val_if_fail(a_ledger && a_tx_hash, (dap_hash_fast_t) {});
+    dap_hash_fast_t l_hash = *a_tx_hash, l_hash_tmp;
+    dap_chain_datum_tx_t *l_prev_tx = dap_ledger_tx_find_by_hash(a_ledger, a_tx_hash);
+    byte_t *l_iter = l_prev_tx->tx_items;
     while (( l_iter = dap_chain_datum_tx_item_get(l_prev_tx, NULL, l_iter, TX_ITEM_TYPE_IN_COND, NULL) )) {
         l_hash_tmp =  ((dap_chain_tx_in_cond_t *)l_iter)->header.tx_prev_hash;
         if ( dap_hash_fast_is_blank(&l_hash_tmp) )
             return l_hash_tmp;
         if (( l_prev_tx = dap_ledger_tx_find_by_hash(a_ledger, &l_hash_tmp) ) &&
-                ( dap_chain_datum_tx_out_cond_get(l_prev_tx, a_cond_out->header.subtype, NULL) )) {
+                ( dap_chain_datum_tx_out_cond_get(l_prev_tx, a_cond_type, NULL) )) {
             l_hash = l_hash_tmp;
             l_iter = l_prev_tx->tx_items;
         }
@@ -2067,6 +2064,11 @@ int dap_ledger_tax_callback_set(dap_ledger_tax_callback_t a_callback)
     return 0;
 }
 
+void dap_ledger_set_cache_tx_check_callback(dap_ledger_t *a_ledger, dap_ledger_cache_tx_check_callback_t a_callback)
+{
+    PVT(a_ledger)->cache_tx_check_callback = a_callback;
+}
+
 uint256_t dap_ledger_calc_balance_full(dap_ledger_t *a_ledger, const dap_chain_addr_t *a_addr, const char *a_token_ticker)
 {
     uint256_t balance = uint256_0;
@@ -2120,3 +2122,102 @@ uint256_t dap_ledger_calc_balance_full(dap_ledger_t *a_ledger, const dap_chain_a
     pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock);
     return balance;
 }
+
+static int s_compare_balances(dap_ledger_hardfork_balances_t *a_list1, dap_ledger_hardfork_balances_t *a_list2)
+{
+    int ret = memcmp(&a_list1->addr, &a_list2->addr, sizeof(dap_chain_addr_t));
+    return ret ? ret : memcmp(a_list1->ticker, a_list2->ticker, DAP_CHAIN_TICKER_SIZE_MAX);
+}
+
+static int s_aggregate_out(dap_ledger_hardfork_balances_t **a_out_list, const char *a_ticker, dap_chain_addr_t *a_addr, uint256_t a_value)
+{
+    dap_ledger_hardfork_balances_t l_new_balance = { .addr = *a_addr, .value = a_value };
+    memcpy(l_new_balance.ticker, a_ticker, DAP_CHAIN_TICKER_SIZE_MAX);
+    dap_ledger_hardfork_balances_t *l_exist = NULL;
+    DL_SEARCH(*a_out_list, l_exist, &l_new_balance, s_compare_balances);
+    if (!l_exist) {
+        l_exist = DAP_DUP(&l_new_balance);
+        if (!l_exist) {
+            log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+            return -1;
+        }
+        DL_APPEND(*a_out_list, l_exist);
+    } else if (SUM_256_256(l_exist->value, a_value, &l_exist->value)) {
+        log_it(L_ERROR, "Integer overflow of hardfork aggregated data for addr %s and token %s with value %s",
+                                    dap_chain_addr_to_str_static(a_addr), a_ticker, dap_uint256_to_char(a_value, NULL));
+        return -2;
+    }
+    return 0;
+}
+
+static int s_aggregate_out_cond(dap_ledger_hardfork_condouts_t **a_ret_list, dap_chain_tx_out_cond_t *a_out_cond, dap_sign_t *a_sign)
+{
+    dap_ledger_hardfork_condouts_t *l_new_condout = DAP_NEW_Z(dap_ledger_hardfork_condouts_t);
+    if (!l_new_condout) {
+        log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+        return -1;
+    }
+    *l_new_condout = (dap_ledger_hardfork_condouts_t) { .cond = a_out_cond, .sign = a_sign };
+    DL_APPEND(*a_ret_list, l_new_condout);
+    return 0;
+}
+
+dap_ledger_hardfork_balances_t *dap_ledger_states_aggregate(dap_ledger_t *a_ledger, dap_ledger_hardfork_condouts_t **l_cond_outs_list)
+{
+    dap_ledger_hardfork_balances_t *ret = NULL;
+    dap_ledger_hardfork_condouts_t *l_cond_ret = NULL;
+    dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger);
+    pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock);
+    for (dap_ledger_tx_item_t *it = l_ledger_pvt->ledger_items; it; it = it->hh.next) {
+        if (it->cache_data.n_outs == it->cache_data.n_outs_used || it->cache_data.ts_spent)
+            continue;
+        uint8_t *l_tx_item = NULL;
+        size_t l_size;
+        int i, j = 0;
+        TX_ITEM_ITER_TX_TYPE(l_tx_item, TX_ITEM_TYPE_OUT_ALL, l_size, i, it->tx) {
+            if (!dap_hash_fast_is_blank(&it->cache_data.tx_hash_spent_fast[j++]))
+                continue;
+            uint8_t l_tx_item_type = *l_tx_item;
+            switch(l_tx_item_type) {
+            case TX_ITEM_TYPE_OUT: {
+                dap_chain_tx_out_t *l_out = (dap_chain_tx_out_t *)l_tx_item;
+                s_aggregate_out(&ret, it->cache_data.token_ticker, &l_out->addr, l_out->header.value);
+                break;
+            }
+            case TX_ITEM_TYPE_OUT_OLD: {
+                dap_chain_tx_out_old_t *l_out = (dap_chain_tx_out_old_t *)l_tx_item;
+                s_aggregate_out(&ret, it->cache_data.token_ticker, &l_out->addr, GET_256_FROM_64(l_out->header.value));
+                break;
+            }
+            case TX_ITEM_TYPE_OUT_EXT: {
+                dap_chain_tx_out_ext_t *l_out = (dap_chain_tx_out_ext_t *)l_tx_item;
+                s_aggregate_out(&ret, l_out->token, &l_out->addr, l_out->header.value);
+                break;
+            }
+            case TX_ITEM_TYPE_OUT_COND: {
+                dap_chain_tx_out_cond_t *l_out = (dap_chain_tx_out_cond_t *)l_tx_item;
+                if (l_out->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE)
+                    continue;
+                dap_hash_fast_t l_first_tx_hash = dap_ledger_get_first_chain_tx_hash(a_ledger, l_out->header.subtype, &it->tx_hash_fast);
+                dap_chain_datum_tx_t *l_tx = dap_hash_fast_compare(&l_first_tx_hash, &it->tx_hash_fast) ? it->tx
+                                                                                                        : dap_ledger_tx_find_by_hash(a_ledger, &l_first_tx_hash);
+                if (!l_tx) {
+                    log_it(L_ERROR, "Can't find header TX for conditional TX %s", dap_hash_fast_to_str_static(&it->tx_hash_fast));
+                    continue;
+                }
+                dap_sign_t *l_tx_sign = dap_chain_datum_tx_get_sign(l_tx, 0);
+                if (!l_tx_sign) {
+                    log_it(L_ERROR, "Can't find sign for conditional TX %s", dap_hash_fast_to_str_static(&l_first_tx_hash));
+                    continue;
+                }
+                s_aggregate_out_cond(&l_cond_ret, l_out, l_tx_sign);
+            }
+            default:
+                log_it(L_ERROR, "Unexpected item type %hhu", l_tx_item_type);
+                break;
+            }
+        }
+    }
+    pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock);
+    return ret;
+}
diff --git a/modules/ledger/include/dap_chain_ledger.h b/modules/ledger/include/dap_chain_ledger.h
index 1036222f9b..bd43965a64 100644
--- a/modules/ledger/include/dap_chain_ledger.h
+++ b/modules/ledger/include/dap_chain_ledger.h
@@ -46,6 +46,24 @@ typedef struct dap_ledger {
     void *_internal;
 } dap_ledger_t;
 
+typedef struct dap_ledger_hardfork_balances {
+    dap_chain_addr_t addr;
+    char ticker[DAP_CHAIN_TICKER_SIZE_MAX];
+    uint256_t value;
+    struct dap_ledger_hardfork_balances *prev, *next;
+} dap_ledger_hardfork_balances_t;
+
+typedef struct dap_ledger_hardfork_condouts {
+    dap_chain_tx_out_cond_t *cond;
+    dap_sign_t *sign;
+    struct dap_ledger_hardfork_condouts *prev, *next;
+} dap_ledger_hardfork_condouts_t;
+
+typedef struct dap_ledger_hardfork_anchors {
+    uint16_t decree_subtype;
+    dap_chain_datum_anchor_t *anchor;
+    struct dap_ledger_hardfork_anchors *prev, *next;
+} dap_ledger_hardfork_anchors_t;
 /**
  * @brief Error codes for accepting a transaction to the ledger.
  */
@@ -390,8 +408,8 @@ uint256_t dap_ledger_calc_balance_full(dap_ledger_t *a_ledger, const dap_chain_a
 dap_chain_datum_tx_t *dap_ledger_tx_find_by_hash(dap_ledger_t *a_ledger, const dap_chain_hash_fast_t *a_tx_hash);
 dap_chain_datum_tx_t *dap_ledger_tx_unspent_find_by_hash(dap_ledger_t *a_ledger, dap_chain_hash_fast_t *a_tx_hash);
 
-dap_hash_fast_t dap_ledger_get_final_chain_tx_hash(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_cond_type, dap_chain_hash_fast_t *a_tx_hash);
-dap_hash_fast_t dap_ledger_get_first_chain_tx_hash(dap_ledger_t *a_ledger, dap_chain_datum_tx_t * a_tx, dap_chain_tx_out_cond_t *a_cond_out);
+dap_hash_fast_t dap_ledger_get_final_chain_tx_hash(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_cond_type, dap_chain_hash_fast_t *a_tx_hash);
+dap_hash_fast_t dap_ledger_get_first_chain_tx_hash(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_cond_type, dap_chain_hash_fast_t *a_tx_hash);
 
  // Get the transaction in the cache by the addr in out item
 dap_chain_datum_tx_t* dap_ledger_tx_find_by_addr(dap_ledger_t *a_ledger, const char * a_token,
@@ -436,8 +454,6 @@ int dap_ledger_tax_callback_set(dap_ledger_tax_callback_t a_callback);
 // Getting a list of transactions from the ledger.
 dap_list_t * dap_ledger_get_txs(dap_ledger_t *a_ledger, size_t a_count, size_t a_page, bool a_reverse, bool a_unspent_only);
 
-//bool dap_ledger_fee_verificator(dap_ledger_t* a_ledger, dap_chain_tx_out_cond_t* a_cond, dap_chain_datum_tx_t* a_tx, bool a_owner);
-
 dap_ledger_datum_iter_t *dap_ledger_datum_iter_create(dap_chain_net_t *a_net);
 void dap_ledger_datum_iter_delete(dap_ledger_datum_iter_t *a_iter);
 dap_chain_datum_tx_t *dap_ledger_datum_iter_get_first(dap_ledger_datum_iter_t *a_iter);
@@ -461,7 +477,7 @@ uint16_t dap_ledger_decree_get_min_num_of_signers(dap_ledger_t *a_ledger);
 uint16_t dap_ledger_decree_get_num_of_owners(dap_ledger_t *a_ledger);
 const dap_list_t *dap_ledger_decree_get_owners_pkeys(dap_ledger_t *a_ledger);
 
-int dap_ledger_decree_apply(dap_hash_fast_t *a_decree_hash, dap_chain_datum_decree_t * a_decree, dap_chain_t *a_chain, bool a_anchored);
+int dap_ledger_decree_apply(dap_hash_fast_t *a_decree_hash, dap_chain_datum_decree_t *a_decree, dap_chain_t *a_chain, dap_hash_fast_t *a_anchor_hash);
 int dap_ledger_decree_verify(dap_chain_net_t *a_net, dap_chain_datum_decree_t *a_decree, size_t a_data_size, dap_chain_hash_fast_t *a_decree_hash);
 int dap_ledger_decree_load(dap_chain_datum_decree_t * a_decree, dap_chain_t *a_chain, dap_chain_hash_fast_t *a_decree_hash);
 dap_chain_datum_decree_t *dap_ledger_decree_get_by_hash(dap_chain_net_t *a_net, dap_hash_fast_t *a_hash, bool *is_applied);
@@ -470,3 +486,7 @@ int dap_ledger_decree_reset_applied(dap_chain_net_t *a_net, dap_chain_hash_fast_
 int dap_ledger_anchor_verify(dap_chain_net_t *a_net, dap_chain_datum_anchor_t * a_anchor, size_t a_data_size);
 int dap_ledger_anchor_load(dap_chain_datum_anchor_t * a_anchor, dap_chain_t *a_chain, dap_hash_fast_t *a_anchor_hash);
 int dap_ledger_anchor_unload(dap_chain_datum_anchor_t * a_anchor, dap_chain_t *a_chain, dap_hash_fast_t *a_anchor_hash);
+dap_chain_datum_anchor_t *dap_ledger_anchor_find(dap_ledger_t *a_ledger, dap_hash_fast_t *a_anchor_hash);
+
+dap_ledger_hardfork_balances_t *dap_ledger_states_aggregate(dap_ledger_t *a_ledger, dap_ledger_hardfork_condouts_t **l_cond_outs_list);
+dap_ledger_hardfork_anchors_t *dap_ledger_anchors_aggregate(dap_ledger_t *a_ledger);
diff --git a/modules/ledger/include/dap_chain_ledger_pvt.h b/modules/ledger/include/dap_chain_ledger_pvt.h
index 29bbf765a9..f6ad897d9a 100644
--- a/modules/ledger/include/dap_chain_ledger_pvt.h
+++ b/modules/ledger/include/dap_chain_ledger_pvt.h
@@ -142,9 +142,10 @@ typedef struct dap_ledger_hal_item {
 
 // private types definition
 typedef struct dap_ledger_decree_item {
-    dap_hash_fast_t key;
+    dap_hash_fast_t decree_hash;
     bool wait_for_apply, is_applied;
     dap_chain_datum_decree_t *decree;
+    dap_hash_fast_t anchor_hash;
     UT_hash_handle hh;
 } dap_ledger_decree_item_t;
 
diff --git a/modules/node-cli/dap_chain_node_cli_cmd.c b/modules/node-cli/dap_chain_node_cli_cmd.c
index 6b1113055b..b1b964bc1a 100644
--- a/modules/node-cli/dap_chain_node_cli_cmd.c
+++ b/modules/node-cli/dap_chain_node_cli_cmd.c
@@ -5390,7 +5390,7 @@ int com_tx_cond_remove(int a_argc, char ** a_argv, void **a_json_arr_reply)
             continue;
         }
         // Get owner tx
-        dap_hash_fast_t l_owner_tx_hash = dap_ledger_get_first_chain_tx_hash(l_ledger, l_cond_tx, l_tx_out_cond);
+        dap_hash_fast_t l_owner_tx_hash = dap_ledger_get_first_chain_tx_hash(l_ledger, DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY, l_hash);
         dap_chain_datum_tx_t *l_owner_tx = dap_hash_fast_is_blank(&l_owner_tx_hash)
             ? l_cond_tx:
             dap_ledger_tx_find_by_hash(l_ledger, &l_owner_tx_hash);
@@ -5638,9 +5638,6 @@ int com_tx_cond_unspent_find(int a_argc, char **a_argv, void **a_json_arr_reply)
     uint256_t l_total_value = {};
     for (dap_list_t *it = l_tx_list; it; it = it->next) {
         tx_check_args_t *l_data_tx = (tx_check_args_t*)it->data;
-        if (l_data_tx->tx_hash.raw[0] == 0x5A && l_data_tx->tx_hash.raw[1] == 0xc1){
-            log_it(L_INFO, "found!");
-        }
         dap_chain_datum_tx_t *l_tx = l_data_tx->tx;
         int l_prev_cond_idx = 0;
         dap_chain_tx_out_cond_t *l_out_cond = dap_chain_datum_tx_out_cond_get(l_tx, DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY , &l_prev_cond_idx);
@@ -5660,7 +5657,7 @@ int com_tx_cond_unspent_find(int a_argc, char **a_argv, void **a_json_arr_reply)
         }
 
         // Check sign
-        dap_hash_fast_t l_owner_tx_hash = dap_ledger_get_first_chain_tx_hash(l_ledger, l_data_tx->tx, l_out_cond);
+        dap_hash_fast_t l_owner_tx_hash = dap_ledger_get_first_chain_tx_hash(l_ledger, DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY, &l_data_tx->tx_hash);
         dap_chain_datum_tx_t *l_owner_tx = dap_hash_fast_is_blank(&l_owner_tx_hash)
             ? l_tx
             : dap_ledger_tx_find_by_hash(l_ledger, &l_owner_tx_hash);
diff --git a/modules/service/xchange/dap_chain_net_srv_xchange.c b/modules/service/xchange/dap_chain_net_srv_xchange.c
index 8c6ea5bd88..fe2224852f 100644
--- a/modules/service/xchange/dap_chain_net_srv_xchange.c
+++ b/modules/service/xchange/dap_chain_net_srv_xchange.c
@@ -1851,7 +1851,7 @@ static bool s_string_append_tx_cond_info( dap_string_t * a_reply_str,
                 log_it(L_ERROR, "Can't find datum tx");
                 return false;
             }
-            dap_hash_fast_t l_order_hash = dap_ledger_get_first_chain_tx_hash(a_net->pub.ledger, a_tx, l_out_cond);
+            dap_hash_fast_t l_order_hash = dap_ledger_get_first_chain_tx_hash(a_net->pub.ledger, DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE, &l_in_cond->header.tx_prev_hash);
             if ( dap_hash_fast_is_blank(&l_order_hash) )
                 l_order_hash = l_in_cond->header.tx_prev_hash;
 
-- 
GitLab