From 90fefea69989a686132c2fccdb16f580ff296996 Mon Sep 17 00:00:00 2001
From: Roman Khlopkov <roman.khlopkov@demlabs.net>
Date: Wed, 23 Sep 2020 09:44:34 +0000
Subject: [PATCH] features-4039

---
 dap-sdk/net/core/dap_events.c                 |   2 +-
 dap-sdk/net/core/include/dap_events_socket.h  |   8 +-
 modules/chain/dap_chain_cs.c                  |   2 +-
 modules/chain/dap_chain_ledger.c              | 423 ++++++++++++++----
 modules/chain/include/dap_chain.h             |   1 +
 modules/chain/include/dap_chain_ledger.h      |  17 +-
 modules/chain/include/dap_chain_pvt.h         |   2 +
 modules/channel/chain/dap_stream_ch_chain.c   |   3 +
 modules/consensus/none/dap_chain_cs_none.c    |  33 +-
 .../none/include/dap_chain_cs_none.h          |   2 +-
 modules/net/CMakeLists.txt                    |   4 +-
 modules/net/dap_chain_net.c                   |  22 +-
 modules/net/srv/dap_chain_net_srv_order.c     |   8 +-
 .../xchange/dap_chain_net_srv_xchange.c       |   3 +-
 modules/type/dag/dap_chain_cs_dag.c           |  38 +-
 15 files changed, 439 insertions(+), 129 deletions(-)

diff --git a/dap-sdk/net/core/dap_events.c b/dap-sdk/net/core/dap_events.c
index fd786c671c..f29f7f3d8f 100644
--- a/dap-sdk/net/core/dap_events.c
+++ b/dap-sdk/net/core/dap_events.c
@@ -39,7 +39,7 @@
 #include <sys/stat.h>
 #endif
 
-#include <netdb.h>
+//#include <netdb.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
diff --git a/dap-sdk/net/core/include/dap_events_socket.h b/dap-sdk/net/core/include/dap_events_socket.h
index 68020f1a33..dae83d1d74 100644
--- a/dap-sdk/net/core/include/dap_events_socket.h
+++ b/dap-sdk/net/core/include/dap_events_socket.h
@@ -54,12 +54,12 @@
     #define DAP_EVENTS_CAPS_PIPE_POSIX
 #endif
 
-#if defined(DAP_EVENTS_CAPS_EPOLL)
-#include <sys/epoll.h>
-#define EPOLL_HANDLE  int
-#elif defined (DAP_EVENTS_CAPS_WEPOLL)
+#if defined(DAP_EVENTS_CAPS_WEPOLL)
 #include "wepoll.h"
 #define EPOLL_HANDLE  HANDLE
+#elif defined (DAP_EVENTS_CAPS_EPOLL)
+#include <sys/epoll.h>
+#define EPOLL_HANDLE  int
 #endif
 
 #define BIT( x ) ( 1 << x )
diff --git a/modules/chain/dap_chain_cs.c b/modules/chain/dap_chain_cs.c
index cc88a25baf..c7349da68e 100644
--- a/modules/chain/dap_chain_cs.c
+++ b/modules/chain/dap_chain_cs.c
@@ -123,7 +123,7 @@ int dap_chain_cs_create(dap_chain_t * a_chain, dap_config_t * a_chain_cfg)
     if ( l_item ) {
         log_it(L_NOTICE,"Consensus \"%s\" found, prepare to parse config file",l_item->name );
         l_item->callback_init( a_chain, a_chain_cfg);
-        // TODO
+        DAP_CHAIN_PVT(a_chain)->cs_name = l_item->name;
         return 0;
     } else {
         log_it(L_ERROR,"Can't find consensus \"%s\"",dap_config_get_item_str( a_chain_cfg, "chain", "consensus"));
diff --git a/modules/chain/dap_chain_ledger.c b/modules/chain/dap_chain_ledger.c
index e0cf28cbf8..0f66f0343a 100644
--- a/modules/chain/dap_chain_ledger.c
+++ b/modules/chain/dap_chain_ledger.c
@@ -52,7 +52,6 @@
 #include "dap_chain_datum_token.h"
 #include "dap_chain_mempool.h"
 #include "dap_chain_global_db.h"
-#include "dap_chain_net.h"
 #include "dap_chain_ledger.h"
 
 #define LOG_TAG "dap_chain_ledger"
@@ -69,18 +68,12 @@ static  pthread_rwlock_t s_verificators_rwlock;
 #define MAX_OUT_ITEMS   10
 typedef struct dap_chain_ledger_token_emission_item {
     dap_chain_hash_fast_t datum_token_emission_hash;
-    // while these are not needed
-    //dap_chain_hash_fast_t datum_tx_token_hash;
-    //dap_chain_tx_token_t * tx_token;
-
     dap_chain_datum_token_emission_t *datum_token_emission;
     UT_hash_handle hh;
 } dap_chain_ledger_token_emission_item_t;
 
 typedef struct dap_chain_ledger_token_item {
     char ticker[DAP_CHAIN_TICKER_SIZE_MAX];
-    dap_chain_hash_fast_t datum_token_hash;
-    uint8_t padding[6];
     dap_chain_datum_token_t * datum_token;
     uint64_t total_supply;
     pthread_rwlock_t token_emissions_rwlock;
@@ -88,17 +81,18 @@ typedef struct dap_chain_ledger_token_item {
     UT_hash_handle hh;
 } dap_chain_ledger_token_item_t;
 
-// ledger cache item - one of unspendet outputs
+// ledger cache item - one of unspent outputs
 typedef struct dap_chain_ledger_tx_item {
     dap_chain_hash_fast_t tx_hash_fast;
     dap_chain_datum_tx_t *tx;
-    time_t ts_created;
-    int n_outs;
-    int n_outs_used;
-    char token_tiker[10];
-    // TODO dynamically allocates the memory in order not to limit the number of outputs in transaction
-    dap_chain_hash_fast_t tx_hash_spent_fast[MAX_OUT_ITEMS]; // spent outs list
-    uint8_t padding[6];
+    struct {
+        time_t ts_created;
+        int n_outs;
+        int n_outs_used;
+        char token_tiker[10];
+        // TODO dynamically allocates the memory in order not to limit the number of outputs in transaction
+        dap_chain_hash_fast_t tx_hash_spent_fast[MAX_OUT_ITEMS]; // spent outs list
+    } cache_data;
     UT_hash_handle hh;
 } dap_chain_ledger_tx_item_t;
 
@@ -123,21 +117,24 @@ typedef struct dap_chain_ledger_tx_bound {
     dap_chain_ledger_tx_item_t *item_out;
 } dap_chain_ledger_tx_bound_t;
 
-// Gotta use a regular null-terminated string instead, for uthash usability
-/*typedef struct dap_ledger_wallet_balance_key{
-    dap_chain_addr_t addr;
-    char ticker[DAP_CHAIN_TICKER_SIZE_MAX];
-} DAP_ALIGN_PACKED dap_ledger_wallet_balance_key_t; */
-
 // in-memory wallet balance
 typedef struct dap_ledger_wallet_balance {
-    //dap_ledger_wallet_balance_key_t key;
     char *key;
     char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX];
     uint128_t balance;
     UT_hash_handle hh;
 } dap_ledger_wallet_balance_t;
 
+typedef struct dap_ledger_cache_item {
+    dap_chain_hash_fast_t *hash;
+    bool found;
+} dap_ledger_cache_item_t;
+
+typedef struct dap_ledger_cache_str_item {
+    char *key;
+    bool found;
+} dap_ledger_cache_str_item_t;
+
 // dap_ledget_t private section
 typedef struct dap_ledger_private {
     // List of ledger - unspent transactions cache
@@ -163,6 +160,11 @@ typedef struct dap_ledger_private {
     bool check_cells_ds;
     bool check_token_emission;
     dap_chain_cell_id_t local_cell_id;
+    /* Cache section */
+    dap_ledger_cache_item_t last_tx;
+    dap_ledger_cache_item_t last_thres_tx;
+    dap_ledger_cache_item_t last_emit;
+    dap_ledger_cache_str_item_t last_ticker;
 } dap_ledger_private_t;
 #define PVT(a) ( (dap_ledger_private_t* ) a->_internal )
 
@@ -282,13 +284,19 @@ int dap_chain_ledger_token_add(dap_ledger_t * a_ledger,  dap_chain_datum_token_t
         l_token_item = DAP_NEW_Z(dap_chain_ledger_token_item_t);
         dap_snprintf(l_token_item->ticker,sizeof (l_token_item->ticker),"%s",a_token->ticker);
         pthread_rwlock_init(&l_token_item->token_emissions_rwlock,NULL);
-
         l_token_item->datum_token = DAP_NEW_Z_SIZE(dap_chain_datum_token_t, a_token_size);
-
         memcpy(l_token_item->datum_token, a_token,a_token_size);
-        dap_hash_fast(a_token,a_token_size, &l_token_item->datum_token_hash);
+        HASH_ADD_STR(PVT(a_ledger)->tokens, ticker, l_token_item);
+        // Add it to cache
+        dap_chain_datum_token_t *l_token_cache = DAP_NEW_Z_SIZE(dap_chain_datum_token_t, a_token_size);
+        memcpy(l_token_cache, a_token, a_token_size);
+        char *l_gdb_group = dap_chain_ledger_get_gdb_group(a_ledger, "tokens");
+        if (!dap_chain_global_db_gr_set(dap_strdup(a_token->ticker), l_token_cache, a_token_size, l_gdb_group)) {
+            log_it(L_WARNING, "Ledger cache mismatch");
+            DAP_DELETE(l_token_cache);
+        }
+        DAP_DELETE(l_gdb_group);
 
-        HASH_ADD_STR(PVT(a_ledger)->tokens, ticker, l_token_item) ;
         switch(a_token->type){
             case DAP_CHAIN_DATUM_TOKEN_TYPE_SIMPLE:
                 l_token_item->total_supply = a_token->header_private.total_supply;
@@ -311,6 +319,15 @@ int dap_chain_ledger_token_add(dap_ledger_t * a_ledger,  dap_chain_datum_token_t
     return  0;
 }
 
+int dap_chain_ledger_token_load(dap_ledger_t *a_ledger,  dap_chain_datum_token_t *a_token, size_t a_token_size)
+{
+    if (PVT(a_ledger)->last_ticker.found) {
+        return dap_chain_ledger_token_add(a_ledger, a_token, a_token_size);
+    } else if (!strncmp(PVT(a_ledger)->last_ticker.key, a_token->ticker, DAP_CHAIN_TICKER_SIZE_MAX)) {
+        PVT(a_ledger)->last_ticker.found = true;
+    }
+    return 0;
+}
 /**
  * @brief s_treshold_emissions_proc
  * @param a_ledger
@@ -332,21 +349,120 @@ static void s_treshold_txs_proc( dap_ledger_t * a_ledger)
 }
 
 
+void dap_chain_ledger_load_cache(dap_ledger_t *a_ledger)
+{
+    dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger);
+
+    char *l_gdb_group = dap_chain_ledger_get_gdb_group(a_ledger, "tokens");
+    size_t l_objs_count = 0;
+    dap_global_db_obj_t *l_objs = dap_chain_global_db_gr_load(l_gdb_group, &l_objs_count);
+    for (size_t i = 0; i < l_objs_count; i++) {
+        dap_chain_ledger_token_item_t *l_token_item = DAP_NEW_Z(dap_chain_ledger_token_item_t);
+        strncpy(l_token_item->ticker, l_objs[i].key, sizeof(l_token_item->ticker));
+        l_token_item->datum_token = DAP_NEW_Z_SIZE(dap_chain_datum_token_t, l_objs[i].value_len);
+        memcpy(l_token_item->datum_token, l_objs[i].value, l_objs[i].value_len);
+        pthread_rwlock_init(&l_token_item->token_emissions_rwlock, NULL);
+        if (l_token_item->datum_token->type == DAP_CHAIN_DATUM_TOKEN_TYPE_SIMPLE) {
+            l_token_item->total_supply = l_token_item->datum_token->header_private.total_supply;
+        }
+        HASH_ADD_STR(PVT(a_ledger)->tokens, ticker, l_token_item);
+        if (i == l_objs_count - 1) {
+            l_ledger_pvt->last_ticker.key = l_token_item->ticker;
+        }
+    }
+    dap_chain_global_db_objs_delete(l_objs, l_objs_count);
+    DAP_DELETE(l_gdb_group);
+    if (l_objs_count == 0) {
+        l_ledger_pvt->last_ticker.found = true;
+    }
+
+    l_gdb_group = dap_chain_ledger_get_gdb_group(a_ledger, "emissions");
+    l_objs_count = 0;
+    l_objs = dap_chain_global_db_gr_load(l_gdb_group, &l_objs_count);
+    for (size_t i = 0; i < l_objs_count; i++) {
+        dap_chain_ledger_token_emission_item_t *l_emission_item = DAP_NEW_Z(dap_chain_ledger_token_emission_item_t);
+        dap_chain_str_to_hash_fast(l_objs[i].key, &l_emission_item->datum_token_emission_hash);
+        l_emission_item->datum_token_emission = DAP_NEW_Z_SIZE(dap_chain_datum_token_emission_t, l_objs[i].value_len);
+        memcpy(l_emission_item->datum_token_emission, l_objs[i].value, l_objs[i].value_len);
+        const char * c_token_ticker = l_emission_item->datum_token_emission->hdr.ticker;
+        dap_chain_ledger_token_item_t *l_token_item = NULL;
+        HASH_FIND_STR(l_ledger_pvt->tokens, c_token_ticker, l_token_item);
+        if (l_token_item) {
+            HASH_ADD(hh, l_token_item->token_emissions, datum_token_emission_hash,
+                     sizeof(dap_chain_hash_fast_t), l_emission_item);
+        } else {
+            HASH_ADD(hh, l_ledger_pvt->treshold_emissions, datum_token_emission_hash,
+                     sizeof(dap_chain_hash_fast_t), l_emission_item);
+        }
+        if (i == l_objs_count - 1) {
+            l_ledger_pvt->last_emit.hash = &l_emission_item->datum_token_emission_hash;
+        }
+    }
+    dap_chain_global_db_objs_delete(l_objs, l_objs_count);
+    DAP_DELETE(l_gdb_group);
+    if (l_objs_count == 0) {
+        l_ledger_pvt->last_emit.found = true;
+    }
+
+    l_gdb_group = dap_chain_ledger_get_gdb_group(a_ledger, "txs");
+    l_objs_count = 0;
+    l_objs = dap_chain_global_db_gr_load(l_gdb_group, &l_objs_count);
+    for (size_t i = 0; i < l_objs_count; i++) {
+        dap_chain_ledger_tx_item_t *l_tx_item = DAP_NEW_Z(dap_chain_ledger_tx_item_t);
+        dap_chain_str_to_hash_fast(l_objs[i].key, &l_tx_item->tx_hash_fast);
+        l_tx_item->tx = DAP_NEW_Z_SIZE(dap_chain_datum_tx_t, l_objs[i].value_len - sizeof(l_tx_item->cache_data));
+        memcpy(l_tx_item->tx, l_objs[i].value + sizeof(l_tx_item->cache_data), l_objs[i].value_len - sizeof(l_tx_item->cache_data));
+        memcpy(&l_tx_item->cache_data, l_objs[i].value, sizeof(l_tx_item->cache_data));
+        HASH_ADD(hh, l_ledger_pvt->ledger_items, tx_hash_fast, sizeof(dap_chain_hash_fast_t), l_tx_item);
+        if (i == l_objs_count - 1) {
+            PVT(a_ledger)->last_tx.hash = &l_tx_item->tx_hash_fast;
+        }
+    }
+    dap_chain_global_db_objs_delete(l_objs, l_objs_count);
+    DAP_DELETE(l_gdb_group);
+    if (l_objs_count == 0) {
+        l_ledger_pvt->last_tx.found = true;
+    }
+
+    //TODO add tx threshold cache if need
+    l_ledger_pvt->last_thres_tx.found = true;
+
+    l_gdb_group = dap_chain_ledger_get_gdb_group(a_ledger, "balances");
+    l_objs_count = 0;
+    l_objs = dap_chain_global_db_gr_load(l_gdb_group, &l_objs_count);
+    for (size_t i = 0; i < l_objs_count; i++) {
+        dap_ledger_wallet_balance_t *l_balance_item = DAP_NEW_Z(dap_ledger_wallet_balance_t);
+        l_balance_item->key = DAP_NEW_Z_SIZE(char, strlen(l_objs[i].key) + 1);
+        strcpy(l_balance_item->key, l_objs[i].key);
+        char *l_ptr = strchr(l_balance_item->key, ' ');
+        if (l_ptr++) {
+            strcpy(l_balance_item->token_ticker, l_ptr);
+        }
+        l_balance_item->balance = *(uint128_t *)l_objs[i].value;
+        HASH_ADD_KEYPTR(hh, l_ledger_pvt->balance_accounts, l_balance_item->key,
+                        strlen(l_balance_item->key), l_balance_item);
+    }
+    dap_chain_global_db_objs_delete(l_objs, l_objs_count);
+    DAP_DELETE(l_gdb_group);
+}
+
 /**
  * @brief dap_chain_ledger_create
  * @param a_check_flags
  * @return dap_ledger_t
  */
-dap_ledger_t* dap_chain_ledger_create(uint16_t a_check_flags)
+dap_ledger_t* dap_chain_ledger_create(uint16_t a_check_flags, char *a_net_name)
 {
     dap_ledger_t *l_ledger = dap_chain_ledger_handle_new();
+    l_ledger->net_name = a_net_name;
     dap_ledger_private_t *l_ledger_priv = PVT(l_ledger);
     l_ledger_priv->check_flags = a_check_flags;
     l_ledger_priv->check_ds = a_check_flags & DAP_CHAIN_LEDGER_CHECK_LOCAL_DS;
     l_ledger_priv->check_cells_ds = a_check_flags & DAP_CHAIN_LEDGER_CHECK_CELLS_DS;
     l_ledger_priv->check_token_emission = a_check_flags & DAP_CHAIN_LEDGER_CHECK_TOKEN_EMISSION;
-    // load ledger from mempool
-    return l_ledger; //dap_chain_ledger_load(l_ledger, "kelvin-testnet", "plasma");
+    // load ledger cache from GDB
+    dap_chain_ledger_load_cache(l_ledger);
+    return l_ledger;
 }
 
 int dap_chain_ledger_token_emission_add_check(dap_ledger_t *a_ledger, const dap_chain_datum_token_emission_t *a_token_emission
@@ -430,16 +546,23 @@ int dap_chain_ledger_token_emission_add(dap_ledger_t *a_ledger,
             memcpy(l_token_emission_item->datum_token_emission, a_token_emission, a_token_emission_size);
             memcpy(&l_token_emission_item->datum_token_emission_hash,
                     l_token_emission_hash_ptr, sizeof(l_token_emission_hash));
-            dap_chain_ledger_token_emission_item_t * l_token_emissions =  l_token_item ?
-                        l_token_item->token_emissions : l_ledger_priv->treshold_emissions;
-            HASH_ADD(hh, l_token_emissions ,
-                     datum_token_emission_hash, sizeof(l_token_emission_hash),
-                    l_token_emission_item);
-            // save pointer to structure
-            if(l_token_item)
-                l_token_item->token_emissions = l_token_emissions;
-            else
-                l_ledger_priv->treshold_emissions = l_token_emissions;
+            if (l_token_item) {
+                HASH_ADD(hh, l_token_item->token_emissions, datum_token_emission_hash,
+                         sizeof(l_token_emission_hash), l_token_emission_item);
+            } else {
+                HASH_ADD(hh, l_ledger_priv->treshold_emissions, datum_token_emission_hash,
+                         sizeof(l_token_emission_hash), l_token_emission_item);
+            }
+            // Add it to cache
+            dap_chain_datum_token_emission_t *l_emission_cache = DAP_NEW_Z_SIZE(dap_chain_datum_token_emission_t, a_token_emission_size);
+            memcpy(l_emission_cache, a_token_emission, a_token_emission_size);
+            char *l_gdb_group = dap_chain_ledger_get_gdb_group(a_ledger, "emissions");
+            if (!dap_chain_global_db_gr_set(dap_strdup(l_hash_str), l_emission_cache, a_token_emission_size, l_gdb_group)) {
+                log_it(L_WARNING, "Ledger cache mismatch");
+                DAP_DELETE(l_emission_cache);
+            }
+            DAP_DELETE(l_gdb_group);
+
             char * l_token_emission_address_str = dap_chain_addr_to_str( &(a_token_emission->hdr.address) );
             log_it(L_NOTICE,
              "Added token emission datum to %s: type=%s value=%.1llf token=%s to_addr=%s ",
@@ -466,6 +589,20 @@ int dap_chain_ledger_token_emission_add(dap_ledger_t *a_ledger,
     return ret;
 }
 
+int dap_chain_ledger_token_emission_load(dap_ledger_t *a_ledger, const dap_chain_datum_token_emission_t *a_token_emission, size_t a_token_emission_size)
+{
+    if (PVT(a_ledger)->last_emit.found) {
+        return dap_chain_ledger_token_emission_add(a_ledger, a_token_emission, a_token_emission_size);
+    } else {
+        dap_chain_hash_fast_t l_token_emission_hash = {};
+        dap_hash_fast(a_token_emission, a_token_emission_size, &l_token_emission_hash);
+        if (!memcmp(PVT(a_ledger)->last_emit.hash, &l_token_emission_hash, sizeof(dap_chain_hash_fast_t))) {
+            PVT(a_ledger)->last_emit.found = true;
+        }
+    }
+    return 0;
+}
+
 /**
  * @brief dap_chain_ledger_token_emission_find
  * @param a_token_ticker
@@ -522,7 +659,7 @@ const char* dap_chain_ledger_tx_get_token_ticker_by_hash(dap_ledger_t *a_ledger,
     pthread_rwlock_rdlock(&l_ledger_priv->ledger_rwlock);
     HASH_FIND(hh, l_ledger_priv->ledger_items, a_tx_hash, sizeof ( *a_tx_hash), l_item );
     pthread_rwlock_unlock(&l_ledger_priv->ledger_rwlock);
-    return l_item? l_item->token_tiker: NULL;
+    return l_item ? l_item->cache_data.token_tiker : NULL;
 }
 
 /**
@@ -548,7 +685,7 @@ void dap_chain_ledger_addr_get_token_ticker_all(dap_ledger_t *a_ledger, dap_chai
             for(size_t i = 0; i < l_tickers_size; i++) {
                 if (l_tickers[i]==NULL)
                     break;
-                if(l_tickers[i] && strcmp(l_tickers[i], l_tx_item->token_tiker) == 0) {
+                if(l_tickers[i] && strcmp(l_tickers[i], l_tx_item->cache_data.token_tiker) == 0) {
                     l_is_not_in_list = false;
                     break;
                 }
@@ -558,7 +695,7 @@ void dap_chain_ledger_addr_get_token_ticker_all(dap_ledger_t *a_ledger, dap_chai
                     l_tickers_size += (l_tickers_size / 2);
                     l_tickers = DAP_REALLOC(l_tickers, l_tickers_size);
                 }
-                l_tickers[l_tickers_pos] = dap_strdup(l_tx_item->token_tiker);
+                l_tickers[l_tickers_pos] = dap_strdup(l_tx_item->cache_data.token_tiker);
                 l_tickers_pos++;
             }
             dap_chain_hash_fast_t* l_tx_hash = dap_chain_node_datum_tx_calc_hash(l_tx_item->tx);
@@ -647,8 +784,8 @@ static bool dap_chain_ledger_item_is_used_out(dap_chain_ledger_tx_item_t *a_item
     }
     assert(a_idx_out < MAX_OUT_ITEMS);
     // if there are used 'out' items
-    if(a_item->n_outs_used > 0) {
-        if(!dap_hash_fast_is_blank(&(a_item->tx_hash_spent_fast[a_idx_out])))
+    if(a_item->cache_data.n_outs_used > 0) {
+        if(!dap_hash_fast_is_blank(&(a_item->cache_data.tx_hash_spent_fast[a_idx_out])))
             l_used_out = true;
     }
     return l_used_out;
@@ -888,7 +1025,7 @@ int dap_chain_ledger_tx_cache_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t
             l_token = NULL;
         }
         if (!l_token || !*l_token) {
-            l_token = l_item_out->token_tiker;
+            l_token = l_item_out->cache_data.token_tiker;
         }
         if (!*l_token) {
             log_it(L_WARNING, "No token ticker found in previous transaction");
@@ -1057,6 +1194,19 @@ int dap_chain_ledger_tx_add_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *
     return 0;
 }
 
+int dap_chain_ledger_balance_cache_update(dap_ledger_t *a_ledger, dap_ledger_wallet_balance_t *a_balance)
+{
+    char *l_gdb_group = dap_chain_ledger_get_gdb_group(a_ledger, "balances");
+    uint128_t *l_balance_value = DAP_NEW_Z(uint128_t);
+    *l_balance_value = a_balance->balance;
+    if (!dap_chain_global_db_gr_set(dap_strdup(a_balance->key), l_balance_value, sizeof(uint128_t), l_gdb_group)) {
+        log_it(L_WARNING, "Ledger cache mismatch");
+        DAP_DELETE(l_balance_value);
+        return -1;
+    }
+    return 0;
+}
+
 /**
  * Add new transaction to the cache list
  *
@@ -1113,8 +1263,8 @@ int dap_chain_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx)
         void *l_item_in = *(void **)&bound_item->in;
         dap_chain_tx_item_type_t l_type = *(uint8_t *)l_item_in;
         dap_chain_ledger_tx_item_t *l_prev_item_out = bound_item->item_out;
-        if (*l_prev_item_out->token_tiker) {
-            l_token_ticker = l_prev_item_out->token_tiker;
+        if (*l_prev_item_out->cache_data.token_tiker) {
+            l_token_ticker = l_prev_item_out->cache_data.token_tiker;
         } else { // Previous multichannel transaction
             l_token_ticker = bound_item->out.tx_prev_out_ext->token;
         }
@@ -1140,26 +1290,28 @@ int dap_chain_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx)
                                     bound_item->out.tx_prev_out_ext->header.value;
                 //log_it(L_DEBUG,"SPEND %lu from addr: %s", l_value, l_wallet_balance_key);
                 wallet_balance->balance -= l_value;
+                // Update the cache
+                dap_chain_ledger_balance_cache_update(a_ledger, wallet_balance);
             } else {
                 log_it(L_ERROR,"!!! Attempt to SPEND from some non-existent balance !!!: %s %s", l_addr_str, l_token_ticker);
             }
             DAP_DELETE(l_addr_str);
             DAP_DELETE(l_wallet_balance_key);
             /// Mark 'out' item in cache because it used
-            l_tx_prev_hash = &(l_prev_item_out->tx_hash_spent_fast[l_tx_in->header.tx_out_prev_idx]);
+            l_tx_prev_hash = &(l_prev_item_out->cache_data.tx_hash_spent_fast[l_tx_in->header.tx_out_prev_idx]);
         } else { // TX_ITEM_TYPE_IN_COND
             // all balance deducts performed with previous conditional transaction
             dap_chain_tx_in_cond_t *l_tx_in_cond = bound_item->in.tx_cur_in_cond;
             /// Mark 'out' item in cache because it used
-            l_tx_prev_hash = &(l_prev_item_out->tx_hash_spent_fast[l_tx_in_cond->header.tx_out_prev_idx]);
+            l_tx_prev_hash = &(l_prev_item_out->cache_data.tx_hash_spent_fast[l_tx_in_cond->header.tx_out_prev_idx]);
         }
         memcpy(l_tx_prev_hash, l_tx_hash, sizeof(dap_chain_hash_fast_t));
         // add a used output
-        l_prev_item_out->n_outs_used++;
+        l_prev_item_out->cache_data.n_outs_used++;
         char * l_tx_prev_hash_str = dap_chain_hash_fast_to_str_new(l_tx_prev_hash);
 
         // delete previous transactions from cache because all out is used
-        if(l_prev_item_out->n_outs_used == l_prev_item_out->n_outs) {
+        if(l_prev_item_out->cache_data.n_outs_used == l_prev_item_out->cache_data.n_outs) {
             dap_chain_hash_fast_t l_tx_prev_hash_to_del = bound_item->tx_prev_hash_fast;
             // remove from memory ledger
             int res = dap_chain_ledger_tx_remove(a_ledger, &l_tx_prev_hash_to_del);
@@ -1237,6 +1389,8 @@ int dap_chain_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx)
                 //log_it(L_DEBUG, "Balance item is present in cache");
                 wallet_balance->balance += l_value;
                 DAP_DELETE (l_wallet_balance_key);
+                // Update the cache
+                dap_chain_ledger_balance_cache_update(a_ledger, wallet_balance);
             } else {
                 wallet_balance = DAP_NEW_Z(dap_ledger_wallet_balance_t);
                 wallet_balance->key = l_wallet_balance_key;
@@ -1245,6 +1399,8 @@ int dap_chain_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx)
                 //log_it(L_DEBUG,"!!! Create new balance item: %s %s", l_addr_str, l_token_ticker);
                 HASH_ADD_KEYPTR(hh, PVT(a_ledger)->balance_accounts, wallet_balance->key,
                                 strlen(l_wallet_balance_key), wallet_balance);
+                // Add it to cache
+                dap_chain_ledger_balance_cache_update(a_ledger, wallet_balance);
             }
 #ifdef __ANDROID__
             log_it(L_INFO, "Updated balance +%.3Lf %s on addr %s",
@@ -1272,15 +1428,15 @@ int dap_chain_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx)
         l_item_tmp = DAP_NEW_Z(dap_chain_ledger_tx_item_t);
         memcpy(&l_item_tmp->tx_hash_fast, l_tx_hash, sizeof(dap_chain_hash_fast_t));
         l_item_tmp->tx = DAP_NEW_SIZE(dap_chain_datum_tx_t, dap_chain_datum_tx_get_size(a_tx));
-        l_item_tmp->ts_created = (time_t) a_tx->header.ts_created;
+        l_item_tmp->cache_data.ts_created = (time_t) a_tx->header.ts_created;
         //calculate l_item_tmp->n_outs;
 
         // If debug mode dump the UTXO
         if ( dap_log_level_get() == L_DEBUG){
-            l_item_tmp->n_outs = 0;
-            if( l_item_tmp->n_outs){
-                dap_list_t *l_tist_tmp = dap_chain_datum_tx_items_get(a_tx, TX_ITEM_TYPE_OUT, &l_item_tmp->n_outs);
-                for (size_t i =0; i < (size_t) l_item_tmp->n_outs; i++){
+            l_item_tmp->cache_data.n_outs = 0;
+            if( l_item_tmp->cache_data.n_outs){
+                dap_list_t *l_tist_tmp = dap_chain_datum_tx_items_get(a_tx, TX_ITEM_TYPE_OUT, &l_item_tmp->cache_data.n_outs);
+                for (size_t i =0; i < (size_t) l_item_tmp->cache_data.n_outs; i++){
                     // TODO list conditional outputs
                     dap_chain_tx_out_t * l_tx_out = l_tist_tmp->data;
                     char * l_tx_out_addr_str = dap_chain_addr_to_str( &l_tx_out->addr );
@@ -1303,10 +1459,20 @@ int dap_chain_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx)
             }
         }
         if (l_token_ticker && !l_multichannel)
-            strncpy(l_item_tmp->token_tiker, l_token_ticker, sizeof(l_item_tmp->token_tiker) - 1);
-
-        memcpy(l_item_tmp->tx, a_tx, dap_chain_datum_tx_get_size(a_tx));
-        HASH_ADD(hh, l_ledger_priv->ledger_items, tx_hash_fast, sizeof(dap_chain_hash_fast_t), l_item_tmp); // tx_hash_fast: name of key field
+            strncpy(l_item_tmp->cache_data.token_tiker, l_token_ticker, sizeof(l_item_tmp->cache_data.token_tiker) - 1);
+        size_t l_tx_size = dap_chain_datum_tx_get_size(a_tx);
+        memcpy(l_item_tmp->tx, a_tx, l_tx_size);
+        HASH_ADD(hh, l_ledger_priv->ledger_items, tx_hash_fast, sizeof(dap_chain_hash_fast_t), l_item_tmp); // tx_hash_fast: name of key field   
+        // Add it to cache
+        uint8_t *l_tx_cache = DAP_NEW_Z_SIZE(uint8_t, l_tx_size + sizeof(l_item_tmp->cache_data));
+        memcpy(l_tx_cache, &l_item_tmp->cache_data, sizeof(l_item_tmp->cache_data));
+        memcpy(l_tx_cache + sizeof(l_item_tmp->cache_data), a_tx, l_tx_size);
+        char *l_gdb_group = dap_chain_ledger_get_gdb_group(a_ledger, "txs");
+        if (!dap_chain_global_db_gr_set(dap_strdup(l_tx_hash_str), l_tx_cache, l_tx_size + sizeof(l_item_tmp->cache_data), l_gdb_group)) {
+            log_it(L_WARNING, "Ledger cache mismatch");
+            DAP_DELETE(l_tx_cache);
+        }
+        DAP_DELETE(l_gdb_group);
         ret = 1;
     }
 FIN:
@@ -1316,6 +1482,25 @@ FIN:
     return ret;
 }
 
+int dap_chain_ledger_tx_load(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx)
+{
+    if (PVT(a_ledger)->last_tx.found && PVT(a_ledger)->last_thres_tx.found) {
+        return dap_chain_ledger_tx_add(a_ledger, a_tx);
+    } else {
+        dap_chain_hash_fast_t l_tx_hash = {};
+        dap_hash_fast(a_tx, dap_chain_datum_tx_get_size(a_tx), &l_tx_hash);
+        if (!PVT(a_ledger)->last_tx.found &&
+                !memcmp(PVT(a_ledger)->last_tx.hash, &l_tx_hash, sizeof(dap_chain_hash_fast_t))) {
+            PVT(a_ledger)->last_tx.found = true;
+        }
+        if (!PVT(a_ledger)->last_thres_tx.found &&
+                !memcmp(PVT(a_ledger)->last_thres_tx.hash, &l_tx_hash, sizeof(dap_chain_hash_fast_t))) {
+            PVT(a_ledger)->last_thres_tx.found = true;
+        }
+    }
+    return 1;
+}
+
 /**
  * Delete transaction from the cache
  *
@@ -1332,18 +1517,20 @@ int dap_chain_ledger_tx_remove(dap_ledger_t *a_ledger, dap_chain_hash_fast_t *a_
     HASH_FIND(hh, l_ledger_priv->ledger_items, a_tx_hash, sizeof(dap_chain_hash_fast_t), l_item_tmp);
     if(l_item_tmp != NULL) {
         HASH_DEL(l_ledger_priv->ledger_items, l_item_tmp);
+        // delete transaction
+        DAP_DELETE(l_item_tmp->tx);
+        // del struct for hash
+        DAP_DELETE(l_item_tmp);
+        // Remove it from cache
+        char *l_gdb_group = dap_chain_ledger_get_gdb_group(a_ledger, "txs");
+        dap_chain_global_db_gr_del(dap_chain_hash_fast_to_str_new(a_tx_hash), l_gdb_group);
+        DAP_DELETE(l_gdb_group);
         l_ret = 1;
     }
     else
         // hash not found in the cache
         l_ret = -2;
     pthread_rwlock_unlock(&l_ledger_priv->ledger_rwlock);
-    if(!l_ret) {
-        // delete transaction
-        DAP_DELETE(l_item_tmp->tx);
-        // del struct for hash
-        DAP_DELETE(l_item_tmp);
-    }
     return l_ret;
 }
 
@@ -1353,16 +1540,82 @@ int dap_chain_ledger_tx_remove(dap_ledger_t *a_ledger, dap_chain_hash_fast_t *a_
 void dap_chain_ledger_purge(dap_ledger_t *a_ledger)
 {
     dap_ledger_private_t *l_ledger_priv = PVT(a_ledger);
-    dap_chain_ledger_tx_item_t *l_iter_current, *l_item_tmp;
+    const int l_hash_str_size = DAP_CHAIN_HASH_FAST_SIZE * 2 + 2;
+    char l_hash_str[l_hash_str_size];
     pthread_rwlock_wrlock(&l_ledger_priv->ledger_rwlock);
-    HASH_ITER(hh, l_ledger_priv->ledger_items , l_iter_current, l_item_tmp)
-    {
-        // delete transaction
-        DAP_DELETE(l_iter_current->tx);
-        // del struct for hash
-        HASH_DEL(l_ledger_priv->ledger_items, l_iter_current);
+    pthread_rwlock_wrlock(&l_ledger_priv->tokens_rwlock);
+    pthread_rwlock_wrlock(&l_ledger_priv->treshold_emissions_rwlock);
+    pthread_rwlock_wrlock(&l_ledger_priv->treshold_txs_rwlock);
+
+    // delete transactions
+    dap_chain_ledger_tx_item_t *l_item_current, *l_item_tmp;
+    char *l_gdb_group = dap_chain_ledger_get_gdb_group(a_ledger, "txs");
+    HASH_ITER(hh, l_ledger_priv->ledger_items , l_item_current, l_item_tmp) {
+        DAP_DELETE(l_item_current->tx);
+        HASH_DEL(l_ledger_priv->ledger_items, l_item_current);
+        dap_chain_hash_fast_to_str(&l_item_current->tx_hash_fast, l_hash_str, l_hash_str_size);
+        dap_chain_global_db_gr_del(l_hash_str, l_gdb_group);
+        DAP_DELETE(l_item_current);
+    }
+    DAP_DELETE(l_gdb_group);
+
+    // delete threshold txs
+    l_gdb_group = dap_chain_ledger_get_gdb_group(a_ledger, "thres_txs");
+    HASH_ITER(hh, l_ledger_priv->treshold_txs, l_item_current, l_item_tmp) {
+        HASH_DEL(l_ledger_priv->treshold_txs, l_item_current);
+        dap_chain_hash_fast_to_str(&l_item_current->tx_hash_fast, l_hash_str, l_hash_str_size);
+        dap_chain_global_db_gr_del(l_hash_str, l_gdb_group);
+        DAP_DELETE(l_item_current->tx);
+        DAP_DELETE(l_item_current);
+    }
+    DAP_DELETE(l_gdb_group);
+
+    // delete balances
+    dap_ledger_wallet_balance_t *l_balance_current, *l_balance_tmp;
+    l_gdb_group = dap_chain_ledger_get_gdb_group(a_ledger, "balances");
+    HASH_ITER(hh, l_ledger_priv->balance_accounts, l_balance_current, l_balance_tmp) {
+        HASH_DEL(l_ledger_priv->balance_accounts, l_balance_current);
+        dap_chain_global_db_gr_del(l_balance_current->key, l_gdb_group);
+        DAP_DELETE(l_balance_current->key);
+        DAP_DELETE(l_balance_current);
     }
+    DAP_DELETE(l_gdb_group);
+
+    // delete threshold emissions
+    dap_chain_ledger_token_emission_item_t *l_emission_current, *l_emission_tmp;
+    char *l_emissions_gdb_group = dap_chain_ledger_get_gdb_group(a_ledger, "emissions");
+    HASH_ITER(hh, l_ledger_priv->treshold_emissions, l_emission_current, l_emission_tmp) {
+        HASH_DEL(l_ledger_priv->treshold_emissions, l_emission_current);
+        dap_chain_hash_fast_to_str(&l_emission_current->datum_token_emission_hash, l_hash_str, l_hash_str_size);
+        dap_chain_global_db_gr_del(l_hash_str, l_emissions_gdb_group);
+        DAP_DELETE(l_emission_current->datum_token_emission);
+        DAP_DELETE(l_emission_current);
+    }
+
+    // delete tokens & its emissions
+    dap_chain_ledger_token_item_t *l_token_current, *l_token_tmp;
+    l_gdb_group = dap_chain_ledger_get_gdb_group(a_ledger, "tokens");
+    HASH_ITER(hh, l_ledger_priv->tokens, l_token_current, l_token_tmp) {
+        HASH_DEL(l_ledger_priv->tokens, l_token_current);
+        HASH_ITER(hh, l_token_current->token_emissions, l_emission_current, l_emission_tmp) {
+            HASH_DEL(l_token_current->token_emissions, l_emission_current);
+            dap_chain_hash_fast_to_str(&l_emission_current->datum_token_emission_hash, l_hash_str, l_hash_str_size);
+            dap_chain_global_db_gr_del(l_hash_str, l_emissions_gdb_group);
+            DAP_DELETE(l_emission_current->datum_token_emission);
+            DAP_DELETE(l_emission_current);
+        }
+        dap_chain_global_db_gr_del(l_token_current->ticker, l_gdb_group);
+        DAP_DELETE(l_token_current->datum_token);
+        pthread_rwlock_destroy(&l_token_current->token_emissions_rwlock);
+        DAP_DELETE(l_token_current);
+    }
+    DAP_DELETE(l_gdb_group);
+    DAP_DELETE(l_emissions_gdb_group);
+
     pthread_rwlock_unlock(&l_ledger_priv->ledger_rwlock);
+    pthread_rwlock_unlock(&l_ledger_priv->tokens_rwlock);
+    pthread_rwlock_unlock(&l_ledger_priv->treshold_emissions_rwlock);
+    pthread_rwlock_unlock(&l_ledger_priv->treshold_txs_rwlock);
 }
 
 /**
@@ -1370,16 +1623,7 @@ void dap_chain_ledger_purge(dap_ledger_t *a_ledger)
  */
 _dap_int128_t dap_chain_ledger_count(dap_ledger_t *a_ledger)
 {
-    _dap_int128_t l_ret = 0;
-    dap_ledger_private_t *l_ledger_priv = PVT(a_ledger);
-    dap_chain_ledger_tx_item_t *l_iter_current, *l_item_tmp;
-    pthread_rwlock_wrlock(&l_ledger_priv->ledger_rwlock);
-    HASH_ITER(hh, l_ledger_priv->ledger_items , l_iter_current, l_item_tmp)
-    {
-        l_ret++;
-    }
-    pthread_rwlock_unlock(&l_ledger_priv->ledger_rwlock);
-    return l_ret;
+    return HASH_COUNT(PVT(a_ledger)->ledger_items);
 }
 
 /**
@@ -1397,17 +1641,17 @@ uint64_t dap_chain_ledger_count_from_to(dap_ledger_t * a_ledger, time_t a_ts_fro
     pthread_rwlock_wrlock(&l_ledger_priv->ledger_rwlock);
     if ( a_ts_from && a_ts_to) {
         HASH_ITER(hh, l_ledger_priv->ledger_items , l_iter_current, l_item_tmp){
-            if ( l_iter_current->ts_created >= a_ts_from && l_iter_current->ts_created <= a_ts_to )
+            if ( l_iter_current->cache_data.ts_created >= a_ts_from && l_iter_current->cache_data.ts_created <= a_ts_to )
             l_ret++;
         }
     } else if ( a_ts_to ){
         HASH_ITER(hh, l_ledger_priv->ledger_items , l_iter_current, l_item_tmp){
-            if ( l_iter_current->ts_created <= a_ts_to )
+            if ( l_iter_current->cache_data.ts_created <= a_ts_to )
             l_ret++;
         }
     } else if ( a_ts_from ){
         HASH_ITER(hh, l_ledger_priv->ledger_items , l_iter_current, l_item_tmp){
-            if ( l_iter_current->ts_created >= a_ts_from )
+            if ( l_iter_current->cache_data.ts_created >= a_ts_from )
             l_ret++;
         }
     }else {
@@ -1512,7 +1756,7 @@ uint64_t dap_chain_ledger_calc_balance_full(dap_ledger_t *a_ledger, const dap_ch
             if (l_type == TX_ITEM_TYPE_OUT) {
                 const dap_chain_tx_out_t *l_tx_out = (const dap_chain_tx_out_t*) l_list_tmp->data;
                 // Check for token name
-                if (!strcmp(a_token_ticker, l_iter_current->token_tiker))
+                if (!strcmp(a_token_ticker, l_iter_current->cache_data.token_tiker))
                 {   // if transaction has the out item with requested addr
                     if (!memcmp(a_addr, &l_tx_out->addr, sizeof(dap_chain_addr_t))) {
                         // if 'out' item not used & transaction is valid
@@ -1563,7 +1807,8 @@ static dap_chain_ledger_tx_item_t* tx_item_find_by_addr(dap_ledger_t *a_ledger,
     HASH_ITER(hh, l_ledger_priv->ledger_items , l_iter_current, l_item_tmp)
     {
         // If a_token is setup we check if its not our token - miss it
-        if (a_token && *l_iter_current->token_tiker && dap_strcmp(l_iter_current->token_tiker, a_token))
+        if (a_token && *l_iter_current->cache_data.token_tiker &&
+                dap_strcmp(l_iter_current->cache_data.token_tiker, a_token))
             continue;
         // Now work with it
         dap_chain_datum_tx_t *l_tx = l_iter_current->tx;
@@ -1715,7 +1960,7 @@ dap_chain_datum_tx_t* dap_chain_ledger_tx_cache_find_out_cond(dap_ledger_t *a_le
             l_cur_tx = l_tx_tmp;
             memcpy(a_tx_first_hash, l_tx_hash_tmp, sizeof(dap_chain_hash_fast_t));
             if (a_token_ticker) {
-                strcpy(a_token_ticker, l_iter_current->token_tiker);
+                strcpy(a_token_ticker, l_iter_current->cache_data.token_tiker);
             }
             break;
         }
diff --git a/modules/chain/include/dap_chain.h b/modules/chain/include/dap_chain.h
index 86f4782d85..ed3f73afb2 100644
--- a/modules/chain/include/dap_chain.h
+++ b/modules/chain/include/dap_chain.h
@@ -117,6 +117,7 @@ typedef struct dap_chain{
 
     dap_chain_callback_new_cfg_t callback_created;
     dap_chain_callback_t callback_delete;
+    dap_chain_callback_t callback_purge;
 
     dap_chain_callback_atom_t callback_atom_add;
     dap_chain_callback_atom_verify_t callback_atom_verify;
diff --git a/modules/chain/include/dap_chain_ledger.h b/modules/chain/include/dap_chain_ledger.h
index 29b7b7e9f0..14fd1a61bf 100644
--- a/modules/chain/include/dap_chain_ledger.h
+++ b/modules/chain/include/dap_chain_ledger.h
@@ -38,6 +38,7 @@
 #include "dap_chain_datum_tx_items.h"
 
 typedef struct dap_ledger {
+    char *net_name;
     void *_internal;
 } dap_ledger_t;
 
@@ -52,7 +53,7 @@ typedef bool (* dap_chain_ledger_verificator_callback_t)(dap_chain_tx_out_cond_t
 // Check the double spending  in all cells
 #define DAP_CHAIN_LEDGER_CHECK_CELLS_DS          0x0100
 
-dap_ledger_t* dap_chain_ledger_create(uint16_t a_check_flags);
+dap_ledger_t* dap_chain_ledger_create(uint16_t a_check_flags, char *a_net_name);
 
 // Remove dap_ledger_t structure
 void dap_chain_ledger_handle_free(dap_ledger_t *a_ledger);
@@ -67,19 +68,28 @@ void dap_chain_ledger_set_local_cell_id(dap_ledger_t *a_ledger, dap_chain_cell_i
  * @param a_tx
  * @return
  */
-static inline dap_chain_hash_fast_t* dap_chain_node_datum_tx_calc_hash(dap_chain_datum_tx_t *a_tx)
+DAP_STATIC_INLINE dap_chain_hash_fast_t* dap_chain_node_datum_tx_calc_hash(dap_chain_datum_tx_t *a_tx)
 {
     dap_chain_hash_fast_t *tx_hash = DAP_NEW_Z(dap_chain_hash_fast_t);
     dap_hash_fast(a_tx, dap_chain_datum_tx_get_size(a_tx), tx_hash);
     return tx_hash;
 }
 
+DAP_STATIC_INLINE char *dap_chain_ledger_get_gdb_group(dap_ledger_t *a_ledger, const char *a_suffix)
+{
+   if (a_ledger) {
+       return dap_strdup_printf("local.ledger-cahce.%s.%s", a_ledger->net_name, a_suffix);
+   }
+   return NULL;
+}
+
 /**
  * Add new transaction to the cache
  *
  * return 1 OK, -1 error
  */
 int dap_chain_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx);
+int dap_chain_ledger_tx_load(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx);
 
 
 int dap_chain_ledger_tx_add_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx);
@@ -97,6 +107,7 @@ int dap_chain_ledger_token_ticker_check(dap_ledger_t * a_ledger, const char *a_t
  */
 
 int dap_chain_ledger_token_add(dap_ledger_t * a_ledger,dap_chain_datum_token_t *a_token, size_t a_token_size);
+int dap_chain_ledger_token_load(dap_ledger_t * a_ledger,dap_chain_datum_token_t *a_token, size_t a_token_size);
 int dap_chain_ledger_token_decl_add_check(dap_ledger_t * a_ledger,dap_chain_datum_token_t *a_token);
 
 /**
@@ -104,6 +115,8 @@ int dap_chain_ledger_token_decl_add_check(dap_ledger_t * a_ledger,dap_chain_datu
  */
 int dap_chain_ledger_token_emission_add(dap_ledger_t *a_ledger,
         const dap_chain_datum_token_emission_t *a_token_emission, size_t a_token_emission_size);
+int dap_chain_ledger_token_emission_load(dap_ledger_t *a_ledger,
+        const dap_chain_datum_token_emission_t *a_token_emission, size_t a_token_emission_size);
 
 // Check if it addable
 int dap_chain_ledger_token_emission_add_check(dap_ledger_t *a_ledger,
diff --git a/modules/chain/include/dap_chain_pvt.h b/modules/chain/include/dap_chain_pvt.h
index d18e434a8f..4af990d5fc 100644
--- a/modules/chain/include/dap_chain_pvt.h
+++ b/modules/chain/include/dap_chain_pvt.h
@@ -41,6 +41,8 @@ typedef struct dap_chain_pvt
 {
     dap_chain_t * chain;
     char * file_storage_dir;
+    char * cs_name;
+    int celled;
 } dap_chain_pvt_t;
 
 #define DAP_CHAIN_PVT(a) ((dap_chain_pvt_t *) a->_pvt  )
diff --git a/modules/channel/chain/dap_stream_ch_chain.c b/modules/channel/chain/dap_stream_ch_chain.c
index 1d6eb6a2d6..b55fe1269d 100644
--- a/modules/channel/chain/dap_stream_ch_chain.c
+++ b/modules/channel/chain/dap_stream_ch_chain.c
@@ -174,6 +174,9 @@ bool s_sync_chains_callback(dap_proc_thread_t *a_thread, void *a_arg)
                 l_ch_chain->request_net_id, l_ch_chain->request_chain_id,
                 l_ch_chain->request_cell_id, &l_request, sizeof(l_request));
         l_ch_chain->state = CHAIN_STATE_IDLE;
+        if (l_ch_chain->callback_notify_packet_out)
+            l_ch_chain->callback_notify_packet_out(l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_CHAINS,
+                                                    NULL, 0, l_ch_chain->callback_notify_arg);
     }
     DAP_DELETE(l_lasts);
     DAP_DELETE(l_iter);
diff --git a/modules/consensus/none/dap_chain_cs_none.c b/modules/consensus/none/dap_chain_cs_none.c
index f68ce988c1..42da697341 100644
--- a/modules/consensus/none/dap_chain_cs_none.c
+++ b/modules/consensus/none/dap_chain_cs_none.c
@@ -65,8 +65,6 @@ typedef struct dap_chain_gdb_private
 
 #define PVT(a) ( (a) ? (dap_chain_gdb_private_t* ) (a)->_internal : NULL)
 
-static int dap_chain_gdb_ledger_load(dap_chain_gdb_t *a_gdb, dap_chain_net_t *a_net);
-
 // Atomic element organization callbacks
 static dap_chain_atom_verify_res_t s_chain_callback_atom_add(dap_chain_t * a_chain, dap_chain_atom_ptr_t, size_t); //    Accept new event in gdb
 static dap_chain_atom_verify_res_t s_chain_callback_atom_verify(dap_chain_t * a_chain, dap_chain_atom_ptr_t, size_t); //    Verify new event in gdb
@@ -138,10 +136,16 @@ static void s_history_callback_notify(void * a_arg, const char a_op_code, const
         dap_chain_net_t *l_net = dap_chain_net_by_id( l_gdb->chain->net_id);
         log_it(L_DEBUG,"%s.%s: op_code='%c' group=\"%s\" key=\"%s\" value_size=%u",l_net->pub.name,
                l_gdb->chain->name, a_op_code, a_group, a_key, a_value_size);
+        dap_chain_node_mempool_autoproc_notify((void *)l_net, a_op_code, a_prefix, a_group, a_key, a_value, a_value_size);
         dap_chain_net_sync_gdb_broadcast((void *)l_net, a_op_code, a_prefix, a_group, a_key, a_value, a_value_size);
     }
 }
 
+static void s_dap_chain_gdb_callback_purge(dap_chain_t *a_chain)
+{
+    PVT(DAP_CHAIN_GDB(a_chain))->is_load_mode = true;
+}
+
 /**
  * @brief dap_chain_gdb_new
  * @param a_chain
@@ -176,10 +180,10 @@ int dap_chain_gdb_new(dap_chain_t * a_chain, dap_config_t * a_chain_cfg)
 
     // load ledger
     l_gdb_priv->is_load_mode = true;
-    dap_chain_gdb_ledger_load(l_gdb, l_net);
-    l_gdb_priv->is_load_mode = false;
+    dap_chain_gdb_ledger_load(l_gdb_priv->group_datums, a_chain);
 
     a_chain->callback_delete = dap_chain_gdb_delete;
+    a_chain->callback_purge = s_dap_chain_gdb_callback_purge;
 
     // Atom element callbacks
     a_chain->callback_atom_add = s_chain_callback_atom_add; // Accept new element in chain
@@ -258,24 +262,17 @@ const char* dap_chain_gdb_get_group(dap_chain_t * a_chain)
  *
  * return 0 if OK otherwise  negative error code
  */
-static int dap_chain_gdb_ledger_load(dap_chain_gdb_t *a_gdb, dap_chain_net_t *a_net)
+int dap_chain_gdb_ledger_load(char *a_gdb_group, dap_chain_t *a_chain)
 {
-    dap_chain_gdb_private_t *l_gdb_priv = PVT(a_gdb);
-    // protect from reloading
-    if(dap_chain_ledger_count( a_net->pub.ledger ) > 0)
-        return 0;
-
     size_t l_data_size = 0;
-
-
     //  Read the entire database into an array of size bytes
-    dap_global_db_obj_t *data = dap_chain_global_db_gr_load(l_gdb_priv->group_datums , &l_data_size);
+    dap_global_db_obj_t *data = dap_chain_global_db_gr_load(a_gdb_group, &l_data_size);
     // make list of datums
     for(size_t i = 0; i < l_data_size; i++) {
-        s_chain_callback_atom_add(a_gdb->chain,data[i].value, data[i].value_len);
-
+        s_chain_callback_atom_add(a_chain, data[i].value, data[i].value_len);
     }
     dap_chain_global_db_objs_delete(data, l_data_size);
+    PVT(DAP_CHAIN_GDB(a_chain))->is_load_mode = false;
     return 0;
 }
 
@@ -322,11 +319,11 @@ static dap_chain_atom_verify_res_t s_chain_callback_atom_add(dap_chain_t * a_cha
     switch (l_datum->header.type_id) {
         case DAP_CHAIN_DATUM_TOKEN_DECL:{
             dap_chain_datum_token_t *l_token = (dap_chain_datum_token_t*) l_datum->data;
-            dap_chain_ledger_token_add(a_chain->ledger,l_token, l_datum->header.data_size);
+            dap_chain_ledger_token_load(a_chain->ledger,l_token, l_datum->header.data_size);
         }break;
         case DAP_CHAIN_DATUM_TOKEN_EMISSION: {
             dap_chain_datum_token_emission_t *l_token_emission = (dap_chain_datum_token_emission_t*) l_datum->data;
-            dap_chain_ledger_token_emission_add(a_chain->ledger, l_token_emission, l_datum->header.data_size);
+            dap_chain_ledger_token_emission_load(a_chain->ledger, l_token_emission, l_datum->header.data_size);
         }break;
         case DAP_CHAIN_DATUM_TX:{
             dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*) l_datum->data;
@@ -335,7 +332,7 @@ static dap_chain_atom_verify_res_t s_chain_callback_atom_add(dap_chain_t * a_cha
             //if(dap_chain_datum_tx_get_size(l_tx) == l_datum->header.data_size){
 
             // don't save bad transactions to base
-            if(dap_chain_ledger_tx_add(a_chain->ledger, l_tx) != 1)
+            if(dap_chain_ledger_tx_load(a_chain->ledger, l_tx) != 1)
                 return ATOM_REJECT;
             //}else
             //    return -2;
diff --git a/modules/consensus/none/include/dap_chain_cs_none.h b/modules/consensus/none/include/dap_chain_cs_none.h
index e40ae2184e..adf5cd6f00 100644
--- a/modules/consensus/none/include/dap_chain_cs_none.h
+++ b/modules/consensus/none/include/dap_chain_cs_none.h
@@ -39,4 +39,4 @@ int dap_chain_gdb_init(void);
 int dap_chain_gdb_new(dap_chain_t * a_chain, dap_config_t * a_chain_cfg);
 void dap_chain_gdb_delete(dap_chain_t * a_chain);
 const char* dap_chain_gdb_get_group(dap_chain_t * a_chain);
-
+int dap_chain_gdb_ledger_load(char *a_gdb_group, dap_chain_t *a_chain);
diff --git a/modules/net/CMakeLists.txt b/modules/net/CMakeLists.txt
index e5d0557256..104d304db2 100644
--- a/modules/net/CMakeLists.txt
+++ b/modules/net/CMakeLists.txt
@@ -40,12 +40,12 @@ add_library(${PROJECT_NAME} STATIC ${DAP_CHAIN_NET_SRCS} ${DAP_CHAIN_NET_HEADERS
 
 if(WIN32)
   target_link_libraries(dap_chain_net dap_core dap_crypto dap_client dap_stream_ch_chain dap_stream_ch_chain_net dap_stream_ch_chain_net_srv dap_chain dap_chain_crypto dap_chain_wallet dap_chain_net_srv
-                            dap_chain_mempool dap_chain_global_db dap_chain_net_srv_stake)
+                            dap_chain_mempool dap_chain_global_db dap_chain_net_srv_stake dap_chain_cs_none)
 endif()
 
 if(UNIX)
     target_link_libraries(${PROJECT_NAME} dap_core dap_crypto dap_client dap_stream_ch_chain dap_stream_ch_chain_net dap_stream_ch_chain_net_srv dap_chain
-      dap_chain_wallet dap_chain_net_srv dap_chain_mempool dap_chain_global_db dap_chain_net_srv_stake
+      dap_chain_wallet dap_chain_net_srv dap_chain_mempool dap_chain_global_db dap_chain_net_srv_stake dap_chain_cs_none
       resolv
       )
 endif()
diff --git a/modules/net/dap_chain_net.c b/modules/net/dap_chain_net.c
index 586c36a59f..7e6256f1bd 100644
--- a/modules/net/dap_chain_net.c
+++ b/modules/net/dap_chain_net.c
@@ -66,10 +66,12 @@
 #include "dap_enc_http.h"
 #include "dap_chain_common.h"
 #include "dap_chain_net.h"
+#include "dap_chain_pvt.h"
 #include "dap_chain_node_client.h"
 #include "dap_chain_node_cli.h"
 #include "dap_chain_node_cli_cmd.h"
 #include "dap_chain_ledger.h"
+#include "dap_chain_cs_none.h"
 
 #include "dap_chain_global_db.h"
 #include "dap_chain_global_db_remote.h"
@@ -865,7 +867,8 @@ int dap_chain_net_init()
             "\tPrint list of authority cetificates from GDB group\n"
         "net -net <chain net name> ca del -hash <cert hash> [-H hex|base58(default)]\n"
             "\tDelete certificate from list of authority cetificates in GDB group by it's hash\n"
-                                        );
+        "net -net <chain net name> ledger reload\n"
+            "\tPurge the cache of chain net ledger and recalculate it from chain file\n"                                        );
     s_seed_mode = dap_config_get_item_bool_default(g_config,"general","seed_mode",false);
     dap_chain_global_db_add_history_group_prefix("global", GROUP_LOCAL_HISTORY);
 
@@ -1004,12 +1007,14 @@ static int s_cli_net( int argc, char **argv, void *arg_func, char **a_str_reply)
         const char *l_get_str = NULL;
         const char *l_stats_str = NULL;
         const char *l_ca_str = NULL;
+        const char *l_ledger_str = NULL;
         dap_chain_node_cli_find_option_val(argv, arg_index, argc, "sync", &l_sync_str);
         dap_chain_node_cli_find_option_val(argv, arg_index, argc, "link", &l_links_str);
         dap_chain_node_cli_find_option_val(argv, arg_index, argc, "go", &l_go_str);
         dap_chain_node_cli_find_option_val(argv, arg_index, argc, "get", &l_get_str);
         dap_chain_node_cli_find_option_val(argv, arg_index, argc, "stats", &l_stats_str);
         dap_chain_node_cli_find_option_val(argv, arg_index, argc, "ca", &l_ca_str);
+        dap_chain_node_cli_find_option_val(argv, arg_index, argc, "ledger", &l_ledger_str);
 
         if ( l_stats_str ){
             if ( strcmp(l_stats_str,"tx") == 0 ) {
@@ -1245,6 +1250,19 @@ static int s_cli_net( int argc, char **argv, void *arg_func, char **a_str_reply)
                                                   "Subcommand \"ca\" requires one of parameter: add, list, del\n");
                 ret = -5;
             }
+        } else if (l_ledger_str && !strcmp(l_ledger_str, "reload")) {
+            dap_chain_ledger_purge(l_net->pub.ledger);
+            dap_chain_t *l_chain;
+            DL_FOREACH(l_net->pub.chains, l_chain) {
+                if (l_chain->callback_purge) {
+                    l_chain->callback_purge(l_chain);
+                }
+                if (!strcmp(DAP_CHAIN_PVT(l_chain)->cs_name, "none")) {
+                    dap_chain_gdb_ledger_load((char *)dap_chain_gdb_get_group(l_chain), l_chain);
+                } else {
+                    dap_chain_load_all(l_chain);
+                }
+            }
         } else {
             dap_chain_node_cli_set_reply_text(a_str_reply,"Command requires one of subcomand: sync, links\n");
             ret = -1;
@@ -1381,7 +1399,7 @@ int s_net_load(const char * a_net_name, uint16_t a_acl_idx)
                 l_ledger_flags |= DAP_CHAIN_LEDGER_CHECK_LOCAL_DS;
         }
         // init LEDGER model
-        l_net->pub.ledger = dap_chain_ledger_create(l_ledger_flags);
+        l_net->pub.ledger = dap_chain_ledger_create(l_ledger_flags, l_net->pub.name);
         // Check if seed nodes are present in local db alias
         char **l_seed_aliases = dap_config_get_array_str( l_cfg , "general" ,"seed_nodes_aliases"
                                                              ,&PVT(l_net)->seed_aliases_count);
diff --git a/modules/net/srv/dap_chain_net_srv_order.c b/modules/net/srv/dap_chain_net_srv_order.c
index 8aba700a30..869461dc4f 100644
--- a/modules/net/srv/dap_chain_net_srv_order.c
+++ b/modules/net/srv/dap_chain_net_srv_order.c
@@ -524,17 +524,17 @@ static void s_srv_order_callback_notify(void *a_arg, const char a_op_code, const
     if (!strcmp(a_group, l_gdb_group_str)) {
         dap_chain_net_srv_order_t *l_order = (dap_chain_net_srv_order_t *)a_value;
         if (l_order->version == 1) {
-            dap_chain_global_db_gr_del((char *)a_key, a_group);
+            dap_chain_global_db_gr_del(dap_strdup(a_key), a_group);
         } else {
             dap_sign_t *l_sign = (dap_sign_t *)&l_order->ext[l_order->ext_size];
             if (!dap_sign_verify(l_sign, l_order, sizeof(dap_chain_net_srv_order_t) + l_order->ext_size)) {
-                dap_chain_global_db_gr_del((char *)a_key, a_group);
+                dap_chain_global_db_gr_del(dap_strdup(a_key), a_group);
                 DAP_DELETE(l_gdb_group_str);
                 return;
             }
             dap_chain_hash_fast_t l_pkey_hash;
             if (!dap_sign_get_pkey_hash(l_sign, &l_pkey_hash)) {
-                dap_chain_global_db_gr_del((char *)a_key, a_group);
+                dap_chain_global_db_gr_del(dap_strdup(a_key), a_group);
                 DAP_DELETE(l_gdb_group_str);
                 return;
             }
@@ -542,7 +542,7 @@ static void s_srv_order_callback_notify(void *a_arg, const char a_op_code, const
             dap_chain_addr_fill(&l_addr, l_sign->header.type, &l_pkey_hash, l_net->pub.id);
             uint64_t l_solvency = dap_chain_ledger_calc_balance(l_net->pub.ledger, &l_addr, l_order->price_ticker);
             if (l_solvency < l_order->price && !dap_chain_net_srv_stake_key_delegated(&l_addr)) {
-                dap_chain_global_db_gr_del((char *)a_key, a_group);
+                dap_chain_global_db_gr_del(dap_strdup(a_key), a_group);
             }
         }
         DAP_DELETE(l_gdb_group_str);
diff --git a/modules/service/xchange/dap_chain_net_srv_xchange.c b/modules/service/xchange/dap_chain_net_srv_xchange.c
index b7bd507149..6336a36690 100644
--- a/modules/service/xchange/dap_chain_net_srv_xchange.c
+++ b/modules/service/xchange/dap_chain_net_srv_xchange.c
@@ -653,7 +653,6 @@ static int s_cli_srv_xchange_price(int a_argc, char **a_argv, int a_arg_index, c
                 }
                 DAP_DELETE(l_order_hash_str);
                 DAP_DELETE(l_price->wallet_str);
-                DAP_DELETE(l_price->key_ptr);
                 DAP_DELETE(l_price);
                 if (!l_str_reply->len) {
                     dap_string_append(l_str_reply, "Price successfully removed");
@@ -715,7 +714,7 @@ static int s_cli_srv_xchange_price(int a_argc, char **a_argv, int a_arg_index, c
                     return -14;
                 }
                 HASH_DEL(s_srv_xchange->pricelist, l_price);
-                dap_chain_global_db_gr_del(l_price->key_ptr, GROUP_LOCAL_XCHANGE);
+                dap_chain_global_db_gr_del(dap_strdup(l_price->key_ptr), GROUP_LOCAL_XCHANGE);
                 bool l_ret = s_xchage_tx_invalidate(l_price, l_wallet); // may be changed to old price later
                 dap_chain_wallet_close(l_wallet);
                 if (!l_ret) {
diff --git a/modules/type/dag/dap_chain_cs_dag.c b/modules/type/dag/dap_chain_cs_dag.c
index 6385aacf84..e9b1d4f5fa 100644
--- a/modules/type/dag/dap_chain_cs_dag.c
+++ b/modules/type/dag/dap_chain_cs_dag.c
@@ -78,6 +78,8 @@ typedef struct dap_chain_cs_dag_pvt {
 
 #define PVT(a) ((dap_chain_cs_dag_pvt_t *) a->_pvt )
 
+static void s_dap_chain_cs_dag_purge(dap_chain_t *a_chain);
+
 // Atomic element organization callbacks
 static dap_chain_atom_verify_res_t s_chain_callback_atom_add(dap_chain_t * a_chain, dap_chain_atom_ptr_t , size_t);                      //    Accept new event in dag
 static dap_chain_atom_verify_res_t s_chain_callback_atom_verify(dap_chain_t * a_chain, dap_chain_atom_ptr_t , size_t);                   //    Verify new event in dag
@@ -168,6 +170,7 @@ int dap_chain_cs_dag_new(dap_chain_t * a_chain, dap_config_t * a_chain_cfg)
     pthread_rwlock_init(& PVT(l_dag)->events_rwlock,NULL);
 
     a_chain->callback_delete = dap_chain_cs_dag_delete;
+    a_chain->callback_purge = s_dap_chain_cs_dag_purge;
 
     // Atom element callbacks
     a_chain->callback_atom_add = s_chain_callback_atom_add ;  // Accept new element in chain
@@ -227,6 +230,34 @@ int dap_chain_cs_dag_new(dap_chain_t * a_chain, dap_config_t * a_chain_cfg)
     return 0;
 }
 
+static void s_dap_chain_cs_dag_purge(dap_chain_t *a_chain)
+{
+    dap_chain_cs_dag_pvt_t *l_dag_pvt = PVT(DAP_CHAIN_CS_DAG(a_chain));
+    pthread_rwlock_wrlock(&l_dag_pvt->events_rwlock);
+    dap_chain_cs_dag_event_item_t *l_event_current, *l_event_tmp;
+    HASH_ITER(hh, l_dag_pvt->events, l_event_current, l_event_tmp) {
+        HASH_DEL(l_dag_pvt->events, l_event_current);
+        DAP_DELETE(l_event_current);
+    }
+    HASH_ITER(hh, l_dag_pvt->events_lasts_unlinked, l_event_current, l_event_tmp) {
+        HASH_DEL(l_dag_pvt->events_lasts_unlinked, l_event_current);
+        DAP_DELETE(l_event_current);
+    }
+    HASH_ITER(hh, l_dag_pvt->events_treshold, l_event_current, l_event_tmp) {
+        HASH_DEL(l_dag_pvt->events_treshold, l_event_current);
+        DAP_DELETE(l_event_current);
+    }
+    HASH_ITER(hh, l_dag_pvt->events_treshold_conflicted, l_event_current, l_event_tmp) {
+        HASH_DEL(l_dag_pvt->events_treshold_conflicted, l_event_current);
+        DAP_DELETE(l_event_current);
+    }
+    HASH_ITER(hh, l_dag_pvt->tx_events, l_event_current, l_event_tmp) {
+        HASH_DEL(l_dag_pvt->tx_events, l_event_current);
+        DAP_DELETE(l_event_current);
+    }
+    pthread_rwlock_unlock(&l_dag_pvt->events_rwlock);
+}
+
 /**
  * @brief dap_chain_cs_dag_delete
  * @param a_dag
@@ -234,6 +265,7 @@ int dap_chain_cs_dag_new(dap_chain_t * a_chain, dap_config_t * a_chain_cfg)
  */
 void dap_chain_cs_dag_delete(dap_chain_t * a_chain)
 {
+    s_dap_chain_cs_dag_purge(a_chain);
     dap_chain_cs_dag_t * l_dag = DAP_CHAIN_CS_DAG ( a_chain );
     pthread_rwlock_destroy(& PVT(l_dag)->events_rwlock);
 
@@ -251,12 +283,12 @@ static int s_dap_chain_add_atom_to_ledger(dap_chain_cs_dag_t * a_dag, dap_ledger
   switch (l_datum->header.type_id) {
     case DAP_CHAIN_DATUM_TOKEN_DECL: {
       dap_chain_datum_token_t *l_token = (dap_chain_datum_token_t*) l_datum->data;
-      return dap_chain_ledger_token_add(a_ledger, l_token, l_datum->header.data_size);
+      return dap_chain_ledger_token_load(a_ledger, l_token, l_datum->header.data_size);
     }
       break;
     case DAP_CHAIN_DATUM_TOKEN_EMISSION: {
       dap_chain_datum_token_emission_t *l_token_emission = (dap_chain_datum_token_emission_t*) l_datum->data;
-      return dap_chain_ledger_token_emission_add(a_ledger, l_token_emission, l_datum->header.data_size);
+      return dap_chain_ledger_token_emission_load(a_ledger, l_token_emission, l_datum->header.data_size);
     }
       break;
     case DAP_CHAIN_DATUM_TX: {
@@ -270,7 +302,7 @@ static int s_dap_chain_add_atom_to_ledger(dap_chain_cs_dag_t * a_dag, dap_ledger
       HASH_ADD(hh,PVT(a_dag)->tx_events,hash,sizeof (l_tx_event->hash),l_tx_event);
 
       // don't save bad transactions to base
-      if(dap_chain_ledger_tx_add(a_ledger, l_tx) != 1) {
+      if(dap_chain_ledger_tx_load(a_ledger, l_tx) != 1) {
         return -1;
       }
     }
-- 
GitLab