From 575b46eb6e374969402cc2e80f34812eeb1b8112 Mon Sep 17 00:00:00 2001
From: "roman.khlopkov" <roman.khlopkov@demlabs.net>
Date: Wed, 4 Dec 2024 13:30:33 +0300
Subject: [PATCH 1/9] [*] Hardfork preparing decree & aux

---
 modules/chain/dap_chain.c                     |  5 +++
 modules/chain/include/dap_chain_srv.h         | 12 +++--
 modules/datum/dap_chain_datum_decree.c        | 16 +++++++
 .../datum/include/dap_chain_datum_decree.h    | 10 ++++-
 modules/ledger/dap_chain_ledger_decree.c      | 44 ++++++++++++-------
 modules/node-cli/dap_chain_node_cli_cmd.c     |  1 +
 6 files changed, 66 insertions(+), 22 deletions(-)

diff --git a/modules/chain/dap_chain.c b/modules/chain/dap_chain.c
index 79c7555221..986748ca56 100644
--- a/modules/chain/dap_chain.c
+++ b/modules/chain/dap_chain.c
@@ -149,6 +149,11 @@ int dap_chain_purge(dap_chain_t *a_chain)
     return ret + dap_chain_cs_purge(a_chain);
 }
 
+int dap_chain_hardfork_preare(dap_chain_t *a_chain, uint64_t a_atom_num)
+{
+
+}
+
 /**
  * @brief
  * delete dap chain object
diff --git a/modules/chain/include/dap_chain_srv.h b/modules/chain/include/dap_chain_srv.h
index 1ca29339d9..558bcc58e9 100644
--- a/modules/chain/include/dap_chain_srv.h
+++ b/modules/chain/include/dap_chain_srv.h
@@ -41,8 +41,10 @@ typedef void (*dap_chain_srv_callback_decree_t)(dap_chain_net_id_t a_net_id, int
 typedef int (*dap_chain_srv_callback_purge_t)(dap_chain_net_id_t a_net_id);
 // Get fee service callback
 typedef json_object * (*dap_chain_srv_callback_get_fee)(dap_chain_net_id_t a_net_id);
-// Hardfork service callback
-typedef int (*dap_chain_srv_callback_hardfork_t)(dap_chain_net_id_t a_net_id);
+// Hardfork prepare service callback
+typedef byte_t * (*dap_chain_srv_callback_hardfork_prepare_t)(dap_chain_net_id_t a_net_id, uint64_t *a_state_size, uint32_t *a_state_count);
+// Hardfork data load service callback
+typedef int (*dap_chain_srv_callback_hardfork_data_t)(dap_chain_net_id_t a_net_id, byte_t *a_state, uint64_t a_state_size, uint32_t a_state_count);
 // Delete service callback
 typedef void (*dap_chain_srv_callback_delete_t)(void *);
 
@@ -55,8 +57,10 @@ typedef struct dap_chain_static_srv_callbacks {
     dap_chain_srv_callback_purge_t purge;
     // Get service fee
     dap_chain_srv_callback_get_fee get_fee_descr;
-    // Hardfork
-    dap_chain_srv_callback_hardfork_t hardfork;
+    // Hardfork prepare
+    dap_chain_srv_callback_hardfork_prepare_t hardfork_prepare;
+    // Hardfork data load
+    dap_chain_srv_callback_hardfork_data_t hardfork_load;
     // Delete for internal service clean exit
     dap_chain_srv_callback_delete_t delete;
     // And no more =)
diff --git a/modules/datum/dap_chain_datum_decree.c b/modules/datum/dap_chain_datum_decree.c
index 7a33f3351b..0221390660 100644
--- a/modules/datum/dap_chain_datum_decree.c
+++ b/modules/datum/dap_chain_datum_decree.c
@@ -149,6 +149,13 @@ int dap_chain_datum_decree_get_ban_addr(dap_chain_datum_decree_t *a_decree, cons
     return l_tsd ? ( *a_addr = dap_tsd_get_string_const(l_tsd), !dap_strcmp(*a_addr, DAP_TSD_CORRUPTED_STRING) ) : 1;
 }
 
+int dap_chain_datum_decree_get_atom_num(dap_chain_datum_decree_t *a_decree, uint64_t *a_atom_num)
+{
+    dap_return_val_if_fail(a_decree && a_atom_num, -1);
+    dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_BLOCK_NUM);
+    return l_tsd && l_tsd->size == sizeof(uint64_t) ? ( _dap_tsd_get_scalar(l_tsd, a_atom_num), 0 ) : 1;
+}
+
 void dap_chain_datum_decree_dump_json(json_object *a_json_out, dap_chain_datum_decree_t *a_decree, size_t a_decree_size, const char *a_hash_out_type)
 {
     char *l_type_str = "";
@@ -309,6 +316,15 @@ void dap_chain_datum_decree_dump_json(json_object *a_json_out, dap_chain_datum_d
             dap_sign_type_t l_sign_type = { .type = l_type };
             json_object_object_add(a_json_out, "Signature type", json_object_new_string(dap_sign_type_to_str(l_sign_type)));
             break;
+        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_BLOCK_NUM:
+            if (l_tsd->size != sizeof(uint64_t)) {
+                json_object_object_add(a_json_out, "Signature type", json_object_new_string("WRONG SIZE"));
+                break;
+            }
+            uint64_t l_num = 0;
+            _dap_tsd_get_scalar(l_tsd, &l_type);
+            json_object_object_add(a_json_out, "Signature type", json_object_new_uint64(l_num));
+            break;
         default:
             json_object_object_add(a_json_out, "UNKNOWN_TYPE_TSD_SECTION", json_object_new_string(""));
             break;
diff --git a/modules/datum/include/dap_chain_datum_decree.h b/modules/datum/include/dap_chain_datum_decree.h
index 075939ce71..f4b569c327 100644
--- a/modules/datum/include/dap_chain_datum_decree.h
+++ b/modules/datum/include/dap_chain_datum_decree.h
@@ -125,12 +125,15 @@ DAP_STATIC_INLINE const char *dap_chain_datum_decree_subtype_to_str(uint16_t a_d
         return "DECREE_COMMON_SUBTYPE_EMERGENCY_VALIDATORS";
     case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_CHECK_SIGNS_STRUCTURE:
         return "DECREE_COMMON_SUBTYPE_CHECK_SIGNS_STRUCTURE";
+    case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_HARDFORK:
+        return "DECREE_COMMON_SUBTYPE_HARDFORK";
     default:
         return "DECREE_SUBTYPE_UNKNOWN";
     }
 }
 
-DAP_STATIC_INLINE uint16_t dap_chain_datum_decree_type_from_str(const char *a_decree_type) {
+DAP_STATIC_INLINE uint16_t dap_chain_datum_decree_type_from_str(const char *a_decree_type)
+{
     if (!dap_strcmp(a_decree_type, "fee")){
         return DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_FEE;
     } else if (!dap_strcmp(a_decree_type, "owners")) {
@@ -157,6 +160,8 @@ DAP_STATIC_INLINE uint16_t dap_chain_datum_decree_type_from_str(const char *a_de
         return DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_EMERGENCY_VALIDATORS;
     } else if (!dap_strcmp(a_decree_type, "check_signs_structure")) {
         return DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_CHECK_SIGNS_STRUCTURE;
+    } else if (!dap_strcmp(a_decree_type, "hardfork_prepare")) {
+        return DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_HARDFORK;
     } else {
         return 0;
     }
@@ -196,6 +201,8 @@ DAP_STATIC_INLINE const char *dap_chain_datum_decree_tsd_type_to_str(uint16_t a_
          return "DAP_CHAIN_DATUM_DECREE_TSD_TYPE_ACTION";
     case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_SIGNATURE_TYPE:
          return "DAP_CHAIN_DATUM_DECREE_TSD_TYPE_SIGNATURE_TYPE";
+    case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_BLOCK_NUM:
+         return "DAP_CHAIN_DATUM_DECREE_TSD_TYPE_BLOCK_NUM";
     default:
         return "DECREE_TSD_TYPE_UNKNOWN";
     }
@@ -303,6 +310,7 @@ int dap_chain_datum_decree_get_stake_min_signers_count(dap_chain_datum_decree_t
 int dap_chain_datum_decree_get_action(dap_chain_datum_decree_t *a_decree, uint8_t *a_action);
 int dap_chain_datum_decree_get_signature_type(dap_chain_datum_decree_t *a_decree, uint32_t *a_signature_type);
 int dap_chain_datum_decree_get_ban_addr(dap_chain_datum_decree_t *a_decree, const char **a_addr);
+int dap_chain_datum_decree_get_atom_num(dap_chain_datum_decree_t *a_decree, uint64_t *a_atom_num);
 
 /**
  * @breif dap_chain_datum_decree_dump Dump information about decree
diff --git a/modules/ledger/dap_chain_ledger_decree.c b/modules/ledger/dap_chain_ledger_decree.c
index 3ecfd2f963..90d7d92daf 100644
--- a/modules/ledger/dap_chain_ledger_decree.c
+++ b/modules/ledger/dap_chain_ledger_decree.c
@@ -328,15 +328,16 @@ static bool s_verify_pkey (dap_sign_t *a_sign, dap_chain_net_t *a_net)
 
 static int s_common_decree_handler(dap_chain_datum_decree_t *a_decree, dap_chain_net_t *a_net, bool a_apply, bool a_anchored)
 {
-    uint256_t l_value;
-    uint32_t l_sign_type;
-    uint16_t l_owners_num;
-    uint8_t l_action;
-    dap_chain_addr_t l_addr = {};
-    dap_hash_fast_t l_hash = {};
-    dap_chain_node_addr_t l_node_addr = {};
-    dap_list_t *l_owners_list = NULL;
-    const char *l_ban_addr;
+uint256_t l_value;
+uint64_t l_block_num;
+uint32_t l_sign_type;
+uint16_t l_owners_num;
+uint8_t l_action;
+dap_chain_addr_t l_addr = {};
+dap_hash_fast_t l_hash = {};
+dap_chain_node_addr_t l_node_addr = {};
+dap_list_t *l_owners_list = NULL;
+const char *l_ban_addr;
 
     dap_return_val_if_fail(a_decree && a_net, -112);
 
@@ -512,8 +513,8 @@ static int s_common_decree_handler(dap_chain_datum_decree_t *a_decree, dap_chain
             if (!a_apply)
                 break;
             uint64_t l_cur_block_num = l_chain->callback_count_atom(l_chain);
-            dap_chain_net_add_reward(a_net, l_value, l_cur_block_num);
-        } break;
+            return dap_chain_net_add_reward(a_net, l_value, l_cur_block_num);
+        }
         case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_MAX_WEIGHT: {
             if (dap_chain_datum_decree_get_value(a_decree, &l_value)) {
                 log_it(L_WARNING,"Can't get value from decree.");
@@ -535,10 +536,11 @@ static int s_common_decree_handler(dap_chain_datum_decree_t *a_decree, dap_chain
             if (!a_apply)
                 break;
             dap_chain_net_srv_stake_set_percent_max(a_net->pub.id, l_value);
-        } break;
+            return 0;
+        }
         case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_CHECK_SIGNS_STRUCTURE: {
             if (dap_chain_datum_decree_get_action(a_decree, &l_action)) {
-                log_it(L_WARNING,"Can't get action from decree.");
+                log_it(L_WARNING, "Can't get action from decree.");
                 return -103;
             }
             dap_chain_t *l_chain = dap_chain_find_by_id(a_net->pub.id, a_decree->header.common_decree_params.chain_id);
@@ -552,8 +554,8 @@ static int s_common_decree_handler(dap_chain_datum_decree_t *a_decree, dap_chain
             }
             if (!a_apply)
                 break;
-            dap_chain_esbocs_set_signs_struct_check(l_chain, l_action);
-        } break;
+            return dap_chain_esbocs_set_signs_struct_check(l_chain, l_action);
+        }
         case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_EMERGENCY_VALIDATORS: {
             if (dap_chain_datum_decree_get_action(a_decree, &l_action)) {
                 log_it(L_WARNING,"Can't get action from decree.");
@@ -578,8 +580,16 @@ static int s_common_decree_handler(dap_chain_datum_decree_t *a_decree, dap_chain
             }
             if (!a_apply)
                 break;
-            dap_chain_esbocs_set_emergency_validator(l_chain, l_action, l_sign_type, &l_hash);
-        } break;
+            return dap_chain_esbocs_set_emergency_validator(l_chain, l_action, l_sign_type, &l_hash);
+        }
+        case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_HARDFORK:
+            if (dap_chain_datum_decree_get_atom_num(a_decree, &l_block_num)) {
+                log_it(L_WARNING, "Can't get atom number from hardfork prepare decree");
+                return -103;
+            }
+            if (!a_apply)
+                break;
+            return dap_chain_hardfork_prepare(l_chain, l_block_num);
         default:
             return -1;
     }
diff --git a/modules/node-cli/dap_chain_node_cli_cmd.c b/modules/node-cli/dap_chain_node_cli_cmd.c
index b02f90d157..8195358abd 100644
--- a/modules/node-cli/dap_chain_node_cli_cmd.c
+++ b/modules/node-cli/dap_chain_node_cli_cmd.c
@@ -4345,6 +4345,7 @@ int cmd_decree(int a_argc, char **a_argv, void **a_str_reply)
             *(uint256_t*)(l_tsd->data) = dap_uint256_scan_uninteger(l_param_value_str);
             l_tsd_list = dap_list_append(l_tsd_list, l_tsd);
         } else if (dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-harfork_from", &l_param_value_str)) {
+            l_subtype = DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_HARDFORK;
             l_total_tsd_size += sizeof(dap_tsd_t) + sizeof(uint64_t);
             l_tsd = DAP_NEW_Z_SIZE(dap_tsd_t, l_total_tsd_size);
             if (!l_tsd) {
-- 
GitLab


From a696a3f02db16f8382c0e8b09f62d72e13d20e18 Mon Sep 17 00:00:00 2001
From: "roman.khlopkov" <roman.khlopkov@demlabs.net>
Date: Tue, 10 Dec 2024 18:51:15 +0300
Subject: [PATCH 2/9] [*] Harfork kernel development

---
 modules/chain/dap_chain_srv.c                 |  15 +-
 modules/chain/include/dap_chain.h             |   2 +-
 modules/chain/include/dap_chain_srv.h         |  10 +-
 .../consensus/esbocs/dap_chain_cs_esbocs.c    |  22 +-
 .../esbocs/include/dap_chain_cs_esbocs.h      |   7 +-
 modules/ledger/dap_chain_ledger_decree.c      |  11 +-
 modules/ledger/dap_chain_ledger_tx.c          |  10 +-
 modules/ledger/include/dap_chain_ledger.h     |   2 +-
 modules/mempool/dap_chain_mempool.c           | 247 +-----------------
 modules/mempool/include/dap_chain_mempool.h   |  17 +-
 modules/net/dap_chain_net.c                   |   2 +-
 modules/net/dap_chain_node.c                  |  54 +++-
 modules/net/include/dap_chain_net.h           |  13 -
 modules/node-cli/dap_chain_node_cli_cmd.c     |  24 +-
 .../node-cli/dap_chain_node_cli_cmd_token.c   |   8 +-
 modules/node-cli/dap_chain_node_cli_cmd_tx.c  |   6 +-
 .../service/datum/dap_chain_net_srv_datum.c   |   4 +-
 .../dap_chain_net_srv_emit_delegate.c         |   2 +-
 modules/type/blocks/dap_chain_cs_blocks.c     |   2 +-
 .../type/blocks/include/dap_chain_cs_blocks.h |   2 +
 20 files changed, 156 insertions(+), 304 deletions(-)

diff --git a/modules/chain/dap_chain_srv.c b/modules/chain/dap_chain_srv.c
index f230fc9461..5955af7924 100644
--- a/modules/chain/dap_chain_srv.c
+++ b/modules/chain/dap_chain_srv.c
@@ -197,14 +197,23 @@ int dap_chain_srv_purge_all(dap_chain_net_id_t a_net_id)
  * @brief dap_chain_srv_hardfork_all
  * @param a_net_id
  */
-void dap_chain_srv_hardfork_all(dap_chain_net_id_t a_net_id)
+dap_chain_srv_hardfork_state_t *dap_chain_srv_hardfork_all(dap_chain_net_id_t a_net_id)
 {
+    dap_chain_srv_hardfork_state_t *ret = NULL;
     pthread_mutex_lock(&s_srv_list_mutex);
     for (struct service_list *it = s_srv_list; it; it = it->hh.next) {
-        if (s_net_service_find(it, a_net_id) && it->callbacks.hardfork)
-            it->callbacks.hardfork(a_net_id);
+        if (s_net_service_find(it, a_net_id) && it->callbacks.hardfork_prepare) {
+            dap_chain_srv_hardfork_state_t *l_state = DAP_NEW_Z(dap_chain_srv_hardfork_state_t), *cur, *tmp;
+            if (!l_state)
+                DL_FOREACH_SAFE(ret, cur, tmp)
+                    DAP_DELETE(cur);
+            l_state->uid = it->uuid;
+            l_state->data = it->callbacks.hardfork_prepare(a_net_id, &l_state->size, &l_state->count);
+            DL_APPEND(ret, l_state);
+        }
     }
     pthread_mutex_unlock(&s_srv_list_mutex);
+    return ret;
 }
 
 /**
diff --git a/modules/chain/include/dap_chain.h b/modules/chain/include/dap_chain.h
index 9ccf259c84..22b4d0d643 100644
--- a/modules/chain/include/dap_chain.h
+++ b/modules/chain/include/dap_chain.h
@@ -33,7 +33,7 @@
 #ifdef DAP_TPS_TEST
 #define DAP_CHAIN_ATOM_MAX_SIZE (100 * 1024 * 1024)
 #else
-#define DAP_CHAIN_ATOM_MAX_SIZE (256 * 1024) // 256 KB
+#define DAP_CHAIN_ATOM_MAX_SIZE (8 * 1024 * 1024) // 8 MB
 #endif
 
 typedef struct dap_chain dap_chain_t;
diff --git a/modules/chain/include/dap_chain_srv.h b/modules/chain/include/dap_chain_srv.h
index 558bcc58e9..91be1dda00 100644
--- a/modules/chain/include/dap_chain_srv.h
+++ b/modules/chain/include/dap_chain_srv.h
@@ -66,6 +66,14 @@ typedef struct dap_chain_static_srv_callbacks {
     // And no more =)
 } dap_chain_static_srv_callbacks_t;
 
+typedef struct dap_chain_srv_hardfork_state {
+    dap_chain_srv_uid_t uid;
+    byte_t *data;
+    uint64_t size;
+    uint32_t count;
+    struct dap_chain_srv_hardfork_state *prev, *next;
+} dap_chain_srv_hardfork_state_t;
+
 // Fees section
 typedef enum dap_chain_srv_fee_tsd_type {
     TSD_FEE = 0x0001,
@@ -111,5 +119,5 @@ dap_list_t *dap_chain_srv_list(dap_chain_net_id_t a_net_id);
 
 int dap_chain_srv_purge(dap_chain_net_id_t a_net_id, dap_chain_srv_uid_t a_srv_uid);
 int dap_chain_srv_purge_all(dap_chain_net_id_t a_net_id);
-void dap_chain_srv_hardfork_all(dap_chain_net_id_t a_net_id);
+dap_chain_srv_hardfork_state_t *dap_chain_srv_hardfork_all(dap_chain_net_id_t a_net_id);
 json_object *dap_chain_srv_get_fees(dap_chain_net_id_t a_net_id);
diff --git a/modules/consensus/esbocs/dap_chain_cs_esbocs.c b/modules/consensus/esbocs/dap_chain_cs_esbocs.c
index 6570a5af1d..fe4100fc0c 100644
--- a/modules/consensus/esbocs/dap_chain_cs_esbocs.c
+++ b/modules/consensus/esbocs/dap_chain_cs_esbocs.c
@@ -41,6 +41,8 @@ along with any CellFrame SDK based project.  If not, see <http://www.gnu.org/lic
 
 #define LOG_TAG "dap_chain_cs_esbocs"
 
+static const dap_chain_cell_id_t c_cell_id_hardfork = { .uint64 = INT64_MIN }; // 0x800...
+
 enum s_esbocs_session_state {
     DAP_CHAIN_ESBOCS_SESSION_STATE_WAIT_START,
     DAP_CHAIN_ESBOCS_SESSION_STATE_WAIT_PROC,
@@ -735,6 +737,15 @@ int dap_chain_esbocs_set_min_validators_count(dap_chain_t *a_chain, uint16_t a_n
     return 0;
 }
 
+int dap_chain_esbocs_set_hardfork_prepare(dap_chain_t *a_chain, uint64_t a_block_num)
+{
+    uint64_t l_last_num = a_chain->callback_count_atom(a_chain);
+    dap_chain_cs_blocks_t *l_blocks = DAP_CHAIN_CS_BLOCKS(a_chain);
+    dap_chain_esbocs_t *l_esbocs = DAP_CHAIN_ESBOCS(l_blocks);
+    l_esbocs->hardfork_from = dap_max(l_last_num, a_block_num);
+    return a_block_num && a_block_num < l_last_num ? 1 : 0;
+}
+
 static int s_callback_purge(dap_chain_t *a_chain)
 {
     dap_return_val_if_fail(a_chain && !strcmp(dap_chain_get_cs_type(a_chain), DAP_CHAIN_ESBOCS_CS_TYPE_STR), -1);
@@ -1120,7 +1131,7 @@ static bool s_session_round_new(void *a_arg)
     a_session->ts_stage_entry = 0;
 
     dap_hash_fast_t l_last_block_hash;
-    dap_chain_get_atom_last_hash(a_session->chain, c_dap_chain_cell_id_null, &l_last_block_hash);
+    dap_chain_get_atom_last_hash(a_session->chain, a_session->is_hardfork ? c_dap_chain_cell_id_null : c_cell_id_hardfork, &l_last_block_hash);
     if (!dap_hash_fast_compare(&l_last_block_hash, &a_session->cur_round.last_block_hash) ||
             (!dap_hash_fast_is_blank(&l_last_block_hash) &&
                 dap_hash_fast_is_blank(&a_session->cur_round.last_block_hash))) {
@@ -1191,6 +1202,10 @@ static bool s_session_round_new(void *a_arg)
     a_session->new_round_enqueued = false;
     a_session->sync_failed = false;
     a_session->listen_ensure = 0;
+    uint64_t l_cur_atom_count = a_session->chain->callback_count_atom(a_session->chain);
+    a_session->is_hardfork = a_session->esbocs->hardfork_from && l_cur_atom_count >= a_session->esbocs->hardfork_from;
+    if (l_cur_atom_count && l_cur_atom_count == a_session->esbocs->hardfork_from)
+        dap_chain_node_hardfork_prepare(a_session->chain);
     return false;
 }
 
@@ -1647,7 +1662,10 @@ static void s_session_candidate_submit(dap_chain_esbocs_session_t *a_session)
     dap_chain_cs_blocks_t *l_blocks = DAP_CHAIN_CS_BLOCKS(l_chain);
     size_t l_candidate_size = 0;
     dap_hash_fast_t l_candidate_hash = {0};
-    dap_chain_node_mempool_process_all(a_session->chain, false);
+    if (a_session->is_hardfork)
+        dap_chain_node_hardfork_process(a_session->chain);
+    else
+        dap_chain_node_mempool_process_all(a_session->chain, false);
     dap_chain_block_t *l_candidate = l_blocks->callback_new_block_move(l_blocks, &l_candidate_size);
     if (l_candidate && l_candidate_size) {
         if (PVT(a_session->esbocs)->emergency_mode)
diff --git a/modules/consensus/esbocs/include/dap_chain_cs_esbocs.h b/modules/consensus/esbocs/include/dap_chain_cs_esbocs.h
index 643bcc2c1f..93ae2be966 100644
--- a/modules/consensus/esbocs/include/dap_chain_cs_esbocs.h
+++ b/modules/consensus/esbocs/include/dap_chain_cs_esbocs.h
@@ -23,7 +23,6 @@ along with any CellFrame SDK based project.  If not, see <http://www.gnu.org/lic
 */
 #pragma once
 
-#include "dap_timerfd.h"
 #include "dap_chain.h"
 #include "dap_chain_block.h"
 #include "dap_chain_cs_blocks.h"
@@ -124,6 +123,7 @@ typedef struct dap_chain_esbocs {
     dap_chain_t *chain;
     dap_chain_cs_blocks_t *blocks;
     dap_chain_esbocs_session_t *session;
+    uint64_t hardfork_from;
     dap_time_t last_directive_vote_timestamp, last_directive_accept_timestamp,
                last_submitted_candidate_timestamp, last_accepted_block_timestamp;
     void *_pvt;
@@ -197,7 +197,8 @@ typedef struct dap_chain_esbocs_session {
     unsigned int listen_ensure;
     dap_chain_node_addr_t my_addr;
     uint8_t state, old_state;
-    bool cs_timer, round_fast_forward, sync_failed, new_round_enqueued, is_actual_hash;
+    bool cs_timer, round_fast_forward, sync_failed,
+         new_round_enqueued, is_actual_hash, is_hardfork;
     dap_global_db_driver_hash_t db_hash;
     dap_chain_addr_t my_signing_addr;
 } dap_chain_esbocs_session_t;
@@ -266,5 +267,5 @@ int dap_chain_esbocs_set_min_validators_count(dap_chain_t *a_chain, uint16_t a_n
 uint16_t dap_chain_esbocs_get_min_validators_count(dap_chain_net_id_t a_net_id);
 int dap_chain_esbocs_set_emergency_validator(dap_chain_t *a_chain, bool a_add, uint32_t a_sign_type, dap_hash_fast_t *a_validator_hash);
 int dap_chain_esbocs_set_signs_struct_check(dap_chain_t *a_chain, bool a_enable);
-
+int dap_chain_esbocs_set_hardfork_prepare(dap_chain_t *a_chain, uint64_t a_block_num);
 void dap_chain_esbocs_change_debug_mode(dap_chain_t *a_chain, bool a_enable);
diff --git a/modules/ledger/dap_chain_ledger_decree.c b/modules/ledger/dap_chain_ledger_decree.c
index 90d7d92daf..63c25704dc 100644
--- a/modules/ledger/dap_chain_ledger_decree.c
+++ b/modules/ledger/dap_chain_ledger_decree.c
@@ -587,9 +587,18 @@ const char *l_ban_addr;
                 log_it(L_WARNING, "Can't get atom number from hardfork prepare decree");
                 return -103;
             }
+            dap_chain_t *l_chain = dap_chain_find_by_id(a_net->pub.id, a_decree->header.common_decree_params.chain_id);
+            if (!l_chain) {
+                log_it(L_WARNING, "Specified chain not found");
+                return -106;
+            }
+            if (dap_strcmp(dap_chain_get_cs_type(l_chain), "esbocs")) {
+                log_it(L_WARNING, "Can't apply this decree to specified chain");
+                return -115;
+            }
             if (!a_apply)
                 break;
-            return dap_chain_hardfork_prepare(l_chain, l_block_num);
+            return dap_chain_esbocs_set_hardfork_prepare(l_chain, l_block_num);
         default:
             return -1;
     }
diff --git a/modules/ledger/dap_chain_ledger_tx.c b/modules/ledger/dap_chain_ledger_tx.c
index 89ffbf2cae..7885a2390b 100644
--- a/modules/ledger/dap_chain_ledger_tx.c
+++ b/modules/ledger/dap_chain_ledger_tx.c
@@ -2442,7 +2442,7 @@ static int s_aggregate_out_cond(dap_ledger_hardfork_condouts_t **a_ret_list, dap
     return 0;
 }
 
-dap_ledger_hardfork_balances_t *dap_ledger_states_aggregate(dap_ledger_t *a_ledger, dap_time_t a_hardfork_decree_creatiom_time, dap_ledger_hardfork_condouts_t **l_cond_outs_list)
+dap_ledger_hardfork_balances_t *dap_ledger_states_aggregate(dap_ledger_t *a_ledger, dap_time_t a_hardfork_decree_creation_time, 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;
@@ -2464,17 +2464,17 @@ dap_ledger_hardfork_balances_t *dap_ledger_states_aggregate(dap_ledger_t *a_ledg
             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, a_ledger, it->cache_data.token_ticker, &l_out->addr, l_out->header.value, a_hardfork_decree_creatiom_time, l_trackers);
+                s_aggregate_out(&ret, a_ledger, it->cache_data.token_ticker, &l_out->addr, l_out->header.value, a_hardfork_decree_creation_time, l_trackers);
                 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, a_ledger, it->cache_data.token_ticker, &l_out->addr, GET_256_FROM_64(l_out->header.value), a_hardfork_decree_creatiom_time, l_trackers);
+                s_aggregate_out(&ret, a_ledger, it->cache_data.token_ticker, &l_out->addr, GET_256_FROM_64(l_out->header.value), a_hardfork_decree_creation_time, l_trackers);
                 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, a_ledger, l_out->token, &l_out->addr, l_out->header.value, a_hardfork_decree_creatiom_time, l_trackers);
+                s_aggregate_out(&ret, a_ledger, l_out->token, &l_out->addr, l_out->header.value, a_hardfork_decree_creation_time, l_trackers);
                 break;
             }
             case TX_ITEM_TYPE_OUT_COND: {
@@ -2493,7 +2493,7 @@ dap_ledger_hardfork_balances_t *dap_ledger_states_aggregate(dap_ledger_t *a_ledg
                     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, a_ledger, l_out, l_tx_sign, &it->tx_hash_fast, a_hardfork_decree_creatiom_time, l_trackers);
+                s_aggregate_out_cond(&l_cond_ret, a_ledger, l_out, l_tx_sign, &it->tx_hash_fast, a_hardfork_decree_creation_time, l_trackers);
             }
             default:
                 log_it(L_ERROR, "Unexpected item type %hhu", l_tx_item_type);
diff --git a/modules/ledger/include/dap_chain_ledger.h b/modules/ledger/include/dap_chain_ledger.h
index 66be425d77..2a47f928c1 100644
--- a/modules/ledger/include/dap_chain_ledger.h
+++ b/modules/ledger/include/dap_chain_ledger.h
@@ -500,7 +500,7 @@ int dap_ledger_anchor_load(dap_chain_datum_anchor_t * a_anchor, dap_chain_t *a_c
 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_time_t a_hardfork_decree_creatiom_time, dap_ledger_hardfork_condouts_t **l_cond_outs_list);
+dap_ledger_hardfork_balances_t *dap_ledger_states_aggregate(dap_ledger_t *a_ledger, dap_time_t a_hardfork_decree_creation_time, dap_ledger_hardfork_condouts_t **l_cond_outs_list);
 dap_ledger_hardfork_anchors_t *dap_ledger_anchors_aggregate(dap_ledger_t *a_ledger);
 
 uint256_t dap_ledger_coin_get_uncoloured_value(dap_ledger_t *a_ledger, dap_hash_fast_t *a_voting_hash, dap_hash_fast_t *a_tx_prev_hash, int a_out_idx);
diff --git a/modules/mempool/dap_chain_mempool.c b/modules/mempool/dap_chain_mempool.c
index 393cf39417..8555094d68 100644
--- a/modules/mempool/dap_chain_mempool.c
+++ b/modules/mempool/dap_chain_mempool.c
@@ -124,7 +124,7 @@ char *dap_chain_mempool_datum_add(const dap_chain_datum_t *a_datum, dap_chain_t
         DAP_DATUM_TYPE_STR(a_datum->header.type_id, l_type_str);
     }
 
-    char *l_gdb_group = dap_chain_net_get_gdb_group_mempool_new(a_chain);
+    char *l_gdb_group = dap_chain_mempool_group_new(a_chain);
     int l_res = dap_global_db_set_sync(l_gdb_group, l_key_str, a_datum, dap_chain_datum_size(a_datum), false);//, NULL, NULL);
     if (l_res == DAP_GLOBAL_DB_RC_SUCCESS)
         log_it(L_NOTICE, "Datum %s with hash %s was placed in mempool group %s", l_type_str, l_key_str_out, l_gdb_group);
@@ -748,7 +748,7 @@ int dap_chain_mempool_tx_create_massive( dap_chain_t * a_chain, dap_enc_key_t *a
 
     }
     dap_list_free_full(l_list_used_out, NULL);
-    char *l_gdb_group = dap_chain_net_get_gdb_group_mempool_new(a_chain);
+    char *l_gdb_group = dap_chain_mempool_group_new(a_chain);
     dap_global_db_set_multiple_zc(l_gdb_group, l_objs, a_tx_num, s_tx_create_massive_gdb_save_callback, NULL);
     DAP_DEL_Z(l_gdb_group);
     return 0;
@@ -1162,7 +1162,7 @@ dap_chain_datum_token_emission_t *dap_chain_mempool_emission_get(dap_chain_t *a_
 dap_chain_datum_t *dap_chain_mempool_datum_get(dap_chain_t *a_chain, const char *a_datum_hash_str)
 {
     size_t l_datum_size;
-    char *l_gdb_group = dap_chain_net_get_gdb_group_mempool_new(a_chain);
+    char *l_gdb_group = dap_chain_mempool_group_new(a_chain);
     dap_chain_datum_t *l_datum = (dap_chain_datum_t *)dap_global_db_get_sync(l_gdb_group,
                                                     a_datum_hash_str, &l_datum_size, NULL, NULL );
     if (!l_datum) {
@@ -1223,245 +1223,6 @@ dap_chain_datum_token_emission_t *dap_chain_mempool_datum_emission_extract(dap_c
     return DAP_DUP_SIZE(l_emission, l_datum->header.data_size);
 }
 
-uint8_t* dap_datum_mempool_serialize(dap_datum_mempool_t *datum_mempool, size_t *size)
-{
-    size_t a_request_size = 2 * sizeof(uint16_t), shift_size = 0;
-    for(int i = 0; i < datum_mempool->datum_count; i++) {
-        a_request_size += dap_chain_datum_size(datum_mempool->data[i]) + sizeof(uint16_t);
-    }
-    uint8_t *l_ret = DAP_NEW_Z_SIZE_RET_VAL_IF_FAIL(uint8_t, a_request_size, NULL);
-    uint8_t *l_pos = dap_mempcpy(l_ret, &datum_mempool->version, sizeof(uint16_t));
-    l_pos = dap_mempcpy(l_pos, &datum_mempool->datum_count, sizeof(uint16_t));
-    for(int i = 0; i < datum_mempool->datum_count; i++) {
-        size_t size_one = dap_chain_datum_size(datum_mempool->data[i]);
-        l_pos = dap_mempcpy( dap_mempcpy(l_pos, &size_one, sizeof(uint16_t)), datum_mempool->data[i], size_one );
-    }
-    assert((size_t)(l_pos - l_ret) == a_request_size);
-    if(size)
-        *size = a_request_size;
-    return l_ret;
-}
-
-dap_datum_mempool_t * dap_datum_mempool_deserialize(uint8_t *a_datum_mempool_ser, size_t a_datum_mempool_ser_size)
-{
-    size_t shift_size = 0;
-    //uint8_t *a_datum_mempool_ser = DAP_NEW_Z_SIZE(uint8_t, datum_mempool_size / 2 + 1);
-    //datum_mempool_size = hex2bin(a_datum_mempool_ser, datum_mempool_str_in, datum_mempool_size) / 2;
-    dap_datum_mempool_t *datum_mempool = DAP_NEW_Z(dap_datum_mempool_t);
-    if (!datum_mempool) {
-        log_it(L_CRITICAL, "%s", c_error_memory_alloc);
-        return NULL;
-    }
-    datum_mempool->version = *(uint16_t*)(a_datum_mempool_ser + shift_size);
-    shift_size += sizeof(uint16_t);
-    datum_mempool->datum_count = *(uint16_t*)(a_datum_mempool_ser + shift_size);
-    shift_size += sizeof(uint16_t);
-    datum_mempool->data = DAP_NEW_Z_SIZE(dap_chain_datum_t*, datum_mempool->datum_count * sizeof(dap_chain_datum_t*));
-    for(int i = 0; i < datum_mempool->datum_count; i++) {
-        uint16_t size_one = *(uint16_t*)(a_datum_mempool_ser + shift_size);
-        shift_size += sizeof(uint16_t);
-        datum_mempool->data[i] = DAP_DUP((dap_chain_datum_t*)(a_datum_mempool_ser + shift_size));
-        shift_size += size_one;
-    }
-    assert(shift_size == a_datum_mempool_ser_size);
-    return datum_mempool;
-}
-
-void dap_datum_mempool_clean(dap_datum_mempool_t *datum)
-{
-    if(!datum)
-        return;
-    for(int i = 0; i < datum->datum_count; i++) {
-        DAP_DELETE(datum->data[i]);
-    }
-    DAP_DELETE(datum->data);
-    datum->data = NULL;
-}
-
-void dap_datum_mempool_free(dap_datum_mempool_t *datum)
-{
-    dap_datum_mempool_clean(datum);
-    DAP_DELETE(datum);
-}
-
-/**
- *
- */
-static char* s_calc_datum_hash(const void *a_datum_str, size_t datum_size)
-{
-    dap_chain_hash_fast_t a_hash;
-    dap_hash_fast( a_datum_str, datum_size, &a_hash);
-    size_t a_str_max = (sizeof(a_hash.raw) + 1) * 2 + 2; /* heading 0x */
-    char *a_str = DAP_NEW_Z_SIZE(char, a_str_max);
-
-//    size_t hash_len = dap_chain_hash_fast_to_str(&a_hash, a_str, a_str_max);
-    dap_chain_hash_fast_to_str(&a_hash, a_str, a_str_max);
-
-//    if(!hash_len) {
-//        DAP_DELETE(a_str);
-//        return NULL;
-//    }
-
-    return a_str;
-}
-
-static void enc_http_reply_encode_new(struct dap_http_simple *a_http_simple, dap_enc_key_t * key,
-        enc_http_delegate_t * a_http_delegate)
-{
-    //dap_enc_key_t * key = dap_enc_ks_find_http(a_http_simple->http);
-    if(key == NULL) {
-        log_it(L_ERROR, "Can't find http key.");
-        return;
-    }
-    if(a_http_delegate->response) {
-
-        if(a_http_simple->reply)
-            DAP_DELETE(a_http_simple->reply);
-
-        size_t l_reply_size_max = dap_enc_code_out_size(a_http_delegate->key,
-                a_http_delegate->response_size,
-                DAP_ENC_DATA_TYPE_RAW);
-
-        a_http_simple->reply = DAP_NEW_SIZE(void, l_reply_size_max);
-        a_http_simple->reply_size = dap_enc_code(a_http_delegate->key,
-                a_http_delegate->response, a_http_delegate->response_size,
-                a_http_simple->reply, l_reply_size_max,
-                DAP_ENC_DATA_TYPE_RAW);
-
-        /*/ decode test
-         size_t l_response_dec_size_max = a_http_simple->reply_size ? a_http_simple->reply_size * 2 + 16 : 0;
-         char * l_response_dec = a_http_simple->reply_size ? DAP_NEW_Z_SIZE(char, l_response_dec_size_max) : NULL;
-         size_t l_response_dec_size = 0;
-         if(a_http_simple->reply_size)
-         l_response_dec_size = dap_enc_decode(a_http_delegate->key,
-         a_http_simple->reply, a_http_simple->reply_size,
-         l_response_dec, l_response_dec_size_max,
-         DAP_ENC_DATA_TYPE_RAW);
-         l_response_dec_size_max = 0;*/
-    }
-
-}
-
-/**
- * @brief
- * @param cl_st HTTP server instance
- * @param arg for return code
- */
-void chain_mempool_proc(struct dap_http_simple *cl_st, void * arg)
-{
-    http_status_code_t * return_code = (http_status_code_t*) arg;
-    // save key while it alive, i.e. still exist
-    dap_enc_key_t *l_enc_key = dap_enc_ks_find_http(cl_st->http_client);
-    //dap_enc_key_serialize_t *key_ser = dap_enc_key_serialize(key_tmp);
-    //dap_enc_key_t *key = dap_enc_key_deserialize(key_ser, sizeof(dap_enc_key_serialize_t));
-
-    // read header
-    dap_http_header_t *hdr_session_close_id =
-            (cl_st->http_client) ? dap_http_header_find(cl_st->http_client->in_headers, "SessionCloseAfterRequest") : NULL;
-    dap_http_header_t *hdr_key_id =
-            (hdr_session_close_id && cl_st->http_client) ? dap_http_header_find(cl_st->http_client->in_headers, "KeyID") : NULL;
-
-    enc_http_delegate_t *l_enc_delegate = enc_http_request_decode(cl_st);
-    if(l_enc_delegate) {
-        char *suburl = l_enc_delegate->url_path;
-        byte_t *l_request_data = l_enc_delegate->request_bytes;
-        size_t l_request_size = l_enc_delegate->request_size;
-        const char * l_gdb_datum_pool = dap_config_get_item_str_default(g_config, "mempool", "gdb_group", "datum-pool");
-        //printf("!!***!!! chain_mempool_proc arg=%d suburl=%s str=%s len=%d\n", arg, suburl, request_str, request_size);
-        if(l_request_data && l_request_size > 1) {
-            //  find what to do
-            uint8_t action = DAP_DATUM_MEMPOOL_NONE; //*(uint8_t*) request_str;
-            if(l_enc_delegate->url_path_size > 0) {
-                if(!strcmp(suburl, "add"))
-                    action = DAP_DATUM_MEMPOOL_ADD;
-                else if(!strcmp(suburl, "check"))
-                    action = DAP_DATUM_MEMPOOL_CHECK;
-                else if(!strcmp(suburl, "del"))
-                    action = DAP_DATUM_MEMPOOL_DEL;
-            }
-            dap_datum_mempool_t *datum_mempool =
-                    (action != DAP_DATUM_MEMPOOL_NONE) ?
-                            dap_datum_mempool_deserialize((uint8_t*) l_request_data, (size_t) l_request_size) : NULL;
-            if(datum_mempool){
-                dap_datum_mempool_free(datum_mempool);
-                char *a_key = s_calc_datum_hash(l_request_data, (size_t) l_request_size);
-                switch (action)
-                {
-                case DAP_DATUM_MEMPOOL_ADD: // add datum in base
-                    //a_value = DAP_NEW_Z_SIZE(char, request_size * 2);
-                    //bin2hex((char*) a_value, (const unsigned char*) request_str, request_size);
-                    if ( dap_global_db_set_sync(l_gdb_datum_pool, a_key, l_request_data, l_request_size, false) == 0 ) {
-                        *return_code = Http_Status_OK;
-                    }
-                    log_it(L_INFO, "Insert hash: key=%s result:%s", a_key,
-                            (*return_code == Http_Status_OK) ? "OK" : "False!");
-                    break;
-
-                case DAP_DATUM_MEMPOOL_CHECK: // check datum in base
-
-                    strcpy(cl_st->reply_mime, "text/text");
-                    size_t l_datum_size = 0;
-                    byte_t *l_datum = dap_global_db_get_sync( l_gdb_datum_pool, a_key,&l_datum_size,NULL,NULL );
-                    if(l_datum) {
-                        l_enc_delegate->response = strdup("1");
-                        DAP_DEL_Z(l_datum);
-                        log_it(L_INFO, "Check hash: key=%s result: Present", a_key);
-                    }
-                    else {
-                        l_enc_delegate->response = strdup("0");
-                        log_it(L_INFO, "Check hash: key=%s result: Absent", a_key);
-                    }
-                    l_enc_delegate->response_size = l_datum_size;
-                    *return_code = Http_Status_OK;
-                    enc_http_reply_encode_new(cl_st, l_enc_key, l_enc_delegate);
-                    break;
-
-                case DAP_DATUM_MEMPOOL_DEL: // delete datum in base
-                    strcpy(cl_st->reply_mime, "text/text");
-                    if (dap_global_db_del_sync(l_gdb_datum_pool, a_key) == 0){
-                        l_enc_delegate->response = dap_strdup("1");
-                        log_it(L_INFO, "Delete hash: key=%s result: Ok", a_key);
-                    } else {
-                        l_enc_delegate->response = dap_strdup("0");
-                        log_it(L_INFO, "Delete hash: key=%s result: False!", a_key);
-                    }
-                    // TODO rework to async processing and return result of delete action
-                    *return_code = Http_Status_OK;
-                    enc_http_reply_encode_new(cl_st, l_enc_key, l_enc_delegate);
-                    break;
-
-                default: // unsupported command
-                    log_it(L_INFO, "Unknown request=%s! key=%s", (suburl) ? suburl : "-", a_key);
-                    enc_http_delegate_delete(l_enc_delegate);
-                    dap_enc_ks_delete(hdr_key_id->value);
-                    *return_code = Http_Status_BadRequest;
-                    return;
-                }
-                DAP_DEL_Z(a_key);
-            } else
-                *return_code = Http_Status_BadRequest;
-        } else
-            *return_code = Http_Status_BadRequest;
-
-        enc_http_delegate_delete(l_enc_delegate);
-    } else
-        *return_code = Http_Status_Unauthorized;
-
-    if (hdr_session_close_id && !strcmp(hdr_session_close_id->value, "yes") && hdr_key_id)
-        // close session
-        dap_enc_ks_delete(hdr_key_id->value);
-}
-
-/**
- * @brief chain_mempool_add_proc
- * @param sh HTTP server instance
- * @param url URL string
- */
-void dap_chain_mempool_add_proc(dap_http_server_t * a_http_server, const char * a_url)
-{
-    dap_http_simple_proc_add(a_http_server, a_url, 4096, chain_mempool_proc);
-}
-
 /**
  * @breif dap_chain_mempool_filter
  * @param a_chain chain whose mempool will be filtered.
@@ -1474,7 +1235,7 @@ void dap_chain_mempool_filter(dap_chain_t *a_chain, int *a_removed){
         return;
     }
     int l_removed = 0;
-    char * l_gdb_group = dap_chain_net_get_gdb_group_mempool_new(a_chain);
+    char * l_gdb_group = dap_chain_mempool_group_new(a_chain);
     size_t l_objs_size = 0;
     dap_time_t l_cut_off_time = dap_time_now() - 3 * 24 * 3600; // 3 days
     char l_cut_off_time_str[DAP_TIME_STR_SIZE] = {'\0'};
diff --git a/modules/mempool/include/dap_chain_mempool.h b/modules/mempool/include/dap_chain_mempool.h
index a62d51a5e1..ce8bcc9848 100644
--- a/modules/mempool/include/dap_chain_mempool.h
+++ b/modules/mempool/include/dap_chain_mempool.h
@@ -51,11 +51,18 @@ int dap_datum_mempool_init(void);
 
 extern const char* c_dap_datum_mempool_gdb_group;
 
-uint8_t* dap_datum_mempool_serialize(dap_datum_mempool_t *datum_mempool, size_t *size);
-dap_datum_mempool_t * dap_datum_mempool_deserialize(uint8_t *datum_mempool_str, size_t size);
-
-void dap_datum_mempool_clean(dap_datum_mempool_t *datum);
-void dap_datum_mempool_free(dap_datum_mempool_t *datum);
+/**
+ * @brief dap_chain_net_get_gdb_group_mempool
+ * @param l_chain
+ * @return
+ */
+DAP_STATIC_INLINE char *dap_chain_mempool_group_new(dap_chain_t *a_chain)
+{
+    dap_chain_net_t *l_net = a_chain ? dap_chain_net_by_id(a_chain->net_id) : NULL;
+    return l_net
+            ? dap_strdup_printf("%s.chain-%s.mempool", l_net->pub.gdb_groups_prefix, a_chain->name)
+            : NULL;
+}
 
 void dap_chain_mempool_add_proc(dap_http_server_t * a_http_server, const char * a_url);
 
diff --git a/modules/net/dap_chain_net.c b/modules/net/dap_chain_net.c
index f20f69afa4..7b1a7504bc 100644
--- a/modules/net/dap_chain_net.c
+++ b/modules/net/dap_chain_net.c
@@ -2474,7 +2474,7 @@ char * dap_chain_net_get_gdb_group_mempool_by_chain_type(dap_chain_net_t *a_net,
     {
         for(int i = 0; i < l_chain->datum_types_count; i++) {
             if(l_chain->datum_types[i] == a_datum_type)
-                return dap_chain_net_get_gdb_group_mempool_new(l_chain);
+                return dap_chain_mempool_group_new(l_chain);
         }
     }
     return NULL;
diff --git a/modules/net/dap_chain_node.c b/modules/net/dap_chain_node.c
index a52e02f7bd..6d43dd19b3 100644
--- a/modules/net/dap_chain_node.c
+++ b/modules/net/dap_chain_node.c
@@ -43,9 +43,13 @@
 #include "dap_chain_net.h"
 #include "dap_global_db.h"
 #include "dap_chain_node.h"
-#include "dap_chain_cs_esbocs.h"
+#include "dap_chain_cs_esbocs.h" // TODO set RPC callbacks for exclude consensus specific dependency
+#include "dap_chain_cs_blocks.h" // TODO set RPC callbacks for exclude storage type specific dependency
 #include "dap_chain_ledger.h"
 #include "dap_cli_server.h"
+#include "dap_chain_srv.h"
+#include "dap_chain_mempool.h"
+#include "dap_chain_datum_service_state.h"
 
 #define LOG_TAG "dap_chain_node"
 #define DAP_CHAIN_NODE_NET_STATES_INFO_CURRENT_VERSION 2
@@ -67,6 +71,14 @@ typedef struct dap_chain_node_net_states_info {
 
 #define node_info_v1_shift ( sizeof(uint16_t) + 16 + sizeof(dap_chain_node_role_t) )
 
+struct hardfork_states {
+    dap_ledger_hardfork_balances_t *balances;
+    dap_ledger_hardfork_condouts_t *condouts;
+    dap_ledger_hardfork_anchors_t  *anchors;
+    dap_chain_cs_blocks_hardfork_fees_t *fees;
+    dap_chain_srv_hardfork_state_t *service_states;
+};
+
 static const uint64_t s_cmp_delta_timestamp = (uint64_t)1000 /*sec*/ * (uint64_t)1000000000;
 static const uint64_t s_cmp_delta_event = 0;
 static const uint64_t s_cmp_delta_atom = 10;
@@ -369,7 +381,7 @@ void dap_chain_node_mempool_process_all(dap_chain_t *a_chain, bool a_force)
         fclose(l_file);
     }
 #endif
-    char *l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool_new(a_chain);
+    char *l_gdb_group_mempool = dap_chain_mempool_group_new(a_chain);
     size_t l_objs_size = 0;
     dap_global_db_obj_t *l_objs = dap_global_db_get_all_sync(l_gdb_group_mempool, &l_objs_size);
     if (l_objs_size) {
@@ -423,6 +435,44 @@ void dap_chain_node_mempool_process_all(dap_chain_t *a_chain, bool a_force)
     DAP_DELETE(l_gdb_group_mempool);
 }
 
+int dap_chain_node_hardfork_prepare(dap_chain_t *a_chain, dap_time_t a_last_block_timestamp)
+{
+    if (dap_strcmp(dap_chain_get_cs_type(a_chain), DAP_CHAIN_ESBOCS_CS_TYPE_STR))
+        return log_it(L_ERROR, "Can't prepare harfork for chain type %s is not supported", dap_chain_get_cs_type(a_chain)), -2;
+    dap_chain_net_t *l_net = dap_chain_net_by_id(a_chain->net_id);
+    assert(l_net);
+    struct hardfork_states *l_states = DAP_NEW_Z_RET_VAL_IF_FAIL(struct hardfork_states, -1, NULL);
+    l_states->balances = dap_ledger_states_aggregate(l_net->pub.ledger, a_last_block_timestamp, &l_states->condouts);
+    l_states->anchors = dap_ledger_anchors_aggregate(l_net->pub.ledger);
+    l_states->fees = dap_chain_cs_blocks_fees_aggregate(a_chain);
+    size_t l_state_size = 0;
+    l_states->service_states = dap_chain_srv_hardfork_all(l_net->pub.id);
+    dap_chain_srv_hardfork_state_t *it, *tmp;
+    DL_FOREACH_SAFE(l_states->service_states, it, tmp) {
+        if (it->uid.uint64 < (uint64_t)INT64_MIN)       // MSB is not set
+            continue;
+        const uint64_t l_max_step_size = DAP_CHAIN_ATOM_MAX_SIZE - sizeof(dap_chain_datum_service_state_t);
+        uint64_t l_step_size = dap_min(l_max_step_size, it->size);
+        byte_t *l_offset = it->data, *l_end = it->data + it->size * it->count;
+        while (l_offset < l_end) {
+            size_t l_cur_step_size = 0, i = 0;
+            while (l_cur_step_size < l_max_step_size && l_offset < l_end) {
+                size_t l_addition = dap_min((uint64_t)(l_end - l_offset), l_step_size);
+                l_cur_step_size += l_addition;
+                l_offset += l_addition;
+                i++;
+            }
+            dap_chain_datum_t *l_datum = dap_chain_datum_create(DAP_CHAIN_DATUM_SERVICE_STATE, NULL, sizeof(dap_chain_datum_service_state_t) + l_cur_step_size);
+            ((dap_chain_datum_service_state_t *)l_datum->data)->srv_uid = it->uid;
+            ((dap_chain_datum_service_state_t *)l_datum->data)->states_count = i;
+            DAP_DELETE(dap_chain_mempool_datum_add(l_datum, a_chain, "hex"));
+        }
+        DL_DELETE(l_states->service_states, it);
+        DAP_DELETE(it);
+    }
+    return 0;
+}
+
 
 /**
  * @brief
diff --git a/modules/net/include/dap_chain_net.h b/modules/net/include/dap_chain_net.h
index b438c77cad..3c7ca76f7a 100644
--- a/modules/net/include/dap_chain_net.h
+++ b/modules/net/include/dap_chain_net.h
@@ -152,19 +152,6 @@ int dap_chain_net_link_add(dap_chain_net_t *a_net, dap_stream_node_addr_t *a_add
 
 void dap_chain_net_purge(dap_chain_net_t *l_net);
 
-/**
- * @brief dap_chain_net_get_gdb_group_mempool
- * @param l_chain
- * @return
- */
-DAP_STATIC_INLINE char *dap_chain_net_get_gdb_group_mempool_new(dap_chain_t *a_chain)
-{
-    dap_chain_net_t *l_net = a_chain ? dap_chain_net_by_id(a_chain->net_id) : NULL;
-    return l_net
-            ? dap_strdup_printf("%s.chain-%s.mempool", l_net->pub.gdb_groups_prefix, a_chain->name)
-            : NULL;
-}
-
 DAP_STATIC_INLINE char *dap_chain_net_get_gdb_group_nochain_new(dap_chain_t *a_chain)
 {
     dap_chain_net_t *l_net = a_chain ? dap_chain_net_by_id(a_chain->net_id) : NULL;
diff --git a/modules/node-cli/dap_chain_node_cli_cmd.c b/modules/node-cli/dap_chain_node_cli_cmd.c
index f6d7003cc8..7cf44cc30d 100644
--- a/modules/node-cli/dap_chain_node_cli_cmd.c
+++ b/modules/node-cli/dap_chain_node_cli_cmd.c
@@ -112,7 +112,7 @@ int _cmd_mempool_add_ca(dap_chain_net_t *a_net, dap_chain_t *a_chain, dap_cert_t
 dap_chain_t *s_get_chain_with_datum(dap_chain_net_t *a_net, const char *a_datum_hash) {
     dap_chain_t *l_chain = NULL;
     DL_FOREACH(a_net->pub.chains, l_chain) {
-        char *l_gdb_mempool = dap_chain_net_get_gdb_group_mempool_new(l_chain);
+        char *l_gdb_mempool = dap_chain_mempool_group_new(l_chain);
         bool is_hash = dap_global_db_driver_is(l_gdb_mempool, a_datum_hash);
         DAP_DELETE(l_gdb_mempool);
         if (is_hash)
@@ -2131,7 +2131,7 @@ void s_com_mempool_list_print_for_chain(json_object* a_json_arr_reply, dap_chain
         DAP_DELETE(l_wallet_addr);
         return;
     }
-    char * l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool_new(a_chain);
+    char * l_gdb_group_mempool = dap_chain_mempool_group_new(a_chain);
     if(!l_gdb_group_mempool){
         dap_json_rpc_error_add(a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_MEMPOOL_LIST_CAN_NOT_GET_MEMPOOL_GROUP,
                                "%s.%s: chain not found\n", a_net->pub.name, a_chain->name);
@@ -2662,7 +2662,7 @@ void s_com_mempool_list_print_for_chain(json_object* a_json_arr_reply, dap_chain
 }
 
 static int mempool_delete_for_chain(dap_chain_t *a_chain, const char * a_datum_hash_str, json_object **a_json_arr_reply) {
-        char * l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool_new(a_chain);
+        char * l_gdb_group_mempool = dap_chain_mempool_group_new(a_chain);
         uint8_t *l_data_tmp = dap_global_db_get_sync(l_gdb_group_mempool, a_datum_hash_str,
                                                      NULL, NULL, NULL);
         if (!l_data_tmp) {
@@ -2750,7 +2750,7 @@ dap_chain_datum_t *s_com_mempool_check_datum_in_chain(dap_chain_t *a_chain, cons
 {
     if (!a_datum_hash_str)
         return NULL;
-    char *l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool_new(a_chain);
+    char *l_gdb_group_mempool = dap_chain_mempool_group_new(a_chain);
     uint8_t *l_data_tmp = dap_global_db_get_sync(l_gdb_group_mempool, a_datum_hash_str, NULL, NULL, NULL);
     DAP_DELETE(l_gdb_group_mempool);
     return (dap_chain_datum_t *)l_data_tmp;
@@ -2945,7 +2945,7 @@ int _cmd_mempool_proc(dap_chain_net_t *a_net, dap_chain_t *a_chain, const char *
     dap_chain_t *l_chain = !a_chain ? s_get_chain_with_datum(a_net, a_datum_hash) : a_chain;
 
     int ret = 0;
-    char *l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool_new(l_chain);
+    char *l_gdb_group_mempool = dap_chain_mempool_group_new(l_chain);
     if (!l_gdb_group_mempool){
         dap_json_rpc_error_add(*a_json_arr_reply, DAP_COM_MEMPOOL_PROC_LIST_ERROR_CAN_NOT_GROUP_NAME,
                                "Failed to get mempool group name on network %s", a_net->pub.name);
@@ -3199,13 +3199,13 @@ int _cmd_mempool_dump(dap_chain_net_t *a_net, dap_chain_t *a_chain, const char *
         return COM_DUMP_ERROR_NULL_IS_ARGUMENT_FUNCTION;
     }
     if (a_chain) {
-        char *l_group_mempool = dap_chain_net_get_gdb_group_mempool_new(a_chain);
+        char *l_group_mempool = dap_chain_mempool_group_new(a_chain);
         _cmd_mempool_dump_from_group(a_net->pub.id, l_group_mempool, a_datum_hash, a_hash_out_type, a_json_arr_reply);
         DAP_DELETE(l_group_mempool);
     } else {
         dap_chain_t *l_chain = NULL;
         DL_FOREACH(a_net->pub.chains, l_chain){
-            char *l_group_mempool = dap_chain_net_get_gdb_group_mempool_new(l_chain);
+            char *l_group_mempool = dap_chain_mempool_group_new(l_chain);
             if (!_cmd_mempool_dump_from_group(a_net->pub.id, l_group_mempool, a_datum_hash, a_hash_out_type, a_json_arr_reply)){
                 DAP_DELETE(l_group_mempool);
                 break;
@@ -3383,7 +3383,7 @@ int com_mempool(int a_argc, char **a_argv, void **a_str_reply)
                 return DAP_JSON_RPC_ERR_CODE_MEMORY_ALLOCATED;
             }
             if(l_chain) {
-                l_mempool_group = dap_chain_net_get_gdb_group_mempool_new(l_chain);
+                l_mempool_group = dap_chain_mempool_group_new(l_chain);
                 size_t l_objs_count = 0;
                 dap_global_db_obj_t *l_objs = dap_global_db_get_all_sync(l_mempool_group, &l_objs_count);
                 dap_global_db_objs_delete(l_objs, l_objs_count);
@@ -3405,7 +3405,7 @@ int com_mempool(int a_argc, char **a_argv, void **a_str_reply)
                 json_object_array_add(l_jobj_chains, l_jobj_chain);
             } else {
                 DL_FOREACH(l_net->pub.chains, l_chain) {
-                    l_mempool_group = dap_chain_net_get_gdb_group_mempool_new(l_chain);
+                    l_mempool_group = dap_chain_mempool_group_new(l_chain);
                     size_t l_objs_count = 0;
                     dap_global_db_obj_t *l_objs = dap_global_db_get_all_sync(l_mempool_group, &l_objs_count);
                     dap_global_db_objs_delete(l_objs, l_objs_count);
@@ -3489,7 +3489,7 @@ void _cmd_find_type_decree_in_chain(json_object *a_out, dap_chain_t *a_chain, ui
         }
     }
     if (a_where == ALL || a_where == MEMPOOL) {
-        char *l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool_new(a_chain);
+        char *l_gdb_group_mempool = dap_chain_mempool_group_new(a_chain);
         size_t l_mempool_count = 0;
         dap_global_db_obj_t *l_objs = dap_global_db_get_all_sync(l_gdb_group_mempool, &l_mempool_count);
         for (size_t i = 0; i < l_mempool_count; i++) {
@@ -4219,7 +4219,7 @@ int cmd_decree(int a_argc, char **a_argv, void **a_str_reply)
                 return -105;
             }
 
-            char * l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool_new(l_chain);
+            char * l_gdb_group_mempool = dap_chain_mempool_group_new(l_chain);
             if(!l_gdb_group_mempool) {
                 l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool_by_chain_type(l_net, CHAIN_TYPE_DECREE);
             }
@@ -5029,7 +5029,7 @@ static int s_check_cmd(int a_arg_index, int a_argc, char **a_argv, void **a_str_
     dap_chain_datum_t *l_datum = NULL;
     char *l_gdb_group = NULL;
 
-    l_gdb_group = dap_chain_net_get_gdb_group_mempool_new(l_chain);
+    l_gdb_group = dap_chain_mempool_group_new(l_chain);
     if (!l_gdb_group) {
         dap_cli_server_cmd_set_reply_text(a_str_reply, "Not found network group for chain: %s", l_chain->name);
         l_ret = -1;
diff --git a/modules/node-cli/dap_chain_node_cli_cmd_token.c b/modules/node-cli/dap_chain_node_cli_cmd_token.c
index 0ea06e1d4b..849fdfe23c 100644
--- a/modules/node-cli/dap_chain_node_cli_cmd_token.c
+++ b/modules/node-cli/dap_chain_node_cli_cmd_token.c
@@ -156,7 +156,7 @@ int com_token_decl_sign(int a_argc, char **a_argv, void **a_str_reply)
             return -7;
         }
 
-        char * l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool_new(l_chain);
+        char * l_gdb_group_mempool = dap_chain_mempool_group_new(l_chain);
         if(!l_gdb_group_mempool) {
             l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool_by_chain_type(l_net, CHAIN_TYPE_TOKEN);
         }
@@ -960,7 +960,7 @@ int com_token_decl(int a_argc, char ** a_argv, void **a_str_reply)
 
     // Add datum to mempool with datum_token hash as a key
     char *l_gdb_group_mempool = l_chain
-            ? dap_chain_net_get_gdb_group_mempool_new(l_chain)
+            ? dap_chain_mempool_group_new(l_chain)
             : dap_chain_net_get_gdb_group_mempool_by_chain_type(l_net, CHAIN_TYPE_TOKEN);
     if (!l_gdb_group_mempool) {
         dap_cli_server_cmd_set_reply_text(a_str_reply, "No suitable chain for placing token datum found");
@@ -1141,7 +1141,7 @@ int com_token_update(int a_argc, char ** a_argv, void **a_str_reply)
 
     // Add datum to mempool with datum_token hash as a key
     char *l_gdb_group_mempool = l_chain
-            ? dap_chain_net_get_gdb_group_mempool_new(l_chain)
+            ? dap_chain_mempool_group_new(l_chain)
             : dap_chain_net_get_gdb_group_mempool_by_chain_type(l_net, CHAIN_TYPE_TOKEN);
     if (!l_gdb_group_mempool) {
         dap_cli_server_cmd_set_reply_text(a_str_reply, "No suitable chain for placing token datum found");
@@ -1345,7 +1345,7 @@ int com_token_emit(int a_argc, char **a_argv, void **a_str_reply)
 
     //remove previous emission datum from mempool if have new signed emission datum
     if (l_emission_hash_str_remove) {
-        char *l_gdb_group_mempool_emission = dap_chain_net_get_gdb_group_mempool_new(l_chain_emission);
+        char *l_gdb_group_mempool_emission = dap_chain_mempool_group_new(l_chain_emission);
         dap_global_db_del_sync(l_gdb_group_mempool_emission, l_emission_hash_str_remove);
         DAP_DEL_Z(l_gdb_group_mempool_emission);
     }
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 1704d4d10e..1a3ecdbcc9 100644
--- a/modules/node-cli/dap_chain_node_cli_cmd_tx.c
+++ b/modules/node-cli/dap_chain_node_cli_cmd_tx.c
@@ -2181,7 +2181,7 @@ void json_rpc_tx_create(json_object *a_param, json_object *a_reply){
         return ;
     }
 
-    char *l_gdb_group_mempool_base_tx = dap_chain_net_get_gdb_group_mempool_new(l_chain);// get group name for mempool
+    char *l_gdb_group_mempool_base_tx = dap_chain_mempool_group_new(l_chain);// get group name for mempool
     bool l_placed = !dap_global_db_set(l_gdb_group_mempool_base_tx, l_tx_hash_str, l_datum_tx, l_datum_tx_size, false, NULL, NULL);
 
     DAP_DELETE(l_datum_tx);
@@ -2335,7 +2335,7 @@ int com_tx_create_json(int a_argc, char ** a_argv, void **reply)
         return DAP_CHAIN_NODE_CLI_COM_TX_CREATE_JSON_CAN_CHECK_TX_ADD_LEDGER;
     }
 
-    char *l_gdb_group_mempool_base_tx = dap_chain_net_get_gdb_group_mempool_new(l_chain);// get group name for mempool
+    char *l_gdb_group_mempool_base_tx = dap_chain_mempool_group_new(l_chain);// get group name for mempool
     bool l_placed = !dap_global_db_set(l_gdb_group_mempool_base_tx, l_tx_hash_str, l_datum_tx, l_datum_tx_size, false, NULL, NULL);
 
     DAP_DEL_Z(l_datum_tx);
@@ -2761,7 +2761,7 @@ int com_tx_verify(int a_argc, char **a_argv, void **a_str_reply)
         }
     }
     size_t l_datum_size = 0;
-    char *l_gdb_group = dap_chain_net_get_gdb_group_mempool_new(l_chain);
+    char *l_gdb_group = dap_chain_mempool_group_new(l_chain);
     dap_chain_datum_t *l_datum = (dap_chain_datum_t*)dap_global_db_get_sync(l_gdb_group, l_hex_str_from58 ? l_hex_str_from58 : l_tx_hash_str, &l_datum_size, NULL, NULL);
     DAP_DEL_Z(l_hex_str_from58);
     if (!l_datum) {
diff --git a/modules/service/datum/dap_chain_net_srv_datum.c b/modules/service/datum/dap_chain_net_srv_datum.c
index b2e0e30bd1..768e158fb1 100644
--- a/modules/service/datum/dap_chain_net_srv_datum.c
+++ b/modules/service/datum/dap_chain_net_srv_datum.c
@@ -131,7 +131,7 @@ static int s_srv_datum_cli(int argc, char ** argv, void **a_str_reply)
     dap_cli_server_cmd_find_option_val(argv, arg_index, argc, "datum", &l_datum_cmd_str);
     if ( l_datum_cmd_str != NULL ) {
         if ( strcmp(l_datum_cmd_str, "save") == 0) {
-            char * l_gdb_group = dap_chain_net_get_gdb_group_mempool_new(l_chain);
+            char * l_gdb_group = dap_chain_mempool_group_new(l_chain);
 
             size_t l_path_length = strlen(l_system_datum_folder)+8+strlen(l_datum_hash_str);
             char l_path[l_path_length];
@@ -237,7 +237,7 @@ void s_order_notficator(dap_store_obj_t *a_obj, void *a_arg)
     dap_chain_datum_tx_t *l_tx_cond = NULL;
     DL_FOREACH(l_net->pub.chains, l_chain) {
         size_t l_datum_size;
-        char *l_gdb_group = dap_chain_net_get_gdb_group_mempool_new(l_chain);
+        char *l_gdb_group = dap_chain_mempool_group_new(l_chain);
         l_datum = (dap_chain_datum_t *)dap_global_db_get_sync(l_gdb_group, l_tx_cond_hash_str, &l_datum_size, NULL, NULL);
         if (l_datum)
             break;
diff --git a/modules/service/emit-delegate/dap_chain_net_srv_emit_delegate.c b/modules/service/emit-delegate/dap_chain_net_srv_emit_delegate.c
index f3681b556a..7046bcfb1b 100644
--- a/modules/service/emit-delegate/dap_chain_net_srv_emit_delegate.c
+++ b/modules/service/emit-delegate/dap_chain_net_srv_emit_delegate.c
@@ -683,7 +683,7 @@ static int s_cli_sign(int a_argc, char **a_argv, int a_arg_index, json_object **
         dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PLACE, "Can't place transaction for delegated emission in mempool");
         return ERROR_PLACE;
     }
-    char *l_mempool_group = dap_chain_net_get_gdb_group_mempool_new(a_chain);
+    char *l_mempool_group = dap_chain_mempool_group_new(a_chain);
     dap_global_db_del_sync(l_mempool_group, l_tx_in_hash_str);
     DAP_DELETE(l_mempool_group);
     json_object * l_json_obj_create_val = json_object_new_object();
diff --git a/modules/type/blocks/dap_chain_cs_blocks.c b/modules/type/blocks/dap_chain_cs_blocks.c
index 89f3c0542d..5dad0dd5bf 100644
--- a/modules/type/blocks/dap_chain_cs_blocks.c
+++ b/modules/type/blocks/dap_chain_cs_blocks.c
@@ -694,7 +694,7 @@ static int s_cli_blocks(int a_argc, char ** a_argv, void **a_str_reply)
         }break;
         case SUBCMD_NEW_DATUM_ADD:{
             size_t l_datums_count=1;
-            char * l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool_new(l_chain);
+            char * l_gdb_group_mempool = dap_chain_mempool_group_new(l_chain);
             dap_chain_datum_t ** l_datums = DAP_NEW_Z_SIZE(dap_chain_datum_t*,
                                                            sizeof(dap_chain_datum_t*)*l_datums_count);
             if (!l_datums) {
diff --git a/modules/type/blocks/include/dap_chain_cs_blocks.h b/modules/type/blocks/include/dap_chain_cs_blocks.h
index b792f244e7..bfa7ac0154 100644
--- a/modules/type/blocks/include/dap_chain_cs_blocks.h
+++ b/modules/type/blocks/include/dap_chain_cs_blocks.h
@@ -91,6 +91,8 @@ int dap_chain_cs_blocks_init();
 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);
+
 DAP_STATIC_INLINE char *dap_chain_cs_blocks_get_fee_group(const char *a_net_name)
 {
     return dap_strdup_printf("local.%s.fees", a_net_name);
-- 
GitLab


From a044e7f181fd2b6f9f341e2ad13b0b6a78524b48 Mon Sep 17 00:00:00 2001
From: "roman.khlopkov" <roman.khlopkov@demlabs.net>
Date: Tue, 17 Dec 2024 19:24:53 +0300
Subject: [PATCH 3/9] [*] Harfork transfer in progress

---
 modules/chain/dap_chain.c                     | 11 +--
 modules/chain/include/dap_chain.h             |  6 +-
 .../consensus/esbocs/dap_chain_cs_esbocs.c    | 19 ++--
 modules/ledger/include/dap_chain_ledger.h     |  4 +-
 modules/net/dap_chain_net.c                   |  5 +-
 modules/net/dap_chain_node.c                  | 68 ++++++++++++++
 modules/net/include/dap_chain_node.h          |  3 +
 .../service/voting/dap_chain_net_srv_voting.c | 91 +++++++++----------
 .../type/blocks/include/dap_chain_cs_blocks.h |  1 +
 9 files changed, 138 insertions(+), 70 deletions(-)

diff --git a/modules/chain/dap_chain.c b/modules/chain/dap_chain.c
index 15de87d389..16c76c5719 100644
--- a/modules/chain/dap_chain.c
+++ b/modules/chain/dap_chain.c
@@ -150,11 +150,6 @@ int dap_chain_purge(dap_chain_t *a_chain)
     return ret + dap_chain_cs_purge(a_chain);
 }
 
-int dap_chain_hardfork_preare(dap_chain_t *a_chain, uint64_t a_atom_num)
-{
-
-}
-
 /**
  * @brief
  * delete dap chain object
@@ -185,7 +180,7 @@ void dap_chain_delete(dap_chain_t *a_chain)
         DAP_DEL_MULTY(DAP_CHAIN_PVT(a_chain)->file_storage_dir, DAP_CHAIN_PVT(a_chain));
     }
     DAP_DEL_MULTY(a_chain->name, a_chain->net_name, a_chain->datum_types,
-        a_chain->autoproc_datum_types, a_chain->authorized_nodes_addrs, a_chain->_inheritor);
+        a_chain->autoproc_datum_types, a_chain->authorized_nodes_addrs), a_chain->_inheritor);
     pthread_rwlock_destroy(&a_chain->rwlock);
     pthread_rwlock_destroy(&a_chain->cell_rwlock);
     DAP_DELETE(a_chain);
@@ -732,7 +727,7 @@ void dap_chain_atom_confirmed_notify_add(dap_chain_t *a_chain, dap_chain_callbac
  * @param a_atom_hash
  * @return
  */
-bool dap_chain_get_atom_last_hash_num(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)
+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)
 {
     dap_return_val_if_fail(a_atom_hash || a_atom_num, false);
     dap_chain_atom_iter_t *l_iter = a_chain->callback_atom_iter_create(a_chain, a_cell_id, NULL);
@@ -743,6 +738,8 @@ bool dap_chain_get_atom_last_hash_num(dap_chain_t *a_chain, dap_chain_cell_id_t
         *a_atom_hash = l_iter->cur_hash ? *l_iter->cur_hash : (dap_hash_fast_t){0};
     if (a_atom_num)
         *a_atom_num = l_iter->cur_num;
+    if (a_atom_timestamp)
+        *a_atom_timestamp = l_iter->cur_ts;
     a_chain->callback_atom_iter_delete(l_iter);
     return true;
 }
diff --git a/modules/chain/include/dap_chain.h b/modules/chain/include/dap_chain.h
index 22b4d0d643..e8166cfa9c 100644
--- a/modules/chain/include/dap_chain.h
+++ b/modules/chain/include/dap_chain.h
@@ -240,6 +240,7 @@ typedef struct dap_chain {
 
     dap_config_t *config;
 
+    struct hardfork_states *hardfork_data;
     void * _pvt; // private data
     void * _inheritor; // inheritor object
 } dap_chain_t;
@@ -275,6 +276,7 @@ typedef struct dap_chain_pvt {
     char *cs_type;
     bool cs_started;
     bool need_reorder;
+
 } dap_chain_pvt_t;
 
 #define DAP_CHAIN_PVT(a) ((dap_chain_pvt_t *)a->_pvt)
@@ -315,10 +317,10 @@ void dap_chain_datum_notify(dap_chain_cell_t *a_chain_cell,  dap_hash_fast_t *a_
 void dap_chain_datum_removed_notify(dap_chain_cell_t *a_chain_cell,  dap_hash_fast_t *a_hash);
 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(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);
+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);
 DAP_STATIC_INLINE bool dap_chain_get_atom_last_hash(dap_chain_t *a_chain, dap_chain_cell_id_t a_cell_id, dap_hash_fast_t *a_atom_hash)
 {
-    return dap_chain_get_atom_last_hash_num(a_chain, a_cell_id, a_atom_hash, NULL);
+    return dap_chain_get_atom_last_hash_num_ts(a_chain, a_cell_id, a_atom_hash, NULL, NULL);
 }
 ssize_t dap_chain_atom_save(dap_chain_cell_t *a_chain_cell, const uint8_t *a_atom, size_t a_atom_size, dap_hash_fast_t *a_new_atom_hash);
 int dap_cert_chain_file_save(dap_chain_datum_t *datum, char *net_name);
diff --git a/modules/consensus/esbocs/dap_chain_cs_esbocs.c b/modules/consensus/esbocs/dap_chain_cs_esbocs.c
index fe4100fc0c..64db92802f 100644
--- a/modules/consensus/esbocs/dap_chain_cs_esbocs.c
+++ b/modules/consensus/esbocs/dap_chain_cs_esbocs.c
@@ -215,10 +215,11 @@ void dap_chain_cs_esbocs_deinit(void)
 {
 }
 
-void dap_chain_esbocs_change_debug_mode(dap_chain_t *a_chain, bool a_enable){
-    dap_chain_cs_blocks_t *l_bocks = DAP_CHAIN_CS_BLOCKS(a_chain);
-    dap_chain_esbocs_pvt_t *pvt = PVT(l_bocks);
-    pvt->debug = a_enable;
+void dap_chain_esbocs_change_debug_mode(dap_chain_t *a_chain, bool a_enable)
+{
+    dap_chain_cs_blocks_t *l_blocks = DAP_CHAIN_CS_BLOCKS(a_chain);
+    dap_chain_esbocs_t *l_esbocs = DAP_CHAIN_ESBOCS(l_blocks);
+    PVT(l_esbocs)->debug = a_enable;
 }
 
 static int s_callback_new(dap_chain_t *a_chain, dap_config_t *a_chain_cfg)
@@ -1204,8 +1205,11 @@ static bool s_session_round_new(void *a_arg)
     a_session->listen_ensure = 0;
     uint64_t l_cur_atom_count = a_session->chain->callback_count_atom(a_session->chain);
     a_session->is_hardfork = a_session->esbocs->hardfork_from && l_cur_atom_count >= a_session->esbocs->hardfork_from;
-    if (l_cur_atom_count && l_cur_atom_count == a_session->esbocs->hardfork_from)
-        dap_chain_node_hardfork_prepare(a_session->chain);
+    if (l_cur_atom_count && l_cur_atom_count == a_session->esbocs->hardfork_from) {
+        dap_time_t l_last_block_timestamp = 0;
+        dap_chain_get_atom_last_hash_num_ts(a_session->chain, c_cell_id_hardfork, NULL, NULL, &l_last_block_timestamp);
+        dap_chain_node_hardfork_prepare(a_session->chain, l_last_block_timestamp);
+    }
     return false;
 }
 
@@ -1724,8 +1728,7 @@ static void s_session_candidate_verify(dap_chain_esbocs_session_t *a_session, da
     }
     // Process candidate
     a_session->processing_candidate = a_candidate;
-    dap_chain_cs_blocks_t *l_blocks = DAP_CHAIN_CS_BLOCKS(a_session->chain);
-    dap_chain_atom_verify_res_t l_verify_status = l_blocks->chain->callback_atom_verify(l_blocks->chain, a_candidate, a_candidate_size, a_candidate_hash);
+    dap_chain_atom_verify_res_t l_verify_status = a_session->chain->callback_atom_verify(a_session->chain, a_candidate, a_candidate_size, a_candidate_hash);
     if (l_verify_status == ATOM_ACCEPT || l_verify_status == ATOM_FORK) {
         // validation - OK, gen event Approve
         s_message_send(a_session, DAP_CHAIN_ESBOCS_MSG_TYPE_APPROVE, a_candidate_hash,
diff --git a/modules/ledger/include/dap_chain_ledger.h b/modules/ledger/include/dap_chain_ledger.h
index 2a47f928c1..bd18479975 100644
--- a/modules/ledger/include/dap_chain_ledger.h
+++ b/modules/ledger/include/dap_chain_ledger.h
@@ -40,16 +40,18 @@
 
 #define DAP_CHAIN_NET_SRV_TRANSFER_ID 0x07
 #define DAP_CHAIN_NET_SRV_BLOCK_REWARD_ID 0x08
+#define DAP_CHAIN_DATUM_TX_TSD_TYPE_TRACKER 0xf0fa
 
 typedef struct dap_ledger {
     dap_chain_net_t *net;
+    bool is_hardfork_data;
     void *_internal;
 } dap_ledger_t;
 
 typedef struct dap_ledger_tracker {
     dap_hash_fast_t voting_hash;
     uint256_t colored_value;
-} dap_ledger_tracker_t;
+} DAP_ALIGN_PACKED dap_ledger_tracker_t;
 
 typedef struct dap_ledger_hardfork_balances {
     dap_chain_addr_t addr;
diff --git a/modules/net/dap_chain_net.c b/modules/net/dap_chain_net.c
index 7b1a7504bc..6649800c7a 100644
--- a/modules/net/dap_chain_net.c
+++ b/modules/net/dap_chain_net.c
@@ -3189,12 +3189,13 @@ static void s_sync_timer_callback(void *a_arg)
     l_net_pvt->sync_context.cur_chain->state = CHAIN_SYNC_STATE_WAITING;
     dap_chain_ch_sync_request_t l_request = {};
     uint64_t l_last_num = 0;
-    if (!dap_chain_get_atom_last_hash_num(l_net_pvt->sync_context.cur_chain,
+    if (!dap_chain_get_atom_last_hash_num_ts(l_net_pvt->sync_context.cur_chain,
                                             l_net_pvt->sync_context.cur_cell
                                             ? l_net_pvt->sync_context.cur_cell->id
                                             : c_dap_chain_cell_id_null,
                                             &l_request.hash_from,
-                                            &l_last_num)) {
+                                            &l_last_num,
+                                            NULL)) {
         log_it(L_ERROR, "Can't get last atom hash and number for chain %s with net %s", l_net_pvt->sync_context.cur_chain->name,
                                                                                         l_net->pub.name);
         return;
diff --git a/modules/net/dap_chain_node.c b/modules/net/dap_chain_node.c
index 6d43dd19b3..202aa5d124 100644
--- a/modules/net/dap_chain_node.c
+++ b/modules/net/dap_chain_node.c
@@ -71,7 +71,16 @@ typedef struct dap_chain_node_net_states_info {
 
 #define node_info_v1_shift ( sizeof(uint16_t) + 16 + sizeof(dap_chain_node_role_t) )
 
+enum hardfork_state {
+    STATE_BALANCES = 0,
+    STATE_CONDOUTS,
+    STATE_ANCHORS,
+    STATE_FEES,
+    STATE_SERVICES
+};
+
 struct hardfork_states {
+    enum hardfork_state state_current;
     dap_ledger_hardfork_balances_t *balances;
     dap_ledger_hardfork_condouts_t *condouts;
     dap_ledger_hardfork_anchors_t  *anchors;
@@ -470,9 +479,68 @@ int dap_chain_node_hardfork_prepare(dap_chain_t *a_chain, dap_time_t a_last_bloc
         DL_DELETE(l_states->service_states, it);
         DAP_DELETE(it);
     }
+    a_chain->hardfork_data = l_states;
+    DAP_CHAIN_CS_BLOCKS(a_chain)->is_hardfork_data = true;
+    l_net->pub.ledger->is_hardfork_data = true;
     return 0;
 }
 
+dap_chain_datum_t *s_datum_tx_create(dap_chain_addr_t *a_addr, const char *a_ticker, uint256_t a_value, dap_list_t *a_trackers)
+{
+    dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create();
+    if (!l_tx)
+        return NULL;
+    if (dap_chain_datum_tx_add_out_ext_item(&l_tx, a_addr, a_value, a_ticker) != 1) {
+        dap_chain_datum_tx_delete(l_tx);
+        return NULL;
+    }
+    for (dap_list_t *it = a_trackers; it; it = it->next) {
+        dap_ledger_tracker_t *l_tracker = it->data;
+        dap_chain_tx_tsd_t *l_tracker_tsd = dap_chain_datum_tx_item_tsd_create(l_tracker, DAP_CHAIN_DATUM_TX_TSD_TYPE_TRACKER, sizeof(dap_ledger_tracker_t));
+        if (!l_tracker_tsd) {
+            dap_chain_datum_tx_delete(l_tx);
+            return NULL;
+        }
+        if (dap_chain_datum_tx_add_item(&l_tx, l_tracker_tsd) != 1) {
+            dap_chain_datum_tx_delete(l_tx);
+            return NULL;
+        }
+    }
+    dap_chain_datum_t *l_datum_tx = dap_chain_datum_create(DAP_CHAIN_DATUM_TX, l_tx, dap_chain_datum_tx_get_size(l_tx));
+    dap_chain_datum_tx_delete(l_tx);
+    return l_datum_tx;
+}
+
+int dap_chain_node_hardfork_process(dap_chain_t *a_chain)
+{
+    dap_return_val_if_fail(a_chain, -1);
+    if (!a_chain->hardfork_data)
+        return log_it(L_ERROR, "Can't process chain with no harfork data. Use dap_chain_node_hardfork_prepare() for collect it first"), -2;
+    struct hardfork_states *l_states = a_chain->hardfork_data;
+    switch (l_states->state_current) {
+    case STATE_BALANCES:
+        for (dap_ledger_hardfork_balances_t *it = l_states->balances; it; it = it->next) {
+            dap_chain_datum_t *l_tx = s_datum_tx_create(&it->addr, it->ticker, it->value, it->trackers);
+            if (!l_tx)
+                return -3;
+            if (!a_chain->callback_add_datums(a_chain, &l_tx, 1)) {
+                log_it(L_NOTICE, "Hardfork processed to datum tx with addr %s", dap_chain_addr_to_str_static(&it->addr));
+                break;
+            }
+        }
+        break;
+    case STATE_CONDOUTS:
+        break;
+    case STATE_ANCHORS:
+        break;
+    case STATE_FEES:
+        break;
+    case STATE_SERVICES:
+        break;
+    // No default here
+    }
+    return 0;
+}
 
 /**
  * @brief
diff --git a/modules/net/include/dap_chain_node.h b/modules/net/include/dap_chain_node.h
index 203ed53078..cbc9a92cfc 100644
--- a/modules/net/include/dap_chain_node.h
+++ b/modules/net/include/dap_chain_node.h
@@ -101,6 +101,9 @@ bool dap_chain_node_mempool_process(dap_chain_t *a_chain, dap_chain_datum_t *a_d
 void dap_chain_node_mempool_process_all(dap_chain_t *a_chain, bool a_force);
 bool dap_chain_node_mempool_autoproc_init();
 inline static void dap_chain_node_mempool_autoproc_deinit() {}
+
+int dap_chain_node_hardfork_prepare(dap_chain_t *a_chain, dap_time_t a_last_block_timestamp);
+
 dap_list_t *dap_chain_node_get_states_list_sort(dap_chain_net_t *a_net, dap_chain_node_addr_t *a_ignored, size_t a_ignored_count);
 dap_string_t *dap_chain_node_states_info_read(dap_chain_net_t *a_net, dap_stream_node_addr_t a_addr);
 int dap_chain_node_cli_cmd_values_parse_net_chain_for_json(json_object* a_json_arr_reply, int *a_arg_index, int a_argc,
diff --git a/modules/service/voting/dap_chain_net_srv_voting.c b/modules/service/voting/dap_chain_net_srv_voting.c
index 96b7ddc57c..b6f3cd706d 100644
--- a/modules/service/voting/dap_chain_net_srv_voting.c
+++ b/modules/service/voting/dap_chain_net_srv_voting.c
@@ -64,6 +64,7 @@ struct srv_voting {
 
 static void *s_callback_start(dap_chain_net_id_t UNUSED_ARG a_net_id, dap_config_t UNUSED_ARG *a_config);
 static void s_callback_delete(void *a_service_internal);
+static byte_t *s_votings_backup(dap_chain_net_id_t a_net_id, uint64_t *a_state_size, uint32_t *a_state_count);
 
 static int s_voting_ledger_verificator_callback(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, bool a_apply);
 static bool s_datum_tx_voting_verification_delete_callback(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash);
@@ -100,7 +101,7 @@ int dap_chain_net_srv_voting_init()
 
     
     dap_chain_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_VOTING_ID };
-    dap_chain_static_srv_callbacks_t l_srv_callbacks = { .start = s_callback_start, .delete = s_callback_delete };
+    dap_chain_static_srv_callbacks_t l_srv_callbacks = { .start = s_callback_start, .delete = s_callback_delete, .hardfork_prepare = s_votings_backup };
     int ret = dap_chain_srv_add(l_uid, "voting", &l_srv_callbacks);
     if (ret) {
         log_it(L_ERROR, "Can't register voting service");
@@ -1426,59 +1427,49 @@ static size_t s_voting_serial_size_calc(struct voting *a_voting, size_t *a_votes
     return ret;
 }
 
-static int s_voting_dump(dap_chain_datum_t **a_dump_datum, struct voting *a_voting)
+static byte_t *s_votings_backup(dap_chain_net_id_t a_net_id, uint64_t *a_state_size, uint32_t *a_state_count)
 {
-    size_t l_votes_count = 0;
-    size_t l_voting_size = s_voting_serial_size_calc(a_voting, &l_votes_count);
-    dap_chain_datum_t *l_dump_datum = DAP_REALLOC(*a_dump_datum, dap_chain_datum_size(*a_dump_datum) + l_voting_size);
-    if (!l_dump_datum) {
-        DAP_DELETE(*a_dump_datum);
-        return -1;
-    }
-    l_dump_datum->header.data_size += l_voting_size;
-    struct voting_serial *cur = (struct voting_serial *)(l_dump_datum->data + l_dump_datum->header.data_size);
-    *cur = (struct voting_serial) {
-            .size = l_voting_size,
-            .hash = a_voting->hash,
-            .voting_start = a_voting->start_time,
-            .voting_expire = a_voting->params->voting_expire,
-            .votes_max_count = a_voting->params->votes_max_count,
-            .votes_count = l_votes_count,
-            .delegate_key_required = a_voting->params->delegate_key_required,
-            .vote_changing_allowed = a_voting->params->vote_changing_allowed
-    };
-    byte_t *l_tsd = dap_tsd_write(cur->question_n_options_n_votes, VOTING_TSD_TYPE_QUESTION, a_voting->params->question, strlen(a_voting->params->question));
-    for (dap_list_t *it = a_voting->params->options; it; it = it->next)
-        l_tsd = dap_tsd_write(l_tsd, VOTING_TSD_TYPE_OPTION, it->data, strlen(it->data));
-    for (dap_list_t *it = a_voting->votes; it; it = it->next)
-        l_tsd = dap_tsd_write(l_tsd, VOTING_TSD_TYPE_VOTE, it->data, sizeof(struct vote));
-    assert(l_tsd == l_dump_datum->data + l_dump_datum->header.data_size);
-    *a_dump_datum = l_dump_datum;
-    return 0;
-}
-
-static int s_votings_backup(dap_chain_net_t *a_net)
-{
-    struct voting *votings_ht = s_votings_ht_get(a_net->pub.id);
+    if (a_state_count)
+        *a_state_count = 0;
+    dap_chain_net_t *l_net = dap_chain_net_by_id(a_net_id);
+    assert(l_net);
+    struct voting *votings_ht = s_votings_ht_get(l_net->pub.id);
     if (!votings_ht) {
-        log_it(L_INFO, "No data to backup for voting service for net id 0x%016" DAP_UINT64_FORMAT_x, a_net->pub.id.uint64);
-        return 0;
+        log_it(L_INFO, "No data to backup for voting service for net id 0x%016" DAP_UINT64_FORMAT_x, l_net->pub.id.uint64);
+        return NULL;
     }
-    dap_chain_datum_t *l_hardfork_state_datum = dap_chain_datum_create(DAP_CHAIN_DATUM_SERVICE_STATE, NULL, sizeof(dap_chain_datum_service_state_t));
-    if (!l_hardfork_state_datum)
-        return -1;
-    size_t i = 0;
-    for (struct voting *it = votings_ht; it; it = it->hh.next, i++) {
-        int ret = s_voting_dump(&l_hardfork_state_datum, it);
-        if (ret)
-            return ret;
+    size_t l_states_count = HASH_COUNT(votings_ht);
+    byte_t *ret = (byte_t *)DAP_NEW_Z_COUNT_RET_VAL_IF_FAIL(struct voting_serial, l_states_count, NULL, NULL);
+    size_t l_total_size = 0;
+    for (struct voting *it = votings_ht; it; it = it->hh.next) {
+        size_t l_votes_count = 0;
+        size_t l_voting_size = s_voting_serial_size_calc(it, &l_votes_count);
+        ret = DAP_REALLOC_RET_VAL_IF_FAIL(ret, l_total_size + l_voting_size, NULL, NULL);
+        struct voting_serial *cur = (struct voting_serial *)(ret + l_total_size);
+        l_total_size += l_voting_size;
+        *cur = (struct voting_serial) {
+                .size = l_voting_size,
+                .hash = it->hash,
+                .voting_start = it->start_time,
+                .voting_expire = it->params->voting_expire,
+                .votes_max_count = it->params->votes_max_count,
+                .votes_count = l_votes_count,
+                .delegate_key_required = it->params->delegate_key_required,
+                .vote_changing_allowed = it->params->vote_changing_allowed
+        };
+        byte_t *l_tsd = dap_tsd_write(cur->question_n_options_n_votes, VOTING_TSD_TYPE_QUESTION, it->params->question, strlen(it->params->question));
+        for (dap_list_t *lst = it->params->options; lst; lst = lst->next)
+            l_tsd = dap_tsd_write(l_tsd, VOTING_TSD_TYPE_OPTION, lst->data, strlen(lst->data));
+        for (dap_list_t *lst = it->votes; lst; lst = lst->next)
+            l_tsd = dap_tsd_write(l_tsd, VOTING_TSD_TYPE_VOTE, lst->data, sizeof(struct vote));
+        assert(l_tsd == ret + l_total_size);
     }
-    ((dap_chain_datum_service_state_t *)l_hardfork_state_datum->data)->srv_uid = (dap_chain_srv_uid_t) { .uint64 = DAP_CHAIN_NET_SRV_VOTING_ID };
-    ((dap_chain_datum_service_state_t *)l_hardfork_state_datum->data)->states_count = i;
-    dap_chain_t *l_chain = dap_chain_net_get_default_chain_by_chain_type(a_net, CHAIN_TYPE_TX);
-    char *l_datum_hash = dap_chain_mempool_datum_add(l_hardfork_state_datum, l_chain, "hex");
-    log_it(L_INFO, "Datum hash %s with voting service states successfully placed in mempool", l_datum_hash);
-    DAP_DELETE(l_datum_hash);
+    if (a_state_count)
+        *a_state_count = l_states_count;
+    if (a_state_size)
+        *a_state_size = l_total_size / l_states_count;
+    if (*a_state_size)
+        (*a_state_size)--;
     return 0;
 }
 
diff --git a/modules/type/blocks/include/dap_chain_cs_blocks.h b/modules/type/blocks/include/dap_chain_cs_blocks.h
index bfa7ac0154..815c0076c6 100644
--- a/modules/type/blocks/include/dap_chain_cs_blocks.h
+++ b/modules/type/blocks/include/dap_chain_cs_blocks.h
@@ -45,6 +45,7 @@ typedef struct dap_chain_cs_blocks
    // For new block creating
    dap_chain_block_t * block_new;
    size_t block_new_size;
+   bool is_hardfork_data;
 
    dap_chain_cs_blocks_callback_t callback_delete;
    dap_chain_cs_blocks_callback_block_create_t callback_block_create;
-- 
GitLab


From 19c7da778e8586592edaf851e699cce7cc826f2a Mon Sep 17 00:00:00 2001
From: "roman.khlopkov" <roman.khlopkov@demlabs.net>
Date: Fri, 27 Dec 2024 18:08:58 +0300
Subject: [PATCH 4/9] [*] Anchors aggregator logic fix

---
 .../datum/include/dap_chain_datum_tx_tsd.h    |  2 +
 modules/ledger/dap_chain_ledger_decree.c      | 47 +++++++++++++++++--
 modules/ledger/dap_chain_ledger_tx.c          | 33 +++++++++++--
 modules/ledger/include/dap_chain_ledger.h     |  3 +-
 modules/net/dap_chain_node.c                  |  6 +--
 modules/net/include/dap_chain_net.h           |  1 +
 6 files changed, 77 insertions(+), 15 deletions(-)

diff --git a/modules/datum/include/dap_chain_datum_tx_tsd.h b/modules/datum/include/dap_chain_datum_tx_tsd.h
index 810988af0f..7f3cfa7bdc 100644
--- a/modules/datum/include/dap_chain_datum_tx_tsd.h
+++ b/modules/datum/include/dap_chain_datum_tx_tsd.h
@@ -4,6 +4,8 @@
 #include "dap_chain_common.h"
 #include "dap_tsd.h"
 
+#define DAP_CHAIN_DATUM_TX_TSD_TYPE_TRACKER 0xf0fa
+
 typedef struct dap_chain_tx_tsd {
     struct {
         dap_chain_tx_item_type_t type;
diff --git a/modules/ledger/dap_chain_ledger_decree.c b/modules/ledger/dap_chain_ledger_decree.c
index 63c25704dc..fb6ee4310d 100644
--- a/modules/ledger/dap_chain_ledger_decree.c
+++ b/modules/ledger/dap_chain_ledger_decree.c
@@ -638,17 +638,49 @@ 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)
+static bool s_compare_anchors(dap_ledger_t *a_ledger, dap_ledger_hardfork_anchors_t *a_exist, dap_ledger_hardfork_anchors_t *a_comp)
 {
-    return a_list1->decree_subtype != a_list2->decree_subtype;
+    bool l_stake_type = false, l_ban_type = false;
+    switch (a_comp->decree_subtype) {
+    case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_STAKE_APPROVE:
+    case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_STAKE_INVALIDATE:
+        if (a_exist->decree_subtype != DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_STAKE_APPROVE &&
+                a_exist->decree_subtype != DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_STAKE_INVALIDATE)
+            return false;
+        l_stake_type = true;
+        break;
+    case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_BAN:
+    case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_UNBAN:
+        if (a_exist->decree_subtype != DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_BAN &&
+                a_exist->decree_subtype != DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_UNBAN)
+            return false;
+        l_ban_type = true;
+        break;
+    default:
+        return a_exist->decree_subtype == a_comp->decree_subtype;
+    }
+    dap_hash_fast_t l_exist_hash = {}, l_comp_hash = {};
+    dap_chain_datum_anchor_get_hash_from_data(a_comp->anchor, &l_comp_hash);
+    dap_chain_datum_anchor_get_hash_from_data(a_exist->anchor, &l_exist_hash);
+    dap_chain_datum_decree_t *l_comp_decree = dap_ledger_decree_get_by_hash(a_ledger->net, &l_comp_hash, NULL);
+    dap_chain_datum_decree_t *l_exist_decree = dap_ledger_decree_get_by_hash(a_ledger->net, &l_exist_hash, NULL);
+    if (l_ban_type) {
+
+    }
+    if (l_stake_type) {
+
+    }
+    return false;
 }
 
 
-int s_aggregate_anchor(dap_ledger_hardfork_anchors_t **a_out_list, uint16_t a_subtype, dap_chain_datum_anchor_t *a_anchor)
+int s_aggregate_anchor(dap_ledger_t *a_ledger, 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);
+    DL_FOREACH(*a_out_list, l_exist)
+        if (s_compare_anchors(a_ledger, l_exist, &l_new_anchor))
+            break;
     if (!l_exist) {
         l_exist = DAP_DUP(&l_new_anchor);
         if (!l_exist) {
@@ -676,7 +708,12 @@ dap_ledger_hardfork_anchors_t *dap_ledger_anchors_aggregate(dap_ledger_t *a_ledg
                                             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);
+            dap_hash_fast_t l_decree_hash;
+            if (dap_chain_datum_anchor_get_hash_from_data(l_anchor, &l_decree_hash)) {
+                log_it(L_ERROR, "Corrupted datum anchor %s, can't get decree hash from it", dap_hash_fast_to_str_static(&it->anchor_hash));
+                continue;
+            }
+            s_aggregate_anchor(a_ledger, &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 4bcf852f96..091191f75b 100644
--- a/modules/ledger/dap_chain_ledger_tx.c
+++ b/modules/ledger/dap_chain_ledger_tx.c
@@ -1175,7 +1175,7 @@ int s_compare_trackers(dap_list_t *a_tracker1, dap_list_t *a_tracker2)
     return memcmp(&l_tracker1->voting_hash, &l_tracker2->voting_hash, sizeof(dap_hash_fast_t));
 }
 
-dap_list_t *s_trackers_concatenate(dap_ledger_t *a_ledger, dap_list_t *a_trackers, dap_list_t *a_added, dap_time_t a_ts_creation_time)
+dap_list_t *s_trackers_aggregate(dap_ledger_t *a_ledger, dap_list_t *a_trackers, dap_list_t *a_added, dap_time_t a_ts_creation_time)
 {
     if (!s_voting_callbacks.voting_expire_callback)
         return a_trackers;
@@ -1363,7 +1363,7 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha
         // Gather colour information from previous outputs
         dap_ledger_tx_item_t *l_prev_item_out = l_bound_item->prev_item;
         l_prev_item_out->out_metadata[l_bound_item->prev_out_idx].tx_spent_hash_fast = *a_tx_hash;
-        l_trackers_mover = s_trackers_concatenate(a_ledger, l_trackers_mover,
+        l_trackers_mover = s_trackers_aggregate(a_ledger, l_trackers_mover,
                                                   l_prev_item_out->out_metadata[l_bound_item->prev_out_idx].trackers, a_tx->header.ts_created);
         // add a used output
         l_prev_item_out->cache_data.n_outs_used++;
@@ -1401,7 +1401,7 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha
         assert(l_vote_tx_item);
         l_new_tracker->voting_hash = l_vote_tx_item->voting_hash;
         dap_list_t *l_new_vote = dap_list_append(NULL, l_new_tracker);
-        l_trackers_mover = s_trackers_concatenate(a_ledger, l_trackers_mover, l_new_vote, a_tx->header.ts_created);
+        l_trackers_mover = s_trackers_aggregate(a_ledger, l_trackers_mover, l_new_vote, a_tx->header.ts_created);
     }
 
     //Update balance : raise
@@ -1951,7 +1951,30 @@ int dap_ledger_tx_load(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_c
     return dap_ledger_tx_add(a_ledger, a_tx, a_tx_hash, false, a_datum_index_data);
 }
 
+int dap_ledger_tx_load_hardfork_data(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_chain_hash_fast_t *a_tx_hash, dap_ledger_datum_iter_data_t *a_datum_index_data)
+{
+    dap_return_val_if_fail(a_ledger && a_tx && a_tx_hash, -1);
 
+    byte_t *l_item = NULL;
+    size_t l_tx_item_size = 0;
+    TX_ITEM_ITER_TX(l_item, l_tx_item_size, a_tx) {
+        if (*l_item != TX_ITEM_TYPE_TSD)
+            continue;
+        dap_tsd_t *l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t *)l_item)->tsd;
+        if (l_tsd->type != DAP_CHAIN_DATUM_TX_TSD_TYPE_TRACKER)
+            continue;
+        if (l_tsd->size != sizeof(dap_ledger_tracker_t))
+            return log_it(L_ERROR, "Incorrect size of TSD tracker section %u (need %zu)", l_tsd->size, sizeof(dap_ledger_tracker_t)), -2;
+        dap_ledger_tx_item_t *l_item_tx = NULL;
+        s_tx_find_by_hash(a_ledger, a_tx_hash, &l_item_tx, true);
+        if (!l_item_tx)
+            return log_it(L_ERROR, "Can't find hardfork tx %s in ledger", dap_hash_fast_to_str_static(a_tx_hash)), -3;
+        if (l_item_tx->cache_data.n_outs != 1)
+            return log_it(L_ERROR, "Can't add hardfork data to tx %s cause it's not a single-out tx", dap_hash_fast_to_str_static(a_tx_hash)), -4;
+        l_item_tx->out_metadata[0].trackers = dap_list_append(l_item_tx->out_metadata[0].trackers, l_tsd->data);
+    }
+    return 0;
+}
 
 static void s_ledger_stake_lock_cache_update(dap_ledger_t *a_ledger, dap_ledger_stake_lock_item_t *a_stake_lock_item)
 {
@@ -2423,7 +2446,7 @@ static int s_aggregate_out(dap_ledger_hardfork_balances_t **a_out_list, dap_ledg
                                     dap_chain_addr_to_str_static(a_addr), a_ticker, dap_uint256_to_char(a_value, NULL));
         return -2;
     }
-    l_exist->trackers = s_trackers_concatenate(a_ledger, l_exist->trackers, a_trackers, a_hardfork_start_time);
+    l_exist->trackers = s_trackers_aggregate(a_ledger, l_exist->trackers, a_trackers, a_hardfork_start_time);
     return 0;
 }
 
@@ -2438,7 +2461,7 @@ static int s_aggregate_out_cond(dap_ledger_hardfork_condouts_t **a_ret_list, dap
         return -1;
     }
     *l_new_condout = (dap_ledger_hardfork_condouts_t) { .hash = *a_tx_hash, .cond = a_out_cond, .sign = a_sign };
-    l_new_condout->trackers = s_trackers_concatenate(a_ledger, NULL, a_trackers, a_hardfork_start_time);
+    l_new_condout->trackers = s_trackers_aggregate(a_ledger, NULL, a_trackers, a_hardfork_start_time);
     DL_APPEND(*a_ret_list, l_new_condout);
     return 0;
 }
diff --git a/modules/ledger/include/dap_chain_ledger.h b/modules/ledger/include/dap_chain_ledger.h
index 525cb98454..f8b3084f25 100644
--- a/modules/ledger/include/dap_chain_ledger.h
+++ b/modules/ledger/include/dap_chain_ledger.h
@@ -40,7 +40,6 @@
 
 #define DAP_CHAIN_NET_SRV_TRANSFER_ID 0x07
 #define DAP_CHAIN_NET_SRV_BLOCK_REWARD_ID 0x08
-#define DAP_CHAIN_DATUM_TX_TSD_TYPE_TRACKER 0xf0fa
 
 typedef struct dap_ledger {
     dap_chain_net_t *net;
@@ -306,9 +305,9 @@ DAP_STATIC_INLINE char *dap_ledger_get_gdb_group(dap_ledger_t *a_ledger, const c
  * return 1 OK, -1 error
  */
 int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash, bool a_from_threshold, dap_ledger_datum_iter_data_t *a_datum_index_data);
+int dap_ledger_tx_load_hardfork_data(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_chain_hash_fast_t *a_tx_hash, dap_ledger_datum_iter_data_t *a_datum_index_data);
 int dap_ledger_tx_load(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_chain_hash_fast_t *a_tx_hash, dap_ledger_datum_iter_data_t *a_datum_index_data);
 int dap_ledger_tx_remove(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_hash_fast_t *a_tx_hash);
-
 int dap_ledger_tx_add_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, size_t a_datum_size, dap_hash_fast_t *a_datum_hash);
 
 /**
diff --git a/modules/net/dap_chain_node.c b/modules/net/dap_chain_node.c
index 9af1e7763c..e05c1cc9d8 100644
--- a/modules/net/dap_chain_node.c
+++ b/modules/net/dap_chain_node.c
@@ -75,18 +75,18 @@ typedef struct dap_chain_node_net_states_info {
 #define node_info_v1_shift ( sizeof(uint16_t) + 16 + sizeof(dap_chain_node_role_t) )
 
 enum hardfork_state {
-    STATE_BALANCES = 0,
+    STATE_ANCHORS = 0,
+    STATE_BALANCES,
     STATE_CONDOUTS,
-    STATE_ANCHORS,
     STATE_FEES,
     STATE_SERVICES
 };
 
 struct hardfork_states {
     enum hardfork_state state_current;
+    dap_ledger_hardfork_anchors_t  *anchors;
     dap_ledger_hardfork_balances_t *balances;
     dap_ledger_hardfork_condouts_t *condouts;
-    dap_ledger_hardfork_anchors_t  *anchors;
     dap_chain_cs_blocks_hardfork_fees_t *fees;
     dap_chain_srv_hardfork_state_t *service_states;
 };
diff --git a/modules/net/include/dap_chain_net.h b/modules/net/include/dap_chain_net.h
index 3c7ca76f7a..dc8a6c4b3c 100644
--- a/modules/net/include/dap_chain_net.h
+++ b/modules/net/include/dap_chain_net.h
@@ -183,6 +183,7 @@ void dap_chain_net_srv_order_add_notify_callback(dap_chain_net_t *a_net, dap_sto
 dap_list_t *dap_chain_datum_list(dap_chain_net_t *a_net, dap_chain_t *a_chain, dap_chain_datum_filter_func_t *a_filter_func, void *a_filter_func_param);
 
 int dap_chain_datum_add(dap_chain_t * a_chain, dap_chain_datum_t *a_datum, size_t a_datum_size, dap_hash_fast_t *a_datum_hash, void *a_datum_index_data);
+int dap_chain_datum_add_hardfork_data(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_datum_size, dap_hash_fast_t *a_datum_hash, void *a_datum_index_data);
 int dap_chain_datum_remove(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_datum_size, dap_hash_fast_t *a_datum_hash);
 
 bool dap_chain_net_get_load_mode(dap_chain_net_t * a_net);
-- 
GitLab


From 68368d7c704c94742b816a25bb273ba8321a206c Mon Sep 17 00:00:00 2001
From: "roman.khlopkov" <roman.khlopkov@demlabs.net>
Date: Mon, 30 Dec 2024 16:08:49 +0300
Subject: [PATCH 5/9] [*] Condouts placing in parachain

---
 .../datum/include/dap_chain_datum_tx_tsd.h    |  4 +-
 modules/ledger/dap_chain_ledger_decree.c      | 33 ++++++--
 modules/ledger/dap_chain_ledger_tx.c          | 14 ++--
 modules/ledger/include/dap_chain_ledger.h     |  3 +-
 modules/net/dap_chain_node.c                  | 83 ++++++++++++++++++-
 modules/type/blocks/dap_chain_cs_blocks.c     |  4 +-
 .../type/blocks/include/dap_chain_cs_blocks.h |  3 +-
 7 files changed, 120 insertions(+), 24 deletions(-)

diff --git a/modules/datum/include/dap_chain_datum_tx_tsd.h b/modules/datum/include/dap_chain_datum_tx_tsd.h
index 7f3cfa7bdc..d63d96d244 100644
--- a/modules/datum/include/dap_chain_datum_tx_tsd.h
+++ b/modules/datum/include/dap_chain_datum_tx_tsd.h
@@ -4,7 +4,9 @@
 #include "dap_chain_common.h"
 #include "dap_tsd.h"
 
-#define DAP_CHAIN_DATUM_TX_TSD_TYPE_TRACKER 0xf0fa
+#define DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TICKER         0xf001
+#define DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TX_HASH        0xf002
+#define DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TRACKER        0xf0fa
 
 typedef struct dap_chain_tx_tsd {
     struct {
diff --git a/modules/ledger/dap_chain_ledger_decree.c b/modules/ledger/dap_chain_ledger_decree.c
index 2148b36fbe..482a8ee00b 100644
--- a/modules/ledger/dap_chain_ledger_decree.c
+++ b/modules/ledger/dap_chain_ledger_decree.c
@@ -678,20 +678,30 @@ static bool s_compare_anchors(dap_ledger_t *a_ledger, dap_ledger_hardfork_anchor
     dap_chain_datum_decree_t *l_comp_decree = dap_ledger_decree_get_by_hash(a_ledger->net, &l_comp_hash, NULL);
     dap_chain_datum_decree_t *l_exist_decree = dap_ledger_decree_get_by_hash(a_ledger->net, &l_exist_hash, NULL);
     if (l_ban_type) {
-
+        const char *l_comp_ban_addr = NULL, *l_exist_ban_addr = NULL;
+        dap_chain_datum_decree_get_ban_addr(l_comp_decree, &l_comp_ban_addr);
+        dap_chain_datum_decree_get_ban_addr(l_exist_decree, &l_exist_ban_addr);
+        if (!dap_strcmp(l_comp_ban_addr, l_exist_ban_addr))
+            return true;
+        return false;
     }
     if (l_stake_type) {
-
+        dap_chain_addr_t l_comp_addr = {}, l_exist_addr = {};
+        dap_chain_datum_decree_get_stake_signing_addr(l_comp_decree, &l_comp_addr);
+        dap_chain_datum_decree_get_stake_signing_addr(l_exist_decree, &l_exist_addr);
+        if (!dap_chain_addr_is_blank(&l_comp_addr) && dap_chain_addr_compare(&l_comp_addr, &l_exist_addr))
+            return true;
+        return false;
     }
-    return false;
+    return assert(false), false;
 }
 
 
 int s_aggregate_anchor(dap_ledger_t *a_ledger, 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_FOREACH(*a_out_list, l_exist)
+    dap_ledger_hardfork_anchors_t *l_exist = NULL, *l_tmp;
+    DL_FOREACH_SAFE(*a_out_list, l_exist, l_tmp)
         if (s_compare_anchors(a_ledger, l_exist, &l_new_anchor))
             break;
     if (!l_exist) {
@@ -700,9 +710,16 @@ int s_aggregate_anchor(dap_ledger_t *a_ledger, dap_ledger_hardfork_anchors_t **a
             log_it(L_CRITICAL, "%s", c_error_memory_alloc);
             return -1;
         }
-        DL_APPEND(*a_out_list, l_exist);
-    } else
-        l_exist->anchor = a_anchor;
+        if (a_subtype != DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_STAKE_INVALIDATE && a_subtype != DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_UNBAN)
+            DL_APPEND(*a_out_list, l_exist);
+    } else {
+        if (l_exist->decree_subtype == DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_STAKE_APPROVE ||
+                l_exist->decree_subtype == DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_BAN) {
+            assert(a_subtype == DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_STAKE_INVALIDATE || a_subtype == DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_UNBAN);
+            DL_DELETE(*a_out_list, l_exist);
+        } else
+            l_exist->anchor = a_anchor;
+    }
     return 0;
 }
 
diff --git a/modules/ledger/dap_chain_ledger_tx.c b/modules/ledger/dap_chain_ledger_tx.c
index 091191f75b..5a199adf42 100644
--- a/modules/ledger/dap_chain_ledger_tx.c
+++ b/modules/ledger/dap_chain_ledger_tx.c
@@ -1961,7 +1961,7 @@ int dap_ledger_tx_load_hardfork_data(dap_ledger_t *a_ledger, dap_chain_datum_tx_
         if (*l_item != TX_ITEM_TYPE_TSD)
             continue;
         dap_tsd_t *l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t *)l_item)->tsd;
-        if (l_tsd->type != DAP_CHAIN_DATUM_TX_TSD_TYPE_TRACKER)
+        if (l_tsd->type != DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TRACKER)
             continue;
         if (l_tsd->size != sizeof(dap_ledger_tracker_t))
             return log_it(L_ERROR, "Incorrect size of TSD tracker section %u (need %zu)", l_tsd->size, sizeof(dap_ledger_tracker_t)), -2;
@@ -2451,16 +2451,16 @@ static int s_aggregate_out(dap_ledger_hardfork_balances_t **a_out_list, dap_ledg
 }
 
 static int s_aggregate_out_cond(dap_ledger_hardfork_condouts_t **a_ret_list, dap_ledger_t *a_ledger,
-                                dap_chain_tx_out_cond_t *a_out_cond, dap_sign_t *a_sign,
-                                dap_hash_fast_t *a_tx_hash, dap_time_t a_hardfork_start_time,
-                                dap_list_t *a_trackers)
+                                dap_chain_tx_out_cond_t *a_out_cond, dap_chain_tx_sig_t *a_sign,
+                                dap_hash_fast_t *a_tx_hash, const char *a_token_ticker,
+                                dap_time_t a_hardfork_start_time, dap_list_t *a_trackers)
 {
     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) { .hash = *a_tx_hash, .cond = a_out_cond, .sign = a_sign };
+    *l_new_condout = (dap_ledger_hardfork_condouts_t) { .hash = *a_tx_hash, .cond = a_out_cond, .sign = a_sign, .ticker = a_token_ticker };
     l_new_condout->trackers = s_trackers_aggregate(a_ledger, NULL, a_trackers, a_hardfork_start_time);
     DL_APPEND(*a_ret_list, l_new_condout);
     return 0;
@@ -2512,12 +2512,12 @@ dap_ledger_hardfork_balances_t *dap_ledger_states_aggregate(dap_ledger_t *a_ledg
                     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);
+                dap_chain_tx_sig_t *l_tx_sign = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get_nth(l_tx, TX_ITEM_TYPE_SIG, 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, a_ledger, l_out, l_tx_sign, &it->tx_hash_fast, a_hardfork_decree_creation_time, l_trackers);
+                s_aggregate_out_cond(&l_cond_ret, a_ledger, l_out, l_tx_sign, &it->tx_hash_fast, it->cache_data.token_ticker, a_hardfork_decree_creation_time, l_trackers);
             }
             default:
                 log_it(L_ERROR, "Unexpected item type %hhu", l_tx_item_type);
diff --git a/modules/ledger/include/dap_chain_ledger.h b/modules/ledger/include/dap_chain_ledger.h
index ca42264366..907d180b8b 100644
--- a/modules/ledger/include/dap_chain_ledger.h
+++ b/modules/ledger/include/dap_chain_ledger.h
@@ -63,7 +63,8 @@ typedef struct dap_ledger_hardfork_balances {
 typedef struct dap_ledger_hardfork_condouts {
     dap_hash_fast_t hash;
     dap_chain_tx_out_cond_t *cond;
-    dap_sign_t *sign;
+    dap_chain_tx_sig_t *sign;
+    const char *ticker;
     dap_list_t *trackers;
     struct dap_ledger_hardfork_condouts *prev, *next;
 } dap_ledger_hardfork_condouts_t;
diff --git a/modules/net/dap_chain_node.c b/modules/net/dap_chain_node.c
index e05c1cc9d8..3ff3f29277 100644
--- a/modules/net/dap_chain_node.c
+++ b/modules/net/dap_chain_node.c
@@ -550,7 +550,55 @@ dap_chain_datum_t *s_datum_tx_create(dap_chain_addr_t *a_addr, const char *a_tic
     }
     for (dap_list_t *it = a_trackers; it; it = it->next) {
         dap_ledger_tracker_t *l_tracker = it->data;
-        dap_chain_tx_tsd_t *l_tracker_tsd = dap_chain_datum_tx_item_tsd_create(l_tracker, DAP_CHAIN_DATUM_TX_TSD_TYPE_TRACKER, sizeof(dap_ledger_tracker_t));
+        dap_chain_tx_tsd_t *l_tracker_tsd = dap_chain_datum_tx_item_tsd_create(l_tracker, DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TRACKER, sizeof(dap_ledger_tracker_t));
+        if (!l_tracker_tsd) {
+            dap_chain_datum_tx_delete(l_tx);
+            return NULL;
+        }
+        if (dap_chain_datum_tx_add_item(&l_tx, l_tracker_tsd) != 1) {
+            dap_chain_datum_tx_delete(l_tx);
+            return NULL;
+        }
+    }
+    dap_chain_datum_t *l_datum_tx = dap_chain_datum_create(DAP_CHAIN_DATUM_TX, l_tx, dap_chain_datum_tx_get_size(l_tx));
+    dap_chain_datum_tx_delete(l_tx);
+    return l_datum_tx;
+}
+
+dap_chain_datum_t *s_cond_tx_create(dap_chain_tx_out_cond_t *a_cond, dap_chain_tx_sig_t *a_sign, dap_hash_fast_t *a_hash, const char *a_ticker, dap_list_t *a_trackers)
+{
+    dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create();
+    if (!l_tx)
+        return NULL;
+    if (dap_chain_datum_tx_add_item(&l_tx, a_cond) != 1) {
+        dap_chain_datum_tx_delete(l_tx);
+        return NULL;
+    }
+    if (dap_chain_datum_tx_add_item(&l_tx, a_sign) != 1) {
+        dap_chain_datum_tx_delete(l_tx);
+        return NULL;
+    }
+    dap_chain_tx_tsd_t *l_tx_hash_tsd = dap_chain_datum_tx_item_tsd_create(a_hash, DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TX_HASH, sizeof(dap_hash_fast_t));
+    if (!l_tx_hash_tsd) {
+        dap_chain_datum_tx_delete(l_tx);
+        return NULL;
+    }
+    if (dap_chain_datum_tx_add_item(&l_tx, l_tx_hash_tsd) != 1) {
+        dap_chain_datum_tx_delete(l_tx);
+        return NULL;
+    }
+    dap_chain_tx_tsd_t *l_ticker_tsd = dap_chain_datum_tx_item_tsd_create(a_hash, DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TICKER, DAP_CHAIN_TICKER_SIZE_MAX);
+    if (!l_ticker_tsd) {
+        dap_chain_datum_tx_delete(l_tx);
+        return NULL;
+    }
+    if (dap_chain_datum_tx_add_item(&l_tx, l_ticker_tsd) != 1) {
+        dap_chain_datum_tx_delete(l_tx);
+        return NULL;
+    }
+    for (dap_list_t *it = a_trackers; it; it = it->next) {
+        dap_ledger_tracker_t *l_tracker = it->data;
+        dap_chain_tx_tsd_t *l_tracker_tsd = dap_chain_datum_tx_item_tsd_create(l_tracker, DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TRACKER, sizeof(dap_ledger_tracker_t));
         if (!l_tracker_tsd) {
             dap_chain_datum_tx_delete(l_tx);
             return NULL;
@@ -572,22 +620,51 @@ int dap_chain_node_hardfork_process(dap_chain_t *a_chain)
         return log_it(L_ERROR, "Can't process chain with no harfork data. Use dap_chain_node_hardfork_prepare() for collect it first"), -2;
     struct hardfork_states *l_states = a_chain->hardfork_data;
     switch (l_states->state_current) {
+    case STATE_ANCHORS:
+        for (dap_ledger_hardfork_anchors_t *it = l_states->anchors; it; it = it->next) {
+            dap_chain_datum_t *l_datum_anchor = dap_chain_datum_create(DAP_CHAIN_DATUM_TX, it->anchor, dap_chain_datum_anchor_get_size(it->anchor));
+            if (!l_datum_anchor)
+                return -2;
+            if (!a_chain->callback_add_datums(a_chain, &l_datum_anchor, 1)) {
+                dap_hash_fast_t l_decree_hash;
+                dap_chain_datum_anchor_get_hash_from_data(it->anchor, &l_decree_hash);
+                log_it(L_NOTICE, "Hardfork processed to datum anchor for decree hash %s", dap_hash_fast_to_str_static(&l_decree_hash));
+                DAP_DELETE(l_datum_anchor);
+                break;
+            }
+            DAP_DELETE(l_datum_anchor);
+        }
+        break;
     case STATE_BALANCES:
         for (dap_ledger_hardfork_balances_t *it = l_states->balances; it; it = it->next) {
             dap_chain_datum_t *l_tx = s_datum_tx_create(&it->addr, it->ticker, it->value, it->trackers);
             if (!l_tx)
                 return -3;
             if (!a_chain->callback_add_datums(a_chain, &l_tx, 1)) {
+                DAP_DELETE(l_tx);
                 log_it(L_NOTICE, "Hardfork processed to datum tx with addr %s", dap_chain_addr_to_str_static(&it->addr));
                 break;
             }
+            DAP_DELETE(l_tx);
         }
         break;
     case STATE_CONDOUTS:
-        break;
-    case STATE_ANCHORS:
+        for (dap_ledger_hardfork_condouts_t *it = l_states->condouts; it; it = it->next) {
+            dap_chain_datum_t *l_cond_tx = s_cond_tx_create(it->cond, it->sign, &it->hash, it->ticker, it->trackers);
+            if (!l_cond_tx)
+                return -4;
+            if (!a_chain->callback_add_datums(a_chain, &l_cond_tx, 1)) {
+                DAP_DELETE(l_cond_tx);
+                log_it(L_NOTICE, "Hardfork processed to datum cond_tx with hash %s", dap_hash_fast_to_str_static(&it->hash));
+                break;
+            }
+            DAP_DELETE(l_cond_tx);
+        }
         break;
     case STATE_FEES:
+        for (dap_chain_cs_blocks_hardfork_fees_t *it = l_states->fees; it; it = it->next) {
+
+        }
         break;
     case STATE_SERVICES:
         break;
diff --git a/modules/type/blocks/dap_chain_cs_blocks.c b/modules/type/blocks/dap_chain_cs_blocks.c
index 5a98a345b0..25ab796329 100644
--- a/modules/type/blocks/dap_chain_cs_blocks.c
+++ b/modules/type/blocks/dap_chain_cs_blocks.c
@@ -2770,12 +2770,12 @@ static int s_aggregate_fees(dap_chain_cs_blocks_hardfork_fees_t **a_out_list, da
     }
     switch (a_type) {
     case DAP_CHAIN_BLOCK_COLLECT_FEES:
-        if (SUM_256_256(l_exist->fees_sum, a_value, &l_exist->fees_sum)) {
+        if (SUM_256_256(l_exist->fees_n_rewards_sum, a_value, &l_exist->fees_n_rewards_sum)) {
             log_it(L_ERROR, "Integer overflow of hardfork aggregated data for not withdrowed fees");
             return -2;
         } break;
     case DAP_CHAIN_BLOCK_COLLECT_REWARDS:
-        if (SUM_256_256(l_exist->rewards_sum, a_value, &l_exist->rewards_sum)) {
+        if (SUM_256_256(l_exist->fees_n_rewards_sum, a_value, &l_exist->fees_n_rewards_sum)) {
             log_it(L_ERROR, "Integer overflow of hardfork aggregated data for not withdrowed rewards");
             return -2;
         } break;
diff --git a/modules/type/blocks/include/dap_chain_cs_blocks.h b/modules/type/blocks/include/dap_chain_cs_blocks.h
index 00c063cbe2..511f18204f 100644
--- a/modules/type/blocks/include/dap_chain_cs_blocks.h
+++ b/modules/type/blocks/include/dap_chain_cs_blocks.h
@@ -81,8 +81,7 @@ typedef enum s_com_blocks_err{
 
 typedef struct dap_chain_cs_blocks_hardfork_fees {
     dap_sign_t *owner_sign;
-    uint256_t fees_sum;
-    uint256_t rewards_sum;
+    uint256_t fees_n_rewards_sum;
     struct dap_chain_cs_blocks_hardfork_fees *prev, *next;
 } dap_chain_cs_blocks_hardfork_fees_t;
 
-- 
GitLab


From 6e3593b1894736572a3aebad3e862ea1063b1df7 Mon Sep 17 00:00:00 2001
From: "roman.khlopkov" <roman.khlopkov@demlabs.net>
Date: Tue, 31 Dec 2024 18:32:53 +0300
Subject: [PATCH 6/9] [*] Composing CTs for reward & fees aggregates

---
 modules/datum/dap_chain_datum_tx_items.c      | 36 ++++++++++++++-----
 .../datum/include/dap_chain_datum_tx_items.h  | 16 ++++++++-
 .../include/dap_chain_datum_tx_out_cond.h     |  1 +
 modules/net/dap_chain_node.c                  | 33 +++++++++++++++--
 4 files changed, 75 insertions(+), 11 deletions(-)

diff --git a/modules/datum/dap_chain_datum_tx_items.c b/modules/datum/dap_chain_datum_tx_items.c
index b3c066ad7f..67c32bbaec 100644
--- a/modules/datum/dap_chain_datum_tx_items.c
+++ b/modules/datum/dap_chain_datum_tx_items.c
@@ -191,7 +191,8 @@ dap_chain_tx_in_reward_t *dap_chain_datum_tx_item_in_reward_create(dap_chain_has
 /**
  * Create tsd section
  */
-dap_chain_tx_tsd_t *dap_chain_datum_tx_item_tsd_create(void *a_data, int a_type, size_t a_size) {
+dap_chain_tx_tsd_t *dap_chain_datum_tx_item_tsd_create(const void *a_data, int a_type, size_t a_size)
+{
     if (!a_data || !a_size) {
         return NULL;
     }
@@ -279,6 +280,16 @@ dap_chain_tx_out_cond_t *dap_chain_datum_tx_item_out_cond_create_fee(uint256_t a
     return l_item;
 }
 
+dap_chain_tx_out_cond_t *dap_chain_datum_tx_item_out_cond_create_fee_stack(uint256_t a_value)
+{
+    dap_return_val_if_pass(IS_ZERO_256(a_value), NULL);
+    dap_chain_tx_out_cond_t *l_item = DAP_NEW_Z_RET_VAL_IF_FAIL(dap_chain_tx_out_cond_t, NULL, NULL);
+    l_item->header.item_type = TX_ITEM_TYPE_OUT_COND;
+    l_item->header.value = a_value;
+    l_item->header.subtype = DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE_STACK;
+    return l_item;
+}
+
 /**
  * Create item dap_chain_tx_out_cond_t
  *
@@ -426,6 +437,19 @@ dap_chain_tx_out_cond_t *dap_chain_datum_tx_item_out_cond_create_srv_emit_delega
     return l_item;
 }
 
+dap_chain_tx_sig_t *dap_chain_tx_sig_create(dap_sign_t *a_sign)
+{
+    dap_return_val_if_fail(a_sign, NULL);
+    size_t l_chain_sign_size = dap_sign_get_size(a_sign); // sign data
+    dap_chain_tx_sig_t *l_tx_sig = DAP_NEW_Z_SIZE_RET_VAL_IF_FAIL(dap_chain_tx_sig_t, sizeof(dap_chain_tx_sig_t)
+                                                                                      + l_chain_sign_size, NULL, NULL);
+    l_tx_sig->header.type = TX_ITEM_TYPE_SIG;
+    l_tx_sig->header.version = 1;
+    l_tx_sig->header.sig_size = (uint32_t)l_chain_sign_size;
+    memcpy(l_tx_sig->sig, a_sign, l_chain_sign_size);
+    return l_tx_sig;
+}
+
 /**
  * Create item dap_chain_tx_sig_t
  *
@@ -445,13 +469,9 @@ dap_chain_tx_sig_t *dap_chain_datum_tx_item_sign_create(dap_enc_key_t *a_key, da
     DAP_DELETE(l_tx);
     if (!l_chain_sign)
         return NULL;
-    size_t l_chain_sign_size = dap_sign_get_size(l_chain_sign); // sign data
-    dap_chain_tx_sig_t *l_tx_sig = DAP_NEW_Z_SIZE(dap_chain_tx_sig_t,
-            sizeof(dap_chain_tx_sig_t) + l_chain_sign_size);
-    l_tx_sig->header.type = TX_ITEM_TYPE_SIG;
-    l_tx_sig->header.version = 1;
-    l_tx_sig->header.sig_size = (uint32_t)l_chain_sign_size;
-    memcpy(l_tx_sig->sig, l_chain_sign, l_chain_sign_size);
+    dap_chain_tx_sig_t *l_tx_sig = dap_chain_tx_sig_create(l_chain_sign);
+    if (!l_tx_sig)
+        return NULL;
     DAP_DELETE(l_chain_sign);
     return l_tx_sig;
 }
diff --git a/modules/datum/include/dap_chain_datum_tx_items.h b/modules/datum/include/dap_chain_datum_tx_items.h
index c8be707da1..397a10cdc2 100644
--- a/modules/datum/include/dap_chain_datum_tx_items.h
+++ b/modules/datum/include/dap_chain_datum_tx_items.h
@@ -109,7 +109,7 @@ dap_chain_tx_in_t* dap_chain_datum_tx_item_in_create(dap_chain_hash_fast_t *a_tx
 
 dap_chain_tx_in_reward_t *dap_chain_datum_tx_item_in_reward_create(dap_chain_hash_fast_t *a_block_hash);
 
-dap_chain_tx_tsd_t *dap_chain_datum_tx_item_tsd_create(void *a_data, int a_type, size_t a_size);
+dap_chain_tx_tsd_t *dap_chain_datum_tx_item_tsd_create(const void *a_data, int a_type, size_t a_size);
 
 dap_chain_tx_in_cond_t* dap_chain_datum_tx_item_in_cond_create(dap_chain_hash_fast_t *a_tx_prev_hash, uint32_t a_tx_out_prev_idx,
                                                                uint32_t a_receipt_idx);
@@ -135,6 +135,13 @@ dap_chain_tx_out_ext_t* dap_chain_datum_tx_item_out_ext_create(const dap_chain_a
  */
 dap_chain_tx_out_cond_t *dap_chain_datum_tx_item_out_cond_create_fee(uint256_t a_value);
 
+/**
+ * Create item dap_chain_tx_out_cond_t with fee_stack subtype
+ *
+ * return item, NULL Error
+ */
+dap_chain_tx_out_cond_t *dap_chain_datum_tx_item_out_cond_create_fee_stack(uint256_t a_value);
+
 /**
  * Create item dap_chain_tx_out_cond_t
  *
@@ -178,6 +185,13 @@ dap_chain_tx_out_cond_t *dap_chain_datum_tx_item_out_cond_create_srv_emit_delega
                                                                                    uint32_t a_signs_min, dap_hash_fast_t *a_pkey_hashes,
                                                                                    size_t a_pkey_hashes_count);
 
+/**
+ * Create item dap_chain_tx_sig_t
+ *
+ * return item, NULL Error
+ */
+dap_chain_tx_sig_t *dap_chain_tx_sig_create(dap_sign_t *a_sign);
+
 /**
  * Create item dap_chain_tx_sig_t
  *
diff --git a/modules/datum/include/dap_chain_datum_tx_out_cond.h b/modules/datum/include/dap_chain_datum_tx_out_cond.h
index c765295b49..e743afd559 100644
--- a/modules/datum/include/dap_chain_datum_tx_out_cond.h
+++ b/modules/datum/include/dap_chain_datum_tx_out_cond.h
@@ -37,6 +37,7 @@ enum dap_chain_tx_out_cond_subtype {
     DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE = 0x04,
     DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK = 0x06,
     DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_EMIT_DELEGATE = 0x07,
+    DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE_STACK = 0x08,
     DAP_CHAIN_TX_OUT_COND_SUBTYPE_ALL = 0xFF
 };
 typedef byte_t dap_chain_tx_out_cond_subtype_t;
diff --git a/modules/net/dap_chain_node.c b/modules/net/dap_chain_node.c
index 3ff3f29277..fb354d6d68 100644
--- a/modules/net/dap_chain_node.c
+++ b/modules/net/dap_chain_node.c
@@ -587,7 +587,7 @@ dap_chain_datum_t *s_cond_tx_create(dap_chain_tx_out_cond_t *a_cond, dap_chain_t
         dap_chain_datum_tx_delete(l_tx);
         return NULL;
     }
-    dap_chain_tx_tsd_t *l_ticker_tsd = dap_chain_datum_tx_item_tsd_create(a_hash, DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TICKER, DAP_CHAIN_TICKER_SIZE_MAX);
+    dap_chain_tx_tsd_t *l_ticker_tsd = dap_chain_datum_tx_item_tsd_create(a_ticker, DAP_CHAIN_DATUM_TX_TSD_TYPE_HARDFORK_TICKER, DAP_CHAIN_TICKER_SIZE_MAX);
     if (!l_ticker_tsd) {
         dap_chain_datum_tx_delete(l_tx);
         return NULL;
@@ -613,6 +613,26 @@ dap_chain_datum_t *s_cond_tx_create(dap_chain_tx_out_cond_t *a_cond, dap_chain_t
     return l_datum_tx;
 }
 
+dap_chain_datum_t *s_fee_tx_create(uint256_t a_value, dap_sign_t *a_owner_sign)
+{
+    dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create();
+    if (!l_tx)
+        return NULL;
+    dap_chain_tx_out_cond_t *l_cond = dap_chain_datum_tx_item_out_cond_create_fee_stack(a_value);
+    if (dap_chain_datum_tx_add_item(&l_tx, l_cond) != 1) {
+        dap_chain_datum_tx_delete(l_tx);
+        return NULL;
+    }
+    dap_chain_tx_sig_t *l_tx_sig = dap_chain_tx_sig_create(a_owner_sign);
+    if (dap_chain_datum_tx_add_item(&l_tx, l_tx_sig) != 1) {
+        dap_chain_datum_tx_delete(l_tx);
+        return NULL;
+    }
+    dap_chain_datum_t *l_datum_tx = dap_chain_datum_create(DAP_CHAIN_DATUM_TX, l_tx, dap_chain_datum_tx_get_size(l_tx));
+    dap_chain_datum_tx_delete(l_tx);
+    return l_datum_tx;
+}
+
 int dap_chain_node_hardfork_process(dap_chain_t *a_chain)
 {
     dap_return_val_if_fail(a_chain, -1);
@@ -663,7 +683,16 @@ int dap_chain_node_hardfork_process(dap_chain_t *a_chain)
         break;
     case STATE_FEES:
         for (dap_chain_cs_blocks_hardfork_fees_t *it = l_states->fees; it; it = it->next) {
-
+            dap_chain_datum_t *l_fee_tx = s_fee_tx_create(it->fees_n_rewards_sum, it->owner_sign);
+            if (!l_fee_tx)
+                return -4;
+            if (!a_chain->callback_add_datums(a_chain, &l_fee_tx, 1)) {
+                DAP_DELETE(l_fee_tx);
+                dap_hash_fast_t l_pkey_hash; dap_sign_get_pkey_hash(it->owner_sign, &l_pkey_hash);
+                log_it(L_NOTICE, "Hardfork processed to datum fee_tx with hash %s", dap_hash_fast_to_str_static(&l_pkey_hash));
+                break;
+            }
+            DAP_DELETE(l_fee_tx);
         }
         break;
     case STATE_SERVICES:
-- 
GitLab


From d98111a35660c18a30de9bfbacef997e51e4cc51 Mon Sep 17 00:00:00 2001
From: "roman.khlopkov" <roman.khlopkov@demlabs.net>
Date: Wed, 8 Jan 2025 13:57:38 +0300
Subject: [PATCH 7/9] [+] Service states composition

---
 modules/net/dap_chain_node.c | 107 +++++++++++++++++++++++++++++------
 1 file changed, 90 insertions(+), 17 deletions(-)

diff --git a/modules/net/dap_chain_node.c b/modules/net/dap_chain_node.c
index fb354d6d68..4a7e1c8c5c 100644
--- a/modules/net/dap_chain_node.c
+++ b/modules/net/dap_chain_node.c
@@ -79,11 +79,14 @@ enum hardfork_state {
     STATE_BALANCES,
     STATE_CONDOUTS,
     STATE_FEES,
-    STATE_SERVICES
+    STATE_SERVICES,
+    STATE_MEMPOOL,
+    STATE_OVER
 };
 
 struct hardfork_states {
     enum hardfork_state state_current;
+    size_t iterator;
     dap_ledger_hardfork_anchors_t  *anchors;
     dap_ledger_hardfork_balances_t *balances;
     dap_ledger_hardfork_condouts_t *condouts;
@@ -498,6 +501,34 @@ void dap_chain_node_mempool_process_all(dap_chain_t *a_chain, bool a_force)
     DAP_DELETE(l_gdb_group_mempool);
 }
 
+dap_chain_datum_t **s_service_state_datums_create(dap_chain_srv_hardfork_state_t *a_state, size_t *a_datums_count)
+{
+    dap_chain_datum_t **ret = NULL;
+    size_t l_datums_count = 0;
+    const uint64_t l_max_step_size = DAP_CHAIN_ATOM_MAX_SIZE - sizeof(dap_chain_datum_service_state_t);
+    uint64_t l_step_size = dap_min(l_max_step_size, a_state->size);
+    byte_t *l_offset = a_state->data, *l_ptr = l_offset, *l_end = a_state->data + a_state->size * a_state->count;
+    while (l_offset < l_end) {
+        size_t l_cur_step_size = 0, i = 0;
+        while (l_cur_step_size < l_max_step_size && l_offset < l_end) {
+            size_t l_addition = dap_min((uint64_t)(l_end - l_offset), l_step_size);
+            l_cur_step_size += l_addition;
+            l_offset += l_addition;
+            i++;
+        }
+        dap_chain_datum_t *l_datum = dap_chain_datum_create(DAP_CHAIN_DATUM_SERVICE_STATE, l_ptr, sizeof(dap_chain_datum_service_state_t) + l_cur_step_size);
+        ((dap_chain_datum_service_state_t *)l_datum->data)->srv_uid = a_state->uid;
+        ((dap_chain_datum_service_state_t *)l_datum->data)->states_count = i;
+        ret = DAP_REALLOC_RET_VAL_IF_FAIL(ret, sizeof(dap_chain_datum_t *) * (++l_datums_count), NULL, NULL);
+        ret[l_datums_count - 1] = l_datum;
+        l_ptr = l_offset;
+    }
+    assert(l_offset == l_end);
+    if (a_datums_count)
+        *a_datums_count = l_datums_count;
+    return ret;
+}
+
 int dap_chain_node_hardfork_prepare(dap_chain_t *a_chain, dap_time_t a_last_block_timestamp)
 {
     if (dap_strcmp(dap_chain_get_cs_type(a_chain), DAP_CHAIN_ESBOCS_CS_TYPE_STR))
@@ -514,22 +545,10 @@ int dap_chain_node_hardfork_prepare(dap_chain_t *a_chain, dap_time_t a_last_bloc
     DL_FOREACH_SAFE(l_states->service_states, it, tmp) {
         if (it->uid.uint64 < (uint64_t)INT64_MIN)       // MSB is not set
             continue;
-        const uint64_t l_max_step_size = DAP_CHAIN_ATOM_MAX_SIZE - sizeof(dap_chain_datum_service_state_t);
-        uint64_t l_step_size = dap_min(l_max_step_size, it->size);
-        byte_t *l_offset = it->data, *l_end = it->data + it->size * it->count;
-        while (l_offset < l_end) {
-            size_t l_cur_step_size = 0, i = 0;
-            while (l_cur_step_size < l_max_step_size && l_offset < l_end) {
-                size_t l_addition = dap_min((uint64_t)(l_end - l_offset), l_step_size);
-                l_cur_step_size += l_addition;
-                l_offset += l_addition;
-                i++;
-            }
-            dap_chain_datum_t *l_datum = dap_chain_datum_create(DAP_CHAIN_DATUM_SERVICE_STATE, NULL, sizeof(dap_chain_datum_service_state_t) + l_cur_step_size);
-            ((dap_chain_datum_service_state_t *)l_datum->data)->srv_uid = it->uid;
-            ((dap_chain_datum_service_state_t *)l_datum->data)->states_count = i;
-            DAP_DELETE(dap_chain_mempool_datum_add(l_datum, a_chain, "hex"));
-        }
+        size_t l_datums_count = 0;
+        dap_chain_datum_t **l_datums = s_service_state_datums_create(it, &l_datums_count);
+        for (size_t i = 0; i < l_datums_count; i++)
+            DAP_DELETE(dap_chain_mempool_datum_add(l_datums[i], a_chain, "hex"));
         DL_DELETE(l_states->service_states, it);
         DAP_DELETE(it);
     }
@@ -636,6 +655,8 @@ dap_chain_datum_t *s_fee_tx_create(uint256_t a_value, dap_sign_t *a_owner_sign)
 int dap_chain_node_hardfork_process(dap_chain_t *a_chain)
 {
     dap_return_val_if_fail(a_chain, -1);
+    if (!dap_chain_net_by_id(a_chain->net_id)->pub.mempool_autoproc)
+        return -2;
     if (!a_chain->hardfork_data)
         return log_it(L_ERROR, "Can't process chain with no harfork data. Use dap_chain_node_hardfork_prepare() for collect it first"), -2;
     struct hardfork_states *l_states = a_chain->hardfork_data;
@@ -696,6 +717,58 @@ int dap_chain_node_hardfork_process(dap_chain_t *a_chain)
         }
         break;
     case STATE_SERVICES:
+        for (dap_chain_srv_hardfork_state_t *it = l_states->service_states; it; it = it->next) {
+            if (it->uid.uint64 >= (uint64_t)INT64_MIN)       // MSB is set
+                continue;
+            bool l_break = false;
+            size_t l_datums_count = 0;
+            dap_chain_datum_t **l_datums = s_service_state_datums_create(it, &l_datums_count);
+            for (size_t i = l_states->iterator; i < l_datums_count; i++) {
+                if (!a_chain->callback_add_datums(a_chain, l_datums + i, 1)) {
+                    log_it(L_NOTICE, "Hardfork processed to datum service_state with uid %" DAP_UINT64_FORMAT_x " and number %zu",
+                                        it->uid.uint64, i);
+                    // save iterator to state machine
+                    l_states->iterator = i;
+                    l_break = true;
+                    break;
+                }
+
+            }
+            for (size_t i = 0; i < l_datums_count; i++)
+                DAP_DELETE(l_datums[i]);
+            DAP_DEL_Z(l_datums);
+            if (l_break)
+                break;
+        }
+        break;
+    case STATE_MEMPOOL: {
+        char *l_gdb_group_mempool = dap_chain_mempool_group_new(a_chain);
+        size_t l_objs_size = 0;
+        dap_global_db_obj_t *l_objs = dap_global_db_get_all_sync(l_gdb_group_mempool, &l_objs_size);
+        if (!l_objs_size) {
+            l_states->state_current = STATE_OVER;
+            break;
+        }
+        bool l_nothing_processed = true;
+        for (size_t i = 0; i < l_objs_size; i++) {
+            if (l_objs[i].value_len < sizeof(dap_chain_datum_t))
+                continue;
+            dap_chain_datum_t *l_datum = (dap_chain_datum_t *)l_objs[i].value;
+            if (dap_chain_datum_size(l_datum) != l_objs[i].value_len)
+                continue;
+            if (l_datum->header.type_id != DAP_CHAIN_DATUM_SERVICE_STATE)
+                continue;
+            if (dap_chain_node_mempool_process(a_chain, l_datum, l_objs[i].key))
+                dap_global_db_del(l_gdb_group_mempool, l_objs[i].key, NULL, NULL);
+            else
+                l_nothing_processed = false;
+        }
+        DAP_DELETE(l_gdb_group_mempool);
+        if (l_nothing_processed)
+            l_states->state_current = STATE_OVER;
+    }
+
+    case STATE_OVER:
         break;
     // No default here
     }
-- 
GitLab


From 91b9cd3b089b6e0d2832d0d3d5c1c9bdcedb4e38 Mon Sep 17 00:00:00 2001
From: "roman.khlopkov" <roman.khlopkov@demlabs.net>
Date: Thu, 9 Jan 2025 20:20:17 +0300
Subject: [PATCH 8/9] [+] Security for service states datums

---
 .../consensus/esbocs/dap_chain_cs_esbocs.c    | 17 ++++++--
 .../esbocs/include/dap_chain_cs_esbocs.h      |  3 +-
 modules/datum/dap_chain_datum_decree.c        | 13 +++++-
 .../datum/include/dap_chain_datum_decree.h    |  4 +-
 modules/ledger/dap_chain_ledger_decree.c      |  6 ++-
 modules/ledger/include/dap_chain_ledger.h     |  2 +-
 modules/net/dap_chain_node.c                  | 43 +++++++++++++------
 modules/net/include/dap_chain_node.h          |  2 +-
 modules/node-cli/dap_chain_node_cli.c         |  5 ++-
 modules/node-cli/dap_chain_node_cli_cmd.c     | 28 +++++++++++-
 modules/type/blocks/dap_chain_cs_blocks.c     |  2 +-
 .../type/blocks/include/dap_chain_cs_blocks.h |  6 +--
 12 files changed, 98 insertions(+), 33 deletions(-)

diff --git a/modules/consensus/esbocs/dap_chain_cs_esbocs.c b/modules/consensus/esbocs/dap_chain_cs_esbocs.c
index e1ed7e5cfb..3c9d78d16e 100644
--- a/modules/consensus/esbocs/dap_chain_cs_esbocs.c
+++ b/modules/consensus/esbocs/dap_chain_cs_esbocs.c
@@ -738,12 +738,13 @@ int dap_chain_esbocs_set_min_validators_count(dap_chain_t *a_chain, uint16_t a_n
     return 0;
 }
 
-int dap_chain_esbocs_set_hardfork_prepare(dap_chain_t *a_chain, uint64_t a_block_num)
+int dap_chain_esbocs_set_hardfork_prepare(dap_chain_t *a_chain, uint64_t a_block_num, dap_list_t *a_trusted_addrs)
 {
     uint64_t l_last_num = a_chain->callback_count_atom(a_chain);
     dap_chain_cs_blocks_t *l_blocks = DAP_CHAIN_CS_BLOCKS(a_chain);
     dap_chain_esbocs_t *l_esbocs = DAP_CHAIN_ESBOCS(l_blocks);
     l_esbocs->hardfork_from = dap_max(l_last_num, a_block_num);
+    l_esbocs->hardfork_trusted_addrs = a_trusted_addrs;
     return a_block_num && a_block_num < l_last_num ? 1 : 0;
 }
 
@@ -1208,7 +1209,7 @@ static bool s_session_round_new(void *a_arg)
     if (l_cur_atom_count && l_cur_atom_count == a_session->esbocs->hardfork_from) {
         dap_time_t l_last_block_timestamp = 0;
         dap_chain_get_atom_last_hash_num_ts(a_session->chain, c_cell_id_hardfork, NULL, NULL, &l_last_block_timestamp);
-        dap_chain_node_hardfork_prepare(a_session->chain, l_last_block_timestamp);
+        dap_chain_node_hardfork_prepare(a_session->chain, l_last_block_timestamp, a_session->esbocs->hardfork_trusted_addrs);
     }
     return false;
 }
@@ -2857,9 +2858,17 @@ static int s_callback_block_verify(dap_chain_cs_blocks_t *a_blocks, dap_chain_bl
     dap_chain_esbocs_t *l_esbocs = DAP_CHAIN_ESBOCS(a_blocks);
     dap_chain_esbocs_pvt_t *l_esbocs_pvt = PVT(l_esbocs);
 
-    if (l_esbocs->session && l_esbocs->session->processing_candidate == a_block)
+    if (l_esbocs->session && l_esbocs->session->processing_candidate == a_block) {
         // It's a block candidate, don't check signs
-        return a_block->hdr.version > 1 ? 0 : -3;
+        if (a_block->hdr.version <= 1) {
+            log_it(L_WARNING, "Illegal block version %d", a_block->hdr.version);
+            return -3;
+        }
+        if (a_blocks->is_hardfork_state) {
+            // Addtionally verify datums vs internal states
+
+        }
+    }
 
     size_t l_block_size = a_block_size; // /* Can't calc it for many old bugged blocks */ dap_chain_block_get_size(a_block);
     size_t l_signs_count = 0;
diff --git a/modules/consensus/esbocs/include/dap_chain_cs_esbocs.h b/modules/consensus/esbocs/include/dap_chain_cs_esbocs.h
index 93ae2be966..c86591e1b4 100644
--- a/modules/consensus/esbocs/include/dap_chain_cs_esbocs.h
+++ b/modules/consensus/esbocs/include/dap_chain_cs_esbocs.h
@@ -124,6 +124,7 @@ typedef struct dap_chain_esbocs {
     dap_chain_cs_blocks_t *blocks;
     dap_chain_esbocs_session_t *session;
     uint64_t hardfork_from;
+    dap_list_t *hardfork_trusted_addrs;
     dap_time_t last_directive_vote_timestamp, last_directive_accept_timestamp,
                last_submitted_candidate_timestamp, last_accepted_block_timestamp;
     void *_pvt;
@@ -267,5 +268,5 @@ int dap_chain_esbocs_set_min_validators_count(dap_chain_t *a_chain, uint16_t a_n
 uint16_t dap_chain_esbocs_get_min_validators_count(dap_chain_net_id_t a_net_id);
 int dap_chain_esbocs_set_emergency_validator(dap_chain_t *a_chain, bool a_add, uint32_t a_sign_type, dap_hash_fast_t *a_validator_hash);
 int dap_chain_esbocs_set_signs_struct_check(dap_chain_t *a_chain, bool a_enable);
-int dap_chain_esbocs_set_hardfork_prepare(dap_chain_t *a_chain, uint64_t a_block_num);
+int dap_chain_esbocs_set_hardfork_prepare(dap_chain_t *a_chain, uint64_t a_block_num, dap_list_t *a_trusted_addrs);
 void dap_chain_esbocs_change_debug_mode(dap_chain_t *a_chain, bool a_enable);
diff --git a/modules/datum/dap_chain_datum_decree.c b/modules/datum/dap_chain_datum_decree.c
index bfddd2c62a..bb2798467b 100644
--- a/modules/datum/dap_chain_datum_decree.c
+++ b/modules/datum/dap_chain_datum_decree.c
@@ -71,7 +71,16 @@ int dap_chain_datum_decree_get_fee_addr(dap_chain_datum_decree_t *a_decree, dap_
 dap_list_t *dap_chain_datum_decree_get_owners(dap_chain_datum_decree_t *a_decree, uint16_t *a_owners_num)
 {
     dap_return_val_if_fail(a_decree && a_owners_num, NULL);
-    dap_list_t *l_ret = dap_tsd_find_all(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_OWNER);
+    dap_list_t *l_ret = dap_tsd_find_all(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_OWNER, 0);
+    dap_list_t *it, *tmp;
+    DL_FOREACH_SAFE(l_ret, it, tmp) {
+        dap_tsd_t *l_tsd = it->data;
+        if (l_tsd->size < sizeof(dap_pkey_t) || l_tsd->size != sizeof(dap_pkey_t) + ((dap_pkey_t *)l_tsd->data)->header.size) {
+            log_it(L_ERROR, "Incorrect size %u of owner pkey", l_tsd->size);
+            DL_DELETE(l_ret, it);
+            DAP_DEL_MULTY(it->data, it);
+        }
+    }
     if (a_owners_num)
         *a_owners_num = (uint16_t)dap_list_length(l_ret);
     return l_ret;
@@ -105,7 +114,7 @@ int dap_chain_datum_decree_get_stake_signing_addr(dap_chain_datum_decree_t *a_de
     return l_tsd && l_tsd->size == sizeof(dap_chain_addr_t) ? ( _dap_tsd_get_scalar(l_tsd, a_signing_addr), 0 ) : 1;
 }
 
-int dap_chain_datum_decree_get_stake_signer_node_addr(dap_chain_datum_decree_t *a_decree, dap_chain_node_addr_t *a_node_addr)
+int dap_chain_datum_decree_get_node_addr(dap_chain_datum_decree_t *a_decree, dap_chain_node_addr_t *a_node_addr)
 {
     dap_return_val_if_fail(a_decree && a_node_addr, -1);
     dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_NODE_ADDR);
diff --git a/modules/datum/include/dap_chain_datum_decree.h b/modules/datum/include/dap_chain_datum_decree.h
index ae1ace834a..9cb2ddda29 100644
--- a/modules/datum/include/dap_chain_datum_decree.h
+++ b/modules/datum/include/dap_chain_datum_decree.h
@@ -293,12 +293,12 @@ int dap_chain_datum_decree_get_value(dap_chain_datum_decree_t *a_decree, uint256
 int dap_chain_datum_decree_get_stake_signing_addr(dap_chain_datum_decree_t *a_decree, dap_chain_addr_t *a_signing_addr);
 
 /**
- * @brief dap_chain_datum_decree_get_stake_signer_node_addr get signer node address
+ * @brief dap_chain_datum_decree_get_node_addr get signer node address
  * @param a_decree pointer to decree
  * @param a_node_addr pointer to signer node address buffer
  * @return result code. 0 - success
  */
-int dap_chain_datum_decree_get_stake_signer_node_addr(dap_chain_datum_decree_t *a_decree, dap_chain_node_addr_t *a_node_addr);
+int dap_chain_datum_decree_get_node_addr(dap_chain_datum_decree_t *a_decree, dap_chain_node_addr_t *a_node_addr);
 
 /**
  * @brief dap_chain_datum_decree_get_stake_min_value get minimum stake value
diff --git a/modules/ledger/dap_chain_ledger_decree.c b/modules/ledger/dap_chain_ledger_decree.c
index 482a8ee00b..e30532b269 100644
--- a/modules/ledger/dap_chain_ledger_decree.c
+++ b/modules/ledger/dap_chain_ledger_decree.c
@@ -399,7 +399,7 @@ const char *l_ban_addr;
                 log_it(L_WARNING,"Can't get signing address from decree.");
                 return -107;
             }
-            if (dap_chain_datum_decree_get_stake_signer_node_addr(a_decree, &l_node_addr)){
+            if (dap_chain_datum_decree_get_node_addr(a_decree, &l_node_addr)){
                 log_it(L_WARNING,"Can't get signer node address from decree.");
                 return -108;
             }
@@ -611,7 +611,9 @@ const char *l_ban_addr;
             }
             if (!a_apply)
                 break;
-            return dap_chain_esbocs_set_hardfork_prepare(l_chain, l_block_num);
+            dap_list_t *l_addrs = dap_tsd_find_all(a_decree->data_n_signs, a_decree->header.data_size,
+                                                   DAP_CHAIN_DATUM_DECREE_TSD_TYPE_NODE_ADDR, sizeof(dap_stream_node_addr_t));
+            return dap_chain_esbocs_set_hardfork_prepare(l_chain, l_block_num, l_addrs);
         default:
             return -1;
     }
diff --git a/modules/ledger/include/dap_chain_ledger.h b/modules/ledger/include/dap_chain_ledger.h
index 907d180b8b..e68e2e8395 100644
--- a/modules/ledger/include/dap_chain_ledger.h
+++ b/modules/ledger/include/dap_chain_ledger.h
@@ -43,7 +43,7 @@
 
 typedef struct dap_ledger {
     dap_chain_net_t *net;
-    bool is_hardfork_data;
+    bool is_hardfork_state;
     void *_internal;
 } dap_ledger_t;
 
diff --git a/modules/net/dap_chain_node.c b/modules/net/dap_chain_node.c
index 4a7e1c8c5c..39ad5ac774 100644
--- a/modules/net/dap_chain_node.c
+++ b/modules/net/dap_chain_node.c
@@ -92,6 +92,7 @@ struct hardfork_states {
     dap_ledger_hardfork_condouts_t *condouts;
     dap_chain_cs_blocks_hardfork_fees_t *fees;
     dap_chain_srv_hardfork_state_t *service_states;
+    dap_list_t *trusted_addrs;
 };
 
 static const uint64_t s_cmp_delta_timestamp = (uint64_t)1000 /*sec*/ * (uint64_t)1000000000;
@@ -448,13 +449,13 @@ void dap_chain_node_mempool_process_all(dap_chain_t *a_chain, bool a_force)
     }
 #endif
     char *l_gdb_group_mempool = dap_chain_mempool_group_new(a_chain);
-    size_t l_objs_size = 0;
-    dap_global_db_obj_t *l_objs = dap_global_db_get_all_sync(l_gdb_group_mempool, &l_objs_size);
-    if (l_objs_size) {
+    size_t l_objs_count = 0;
+    dap_global_db_obj_t *l_objs = dap_global_db_get_all_sync(l_gdb_group_mempool, &l_objs_count);
+    if (l_objs_count) {
 #ifdef DAP_TPS_TEST
-        log_it(L_TPS, "Get %zu datums from mempool", l_objs_size);
+        log_it(L_TPS, "Get %zu datums from mempool", l_objs_count);
 #endif
-        for (size_t i = 0; i < l_objs_size; i++) {
+        for (size_t i = 0; i < l_objs_count; i++) {
             if (l_objs[i].value_len < sizeof(dap_chain_datum_t))
                 continue;
             dap_chain_datum_t *l_datum = (dap_chain_datum_t *)l_objs[i].value;
@@ -496,7 +497,7 @@ void dap_chain_node_mempool_process_all(dap_chain_t *a_chain, bool a_force)
                 }
             }
         }
-        dap_global_db_objs_delete(l_objs, l_objs_size);
+        dap_global_db_objs_delete(l_objs, l_objs_count);
     }
     DAP_DELETE(l_gdb_group_mempool);
 }
@@ -529,7 +530,7 @@ dap_chain_datum_t **s_service_state_datums_create(dap_chain_srv_hardfork_state_t
     return ret;
 }
 
-int dap_chain_node_hardfork_prepare(dap_chain_t *a_chain, dap_time_t a_last_block_timestamp)
+int dap_chain_node_hardfork_prepare(dap_chain_t *a_chain, dap_time_t a_last_block_timestamp, dap_list_t *a_trusted_addrs)
 {
     if (dap_strcmp(dap_chain_get_cs_type(a_chain), DAP_CHAIN_ESBOCS_CS_TYPE_STR))
         return log_it(L_ERROR, "Can't prepare harfork for chain type %s is not supported", dap_chain_get_cs_type(a_chain)), -2;
@@ -552,9 +553,10 @@ int dap_chain_node_hardfork_prepare(dap_chain_t *a_chain, dap_time_t a_last_bloc
         DL_DELETE(l_states->service_states, it);
         DAP_DELETE(it);
     }
+    l_states->trusted_addrs = a_trusted_addrs;
     a_chain->hardfork_data = l_states;
-    DAP_CHAIN_CS_BLOCKS(a_chain)->is_hardfork_data = true;
-    l_net->pub.ledger->is_hardfork_data = true;
+    DAP_CHAIN_CS_BLOCKS(a_chain)->is_hardfork_state = true;
+    l_net->pub.ledger->is_hardfork_state = true;
     return 0;
 }
 
@@ -743,16 +745,30 @@ int dap_chain_node_hardfork_process(dap_chain_t *a_chain)
         break;
     case STATE_MEMPOOL: {
         char *l_gdb_group_mempool = dap_chain_mempool_group_new(a_chain);
-        size_t l_objs_size = 0;
-        dap_global_db_obj_t *l_objs = dap_global_db_get_all_sync(l_gdb_group_mempool, &l_objs_size);
-        if (!l_objs_size) {
+        size_t l_objs_count = 0;
+        dap_store_obj_t *l_objs = dap_global_db_get_all_raw_sync(l_gdb_group_mempool, &l_objs_count);
+        if (!l_objs_count) {
             l_states->state_current = STATE_OVER;
             break;
         }
         bool l_nothing_processed = true;
-        for (size_t i = 0; i < l_objs_size; i++) {
+        for (size_t i = 0; i < l_objs_count; i++) {
             if (l_objs[i].value_len < sizeof(dap_chain_datum_t))
                 continue;
+            if (!l_objs[i].sign)
+                continue;
+            dap_stream_node_addr_t l_addr = dap_stream_node_addr_from_sign(l_objs[i].sign);
+            bool l_addr_match = false;
+            for (dap_list_t *it = l_states->trusted_addrs; it; it = it->next) {
+                if (((dap_stream_node_addr_t *)it->data)->uint64 != l_addr.uint64)
+                    continue;
+                l_addr_match = true;
+                break;
+            }
+            if (!l_addr_match) {
+                log_it(L_WARNING, "Trying to inject hardfork service state datum from addr " NODE_ADDR_FP_STR, NODE_ADDR_FP_ARGS_S(l_addr));
+                continue;
+            }
             dap_chain_datum_t *l_datum = (dap_chain_datum_t *)l_objs[i].value;
             if (dap_chain_datum_size(l_datum) != l_objs[i].value_len)
                 continue;
@@ -763,6 +779,7 @@ int dap_chain_node_hardfork_process(dap_chain_t *a_chain)
             else
                 l_nothing_processed = false;
         }
+        dap_store_obj_free(l_objs, l_objs_count);
         DAP_DELETE(l_gdb_group_mempool);
         if (l_nothing_processed)
             l_states->state_current = STATE_OVER;
diff --git a/modules/net/include/dap_chain_node.h b/modules/net/include/dap_chain_node.h
index 98833d24fb..bec13ed40f 100644
--- a/modules/net/include/dap_chain_node.h
+++ b/modules/net/include/dap_chain_node.h
@@ -103,7 +103,7 @@ void dap_chain_node_mempool_process_all(dap_chain_t *a_chain, bool a_force);
 bool dap_chain_node_mempool_autoproc_init();
 inline static void dap_chain_node_mempool_autoproc_deinit() {}
 
-int dap_chain_node_hardfork_prepare(dap_chain_t *a_chain, dap_time_t a_last_block_timestamp);
+int dap_chain_node_hardfork_prepare(dap_chain_t *a_chain, dap_time_t a_last_block_timestamp, dap_list_t *a_trusted_addrs);
 
 dap_list_t *dap_chain_node_get_states_list_sort(dap_chain_net_t *a_net, dap_chain_node_addr_t *a_ignored, size_t a_ignored_count);
 dap_string_t *dap_chain_node_states_info_read(dap_chain_net_t *a_net, dap_stream_node_addr_t a_addr);
diff --git a/modules/node-cli/dap_chain_node_cli.c b/modules/node-cli/dap_chain_node_cli.c
index 9dde939f63..735e71f020 100644
--- a/modules/node-cli/dap_chain_node_cli.c
+++ b/modules/node-cli/dap_chain_node_cli.c
@@ -380,7 +380,10 @@ int dap_chain_node_cli_init(dap_config_t * g_config)
 
     // Decree create command
     dap_cli_server_cmd_add ("decree", cmd_decree, "Work with decree",
-            "decree create [common] -net <net_name> [-chain <chain_name>] -decree_chain <chain_name> -certs <certs_list> {-fee <net_fee_value> -to_addr <net_fee_wallet_addr> | -hardfork_from <atom_number> | -new_certs <new_owners_certs_list> | -signs_verify <value>}\n"
+            "decree create [common] -net <net_name> [-chain <chain_name>] -decree_chain <chain_name> -certs <certs_list> {-fee <net_fee_value> -to_addr <net_fee_wallet_addr> |"
+                                                                                                                        " -hardfork_from <atom_number> [-trusted_addrs <node_addresses>] |"
+                                                                                                                        " -new_certs <new_owners_certs_list> |"
+                                                                                                                        " -signs_verify <value>}\n"
             "Creates common network decree in net <net_name>. Decree adds to chain -chain and applies to chain -decree_chain. If -chain and -decree_chain is different you must create anchor in -decree_chain that is connected to this decree."
             "\nCommon decree parameters:\n"
             "\t -fee <value>: sets network fee\n"
diff --git a/modules/node-cli/dap_chain_node_cli_cmd.c b/modules/node-cli/dap_chain_node_cli_cmd.c
index a0fae6b9b4..dd06c89c80 100644
--- a/modules/node-cli/dap_chain_node_cli_cmd.c
+++ b/modules/node-cli/dap_chain_node_cli_cmd.c
@@ -4071,7 +4071,6 @@ int cmd_decree(int a_argc, char **a_argv, void **a_str_reply)
             l_tsd = DAP_NEW_Z_SIZE(dap_tsd_t, l_total_tsd_size);
             if (!l_tsd) {
                 log_it(L_CRITICAL, "%s", c_error_memory_alloc);
-                dap_list_free_full(l_tsd_list, NULL);
                 return -1;
             }
             l_tsd->type = DAP_CHAIN_DATUM_DECREE_TSD_TYPE_BLOCK_NUM;
@@ -4079,10 +4078,35 @@ int cmd_decree(int a_argc, char **a_argv, void **a_str_reply)
             *(uint64_t*)(l_tsd->data) = strtoll(l_param_value_str, NULL, 10);
             if (!*(uint64_t*)l_tsd->data && dap_strcmp(l_param_value_str, "0")) {
                 log_it(L_ERROR, "Can't converts %s to atom number", l_param_value_str);
-                dap_list_free_full(l_tsd_list, NULL);
+                DAP_DELETE(l_tsd);
                 return -1;
             }
             l_tsd_list = dap_list_append(l_tsd_list, l_tsd);
+            if (dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-trusted_addrs", &l_param_addr_str)) {
+                char **l_addrs = dap_strsplit(l_param_addr_str, ",", 256);
+                for (uint16_t i = 0; l_addrs[i]; i++) {
+                    dap_stream_node_addr_t l_addr_cur;
+                    if (dap_stream_node_addr_from_str(&l_addr_cur, l_addrs[i])) {
+                        log_it(L_ERROR, "Can't convert %s to node addr", l_addrs[i]);
+                        dap_list_free_full(l_tsd_list, NULL);
+                        dap_strfreev(l_addrs);
+                        return -5;
+                    }
+                    l_tsd = DAP_NEW_Z_SIZE(dap_tsd_t, sizeof(dap_tsd_t) + sizeof(dap_stream_node_addr_t));
+                    if (!l_tsd) {
+                        log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+                        dap_list_free_full(l_tsd_list, NULL);
+                        dap_strfreev(l_addrs);
+                        return -1;
+                    }
+                    l_tsd->type = DAP_CHAIN_DATUM_DECREE_TSD_TYPE_NODE_ADDR;
+                    l_tsd->size = sizeof(dap_stream_node_addr_t);
+
+                    l_tsd_list = dap_list_append(l_tsd_list, l_tsd);
+                    l_total_tsd_size += sizeof(dap_tsd_t) + sizeof(dap_stream_node_addr_t);
+                }
+                dap_strfreev(l_addrs);
+            }
         } else if (dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-new_certs", &l_param_value_str)){
             l_subtype = DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_OWNERS;
             dap_cert_parse_str_list(l_param_value_str, &l_new_certs, &l_new_certs_count);
diff --git a/modules/type/blocks/dap_chain_cs_blocks.c b/modules/type/blocks/dap_chain_cs_blocks.c
index 25ab796329..67409a44d7 100644
--- a/modules/type/blocks/dap_chain_cs_blocks.c
+++ b/modules/type/blocks/dap_chain_cs_blocks.c
@@ -2054,7 +2054,7 @@ static dap_chain_atom_verify_res_t s_callback_atom_verify(dap_chain_t *a_chain,
         }
     }
 
-    if (ret == ATOM_FORK || ret == ATOM_ACCEPT) {
+    if (ret == ATOM_ACCEPT || (!l_blocks->is_hardfork_state && ret == ATOM_FORK)) {
         // 2nd level consensus
         if (l_blocks->callback_block_verify && l_blocks->callback_block_verify(l_blocks, l_block, a_atom_hash, /* Old bug, crutch for it */ a_atom_size)) {
             // Hard accept list
diff --git a/modules/type/blocks/include/dap_chain_cs_blocks.h b/modules/type/blocks/include/dap_chain_cs_blocks.h
index 511f18204f..d260fdcfa3 100644
--- a/modules/type/blocks/include/dap_chain_cs_blocks.h
+++ b/modules/type/blocks/include/dap_chain_cs_blocks.h
@@ -39,12 +39,12 @@ typedef dap_chain_block_t * (*dap_chain_cs_blocks_callback_block_create_t)(dap_c
                                                                                dap_chain_datum_t *,
                                                                                dap_chain_hash_fast_t *,
                                                                                size_t, size_t*);
-typedef struct dap_chain_cs_blocks
-{
+typedef struct dap_chain_cs_blocks {
    dap_chain_t *chain;
    dap_chain_block_t *block_new; // For new block creating
    size_t block_new_size;
-   bool is_hardfork_data;
+
+   bool is_hardfork_state;
    uint16_t generation;
 
    dap_chain_cs_blocks_callback_t callback_delete;
-- 
GitLab


From 6261a01aea15a95943d81323627a51e7592fd33b Mon Sep 17 00:00:00 2001
From: "roman.khlopkov" <roman.khlopkov@demlabs.net>
Date: Thu, 9 Jan 2025 20:32:34 +0300
Subject: [PATCH 9/9] [*] Build fix

---
 dap-sdk                              | 2 +-
 modules/chain/dap_chain.c            | 2 +-
 modules/net/dap_chain_net.c          | 1 +
 modules/net/include/dap_chain_node.h | 1 +
 4 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/dap-sdk b/dap-sdk
index 5a92226d09..5c58976798 160000
--- a/dap-sdk
+++ b/dap-sdk
@@ -1 +1 @@
-Subproject commit 5a92226d095b1df1996c1122de2dbe636b139ffc
+Subproject commit 5c58976798909feee535cbff8b2b7e80028aad1c
diff --git a/modules/chain/dap_chain.c b/modules/chain/dap_chain.c
index a3563babae..9ed29c5e98 100644
--- a/modules/chain/dap_chain.c
+++ b/modules/chain/dap_chain.c
@@ -180,7 +180,7 @@ void dap_chain_delete(dap_chain_t *a_chain)
         DAP_DEL_MULTY(DAP_CHAIN_PVT(a_chain)->file_storage_dir, DAP_CHAIN_PVT(a_chain));
     }
     DAP_DEL_MULTY(a_chain->name, a_chain->net_name, a_chain->datum_types,
-        a_chain->autoproc_datum_types, a_chain->authorized_nodes_addrs), a_chain->_inheritor);
+        a_chain->autoproc_datum_types, a_chain->authorized_nodes_addrs, a_chain->_inheritor);
     pthread_rwlock_destroy(&a_chain->rwlock);
     pthread_rwlock_destroy(&a_chain->cell_rwlock);
     DAP_DELETE(a_chain);
diff --git a/modules/net/dap_chain_net.c b/modules/net/dap_chain_net.c
index 8c5b699e05..2d1ea5caf4 100644
--- a/modules/net/dap_chain_net.c
+++ b/modules/net/dap_chain_net.c
@@ -81,6 +81,7 @@
 #include "dap_chain_datum_decree.h"
 #include "dap_chain_datum_anchor.h"
 #include "dap_chain_node_client.h"
+#include "dap_chain_mempool.h"
 #include "dap_chain_net.h"
 #include "dap_chain_net_node_list.h"
 #include "dap_chain_net_tx.h"
diff --git a/modules/net/include/dap_chain_node.h b/modules/net/include/dap_chain_node.h
index bec13ed40f..e1a0550c9e 100644
--- a/modules/net/include/dap_chain_node.h
+++ b/modules/net/include/dap_chain_node.h
@@ -104,6 +104,7 @@ bool dap_chain_node_mempool_autoproc_init();
 inline static void dap_chain_node_mempool_autoproc_deinit() {}
 
 int dap_chain_node_hardfork_prepare(dap_chain_t *a_chain, dap_time_t a_last_block_timestamp, dap_list_t *a_trusted_addrs);
+int dap_chain_node_hardfork_process(dap_chain_t *a_chain);
 
 dap_list_t *dap_chain_node_get_states_list_sort(dap_chain_net_t *a_net, dap_chain_node_addr_t *a_ignored, size_t a_ignored_count);
 dap_string_t *dap_chain_node_states_info_read(dap_chain_net_t *a_net, dap_stream_node_addr_t a_addr);
-- 
GitLab