diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3c18deda471cd5aa2eb6246bb2a3b06ecd70edb3..ecaf5efeb98c0a8350b6ef22458a607f63885b8d 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,7 +6,7 @@ file(GLOB DAP_CHAIN_GLOBAL_DB_HDR *.h)
 
 add_library(${PROJECT_NAME} STATIC ${DAP_CHAIN_GLOBAL_DB_SRC} ${DAP_CHAIN_GLOBAL_DB_HDR})
 
-target_link_libraries(dap_chain_global_db dap_core dap_crypto dap_chain dap_chain_crypto ldb talloc tevent sqlite3)
+target_link_libraries(dap_chain_global_db dap_core dap_crypto dap_chain dap_chain_crypto ldb talloc tevent sqlite3 ${CMAKE_CURRENT_SOURCE_DIR}/libcuttdb.a)
 target_include_directories(dap_chain_global_db INTERFACE .)
 
 set(${PROJECT_NAME}_DEFINITIONS CACHE INTERNAL "${PROJECT_NAME}: Definitions" FORCE)
diff --git a/cuttdb.h b/cuttdb.h
new file mode 100644
index 0000000000000000000000000000000000000000..e3eeac9c969d42ca70e72936e667f006e0810abc
--- /dev/null
+++ b/cuttdb.h
@@ -0,0 +1,215 @@
+/*
+ *   CuttDB - a fast key-value storage engine
+ *
+ *
+ *   http://code.google.com/p/cuttdb/
+ *   
+ *   Copyright (c) 2012, Siyuan Fu.  All rights reserved.
+ *   Use and distribution licensed under the BSD license. 
+ *   See the LICENSE file for full text
+ *
+ *   Author: Siyuan Fu <fusiyuan2010@gmail.com>
+ *
+ */
+
+
+#ifndef _CUTTDB_H_
+#define _CUTTDB_H_
+#include <stdint.h>
+#include <stdbool.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct CDB CDB;
+typedef void (*CDB_ERRCALLBACK)(void *, int, const char *, int);
+typedef bool (*CDB_ITERCALLBACK)(void *, const char *, int, const char *, int, uint32_t, uint64_t);
+
+/* performance statistical information of an database instance */
+typedef struct {
+    /* number of records in db */
+    uint64_t rnum;
+    /* number of records in cache */
+    uint64_t rcnum;
+    /* number of pages in db */
+    uint64_t pnum;
+    /* number of pages in cache */
+    uint64_t pcnum;
+    /* cache hit of record cache */
+    uint64_t rchit;
+    /* cache miss of record cache */
+    uint64_t rcmiss;
+    /* cache hit of page cache */
+    uint64_t pchit;
+    /* cache miss of page cache */
+    uint64_t pcmiss;
+    /* average disk read latency */
+    uint32_t rlatcy;
+    /* average disk write latency */
+    uint32_t wlatcy;
+} CDBSTAT;
+
+/* options to open a database*/
+enum {
+    /* create an database if not exist */
+    CDB_CREAT = 0x1,
+    /* clean the database if already exist */
+    CDB_TRUNC = 0x2,
+    /* fill the cache when start up */
+    CDB_PAGEWARMUP = 0x4,
+};
+
+/* error codes */
+enum {
+    CDB_SUCCESS = 0,
+    CDB_NOTFOUND,
+    CDB_EXIST,
+    CDB_DIRNOEXIST,
+    CDB_OPENERR,
+    CDB_PIDEXIST,
+    CDB_DATAERRDAT,
+    CDB_DATAERRIDX,
+    CDB_WRITEERR,
+    CDB_READERR,
+    CDB_NOFID,
+    CDB_INTERNALERR,
+    CDB_DATAERRMETA,
+    CDB_MEMDBNOCACHE,
+};
+
+/* record insertion options */
+enum {
+    CDB_OVERWRITE = 0,
+    CDB_INSERTIFEXIST = 0x1,
+    CDB_INSERTIFNOEXIST = 0x2,
+    CDB_INSERTCACHE = 0x8,
+};
+
+/* if database path is CDB_MEMDB, records are never written to disk, they stay in cache only */
+#define CDB_MEMDB ":memory:"
+
+/*
+ WARNING: 
+
+ the library has auxiliary thread, which means do fork() after open a database will cause
+ unpredictable situation.
+*/
+
+/* create an cuttdb object, which should be freed by cdb_destory() */
+CDB *cdb_new();
+
+/* cdb_option() must be called before cdb_open()
+
+ the second parameter 'hsize' indicates the size of main hash table, which can't be
+ modified after the database be created. To get better performance, it is suggest to
+ set the 'hsize' to 10% - 1% of the total number of records. The default value 1 million
+ should be proper for about 100 million records. Too large or small of the value would
+ lead to drop in speed or waste of memory
+
+ the third parameter 'rcacheMB' indicates the size limit of record cache (measured by 
+ MegaBytes), every record in cache would have about 40 bytes overhead. 
+
+ the fourth parameter 'pcacheMB' indicates the size limit of index page cache (measured 
+ by MegaBytes). If a record is not in record cache, it will be read by only 1 disk seek
+ with enough page cache, or it have to make an extra disk seek to load the page. 
+ cuttdb will use about {10 * number of records} bytes to cache all index pages, which 
+ ensures fastest 'set' operation.
+
+ the default parameter is (_db, 1000000, 128, 1024)
+
+ return 0 if success, or -1 at failure. */
+int cdb_option(CDB *db, int hsize, int rcacheMB, int pcacheMB);
+
+/* Enable bloomfilter, size should be the estimated number of records in database 
+ must be called before cdb_open(),
+ The value is 100000 at minimum. Memory cost of bloomfilter is size/8 bytes */
+void cdb_option_bloomfilter(CDB *db, uint64_t size);
+
+/* this is an advanced parameter. It is the size for cuttdb making a read from disk.
+ CuttDB do not know the record size even if the index is in memory,
+ so at least a read with default size will performed while in cdb_get().
+ The value is recommended to be larger than the size of most records in database,
+ unless the records are mostly larger than tens of KB.
+ If the value is much larger than recommended, it will be a waste of computing. 
+ The value can only be 65536 at maximum, 1024 at minimum */
+void cdb_option_areadsize(CDB *db, uint32_t size);
+
+/* open an database, 'file' should be an existing directory, or CDB_MEMDB for temporary store,
+   'mode' should be combination of CDB_CREAT / CDB_TRUNC / CDB_PAGEWARMUP 
+   CDB_PAGEWARMUP means to warm up page cache while opening 
+   If there is a file called 'force_recovery' in the data directory, even if it might be made by 'touch force_recovery',
+   a force recovery will happen to rebuild the index (be aware that some deleted records would reappear after this)
+ */
+int cdb_open(CDB *db, const char *file, int mode);
+
+
+/* simplified cdb_set2, insert a record with CDB_OVERWRITE and never expire */
+int cdb_set(CDB *db, const char *key, int ksize, const char *val, int vsize);
+
+/* set a record by 'key' and 'value', 
+   opt could be bit combination of CDB_INSERTCACHE and one in {CDB_INSERTIFEXIST, CDB_INSERTNOEXIST,
+   CDB_OVERWRITE}
+   expire is the time for the record be valid, measured by second. 0 means never expire.
+   return 0 if success, or -1 at failure. */
+int cdb_set2(CDB *db, const char *key, int ksize, const char *val, int vsize, int opt, int expire);
+
+
+/* get an record by 'key', the value will be allocated and passed out by 'val', its size is
+   'vsize'.  return 0 if success, or -1 at failure. */
+int cdb_get(CDB *db, const char *key, int ksize, void **val, int *vsize);
+
+
+/* the val got by cdb_get should be freed by this for safety.
+   If there is more than one memory allocator */
+void cdb_free_val(void **val);
+
+
+/* delete an record by 'key'. However ,the space of the record would not be recycled. 
+   'vsize'.  return 0 if success, or -1 at failure. */
+int cdb_del(CDB *db, const char *key, int ksize);
+
+
+/* create a new iterator begins at given operation id */
+void *cdb_iterate_new(CDB *db, uint64_t oid);
+
+/* iterate through the database with a callback, the function would stop if callback returned false
+   The callback should accept key, ksize, value, vsize, expire time, oid
+   Returns the number of records have been visited */
+uint64_t cdb_iterate(CDB *db, CDB_ITERCALLBACK itcb, void *arg, void *iter);
+
+/* destroy the iterator */
+void cdb_iterate_destroy(CDB *db, void *iter);
+
+/* get the current statistic information of db. 'stat' should be the struct already allocated.
+   if 'stat' is NULL, the statistic will be reset to zero. */
+void cdb_stat(CDB *db, CDBSTAT *stat);
+
+
+/* close the database. IT MUST BE CALLED BEFORE PROGRAM EXITS TO ENSURE DATA COMPLETION */
+int cdb_close(CDB *db);
+
+
+/* close the database if it opened, and free the object */
+int cdb_destroy(CDB *db);
+
+
+/* get last error number in current thread */
+int cdb_errno(CDB *db);
+
+
+/* get the description of an error number */
+const char *cdb_errmsg(int ecode);
+
+
+/* set callback when error happened, 'cdb_deferrorcb' is optional, which shows the error to stderr */
+void cdb_seterrcb(CDB *db, CDB_ERRCALLBACK errcb, void *arg);
+
+/* a possible error callback */
+void cdb_deferrorcb(void *arg, int errno, const char *file, int line);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/dap_chain_global_db.c b/dap_chain_global_db.c
index 71b053b8efe5176800210862335b751b3b2565aa..3fa609bd65615e411a2204d39d44fc6b9b24e136 100755
--- a/dap_chain_global_db.c
+++ b/dap_chain_global_db.c
@@ -139,8 +139,8 @@ void dap_chain_global_db_objs_delete(dap_global_db_obj_t **objs)
 int dap_chain_global_db_init(dap_config_t * g_config)
 {
     const char *l_storage_path = dap_config_get_item_str(g_config, "resources", "dap_global_db_path");
-    const char *l_driver_name = dap_config_get_item_str_default(g_config, "resources", "dap_global_db_driver",
-            "sqlite");
+    //const char *l_driver_name = dap_config_get_item_str_default(g_config, "resources", "dap_global_db_driver", "sqlite");
+    const char *l_driver_name = dap_config_get_item_str_default(g_config, "resources", "dap_global_db_driver", "cdb");
     lock();
     int res = dap_db_driver_init(l_driver_name, l_storage_path);
     //int res = dap_db_init(a_storage_path);
diff --git a/dap_chain_global_db_driver.c b/dap_chain_global_db_driver.c
index bb456a1f4595abc27f16b436d07fa2fb7d9fd605..612d04c02d6bd12517d0cdb3215012a5c269d4d4 100755
--- a/dap_chain_global_db_driver.c
+++ b/dap_chain_global_db_driver.c
@@ -34,6 +34,7 @@
 #include "dap_hash.h"
 
 #include "dap_chain_global_db_driver_sqlite.h"
+#include "dap_chain_global_db_driver_cdb.h"
 #include "dap_chain_global_db_driver.h"
 
 #define LOG_TAG "db_driver"
@@ -79,6 +80,8 @@ int dap_db_driver_init(const char *a_driver_name, const char *a_filename_db)
         l_ret = -1;
     if(!dap_strcmp(s_used_driver, "sqlite"))
         l_ret = dap_db_driver_sqlite_init(a_filename_db, &s_drv_callback);
+    if(!dap_strcmp(s_used_driver, "cdb"))
+        l_ret = dap_db_driver_cdb_init(a_filename_db, &s_drv_callback);
     if(!l_ret) {
         pthread_condattr_t l_condattr;
         pthread_condattr_init(&l_condattr);
diff --git a/dap_chain_global_db_driver_cdb.c b/dap_chain_global_db_driver_cdb.c
new file mode 100644
index 0000000000000000000000000000000000000000..a16afebe43e4cb7bbed867ed44606848b107a6d8
--- /dev/null
+++ b/dap_chain_global_db_driver_cdb.c
@@ -0,0 +1,370 @@
+/*
+ * Authors:
+ * Konstantin Papizh <konstantin.papizh@demlabs.net>
+ * DeM Labs Inc.   https://demlabs.net
+ * Kelvin Project https://github.com/kelvinblockchain
+ * Copyright  (c) 2019
+ * All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+ DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ DAP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <uthash.h>
+#include "dap_common.h"
+#include "dap_hash.h"
+#include "dap_strfuncs.h"
+#include "dap_chain_global_db_driver_cdb.h"
+
+#define LOG_TAG "db_cdb"
+
+#define uint64_size sizeof(uint64_t)
+
+typedef struct _obj_arg {
+    pdap_store_obj_t o;
+    uint64_t q;
+    uint64_t n;
+} obj_arg, *pobj_arg;
+
+typedef struct _cdb_instance {
+    CDB *cdb;
+    char *local_group;
+    UT_hash_handle hh;
+} cdb_instance, *pcdb_instance;
+
+static char *s_cdb_path = NULL;
+static pcdb_instance s_cdb = NULL;
+
+static inline void dap_cdb_uint_to_hex(char *arr, uint64_t val, short size) {
+    short i = 0;
+    for (i = 0; i < size; ++i) {
+        arr[i] = (char)(((uint64_t) val >> (8 * (size - 1 - i))) & 0xFFu);
+    }
+}
+
+static inline uint64_t dap_cdb_hex_to_uint(char *arr, short size) {
+    uint64_t val = 0;
+    short i = 0;
+    for (i = 0; i < size; ++i){
+        uint8_t byte = *arr++;
+        if (byte >= 'a' && byte <='f'){
+            byte = byte - 'a' + 10;
+        } else if (byte >= 'A' && byte <='F') {
+            byte = byte - 'A' + 10;
+        }
+        val = (val << 8) | (byte & 0xFFu);
+    }
+    return val;
+}
+
+static void cdb_serialize_val_to_dap_store_obj(pdap_store_obj_t a_obj, char *key, char *val) {
+    if (!key || !val) {
+        a_obj = NULL;
+        return;
+    }
+    int offset = 0;
+    a_obj->key = dap_strdup(key);
+
+    unsigned char l_id[uint64_size] = {'\0'};
+    memcpy(l_id, val, uint64_size);
+    a_obj->id = dap_cdb_hex_to_uint(l_id, uint64_size);
+    offset += uint64_size;
+
+    unsigned char l_val_size[sizeof(unsigned long)] = {'\0'};
+    memcpy(l_val_size, val + offset, sizeof(unsigned long));
+    a_obj->value_len = dap_cdb_hex_to_uint(l_val_size, sizeof(unsigned long));
+    offset += sizeof(unsigned long);
+
+    a_obj->value = DAP_NEW_SIZE(uint8_t, a_obj->value_len);
+    memcpy(a_obj->value, val + offset, a_obj->value_len);
+    offset += a_obj->value_len;
+
+    unsigned char l_rawtime[sizeof(time_t)] = {'\0'};
+    memcpy(l_rawtime, val + offset, sizeof(time_t));
+    a_obj->timestamp = dap_cdb_hex_to_uint(l_rawtime, sizeof(time_t));
+}
+
+pcdb_instance *dap_cdb_init_group(char *a_group, int a_flags) {
+    pcdb_instance l_cdb_i = DAP_NEW(cdb_instance);
+    l_cdb_i->local_group = dap_strdup(a_group);
+    l_cdb_i->cdb = cdb_new();
+    char l_cdb_path[strlen(s_cdb_path) + strlen(a_group) + 2];
+    memset(l_cdb_path, '\0', strlen(s_cdb_path) + strlen(a_group) + 2);
+    strcat(l_cdb_path, s_cdb_path);
+    strcat(l_cdb_path, "/");
+    strcat(l_cdb_path, a_group);
+    cdb_options l_opts = { 1000000, 128, 1024 };
+    if ((dap_db_driver_cdb_options(&l_opts, l_cdb_i->cdb) != CDB_SUCCESS) ||
+            cdb_open(l_cdb_i->cdb, l_cdb_path, a_flags) < 0)
+    {
+        log_it(L_ERROR, "An error occured while opening CDB: \"%s\"", cdb_errmsg(cdb_errno(l_cdb_i->cdb)));
+        DAP_DELETE(l_cdb_i->local_group);
+        DAP_DELETE(l_cdb_i);
+        return NULL;
+    }
+    HASH_ADD_KEYPTR(hh, s_cdb, l_cdb_i->local_group, strlen(l_cdb_i->local_group), l_cdb_i);
+    return l_cdb_i;
+}
+
+int dap_db_driver_cdb_init(const char *a_cdb_path, dap_db_driver_callbacks_t *a_drv_callback) {
+    s_cdb_path = dap_strdup(a_cdb_path);
+    if(s_cdb_path[strlen(s_cdb_path)] == '/') {
+        s_cdb_path[strlen(s_cdb_path)] = '\0';
+    }
+    mkdir(s_cdb_path, 0755);
+    struct dirent *d;
+    DIR *dir = opendir(s_cdb_path);
+    if (!dir) {
+        log_it(L_ERROR, "Couldn't open db directory");
+        return -1;
+    }
+    for (d = readdir(dir); d; d = readdir(dir)) {
+        if (!dap_strcmp(d->d_name, ".") || !dap_strcmp(d->d_name, "..")) {
+            continue;
+        }
+        pcdb_instance l_cdb_i = dap_cdb_init_group(d->d_name, CDB_CREAT | CDB_PAGEWARMUP);
+        if (!l_cdb_i) {
+            dap_db_driver_cdb_deinit();
+            DAP_DELETE(s_cdb_path);
+            closedir(dir);
+            return -2;
+        }
+        CDBSTAT l_cdb_stat;
+        cdb_stat(l_cdb_i->cdb, &l_cdb_stat);
+        log_it(L_INFO, "Group \"%s\" found"             , l_cdb_i->local_group);
+        log_it(L_INFO, "Records: %-24u"                 , l_cdb_stat.rnum);
+        log_it(L_INFO, "Average read latency: %-24u"    , l_cdb_stat.rlatcy);
+        log_it(L_INFO, "Average write latency: %-24u"   , l_cdb_stat.wlatcy);
+    }
+    a_drv_callback->read_last_store_obj = dap_db_driver_cdb_read_last_store_obj;
+    a_drv_callback->apply_store_obj     = dap_db_driver_cdb_apply_store_obj;
+    a_drv_callback->read_store_obj      = dap_db_driver_cdb_read_store_obj;
+    a_drv_callback->read_cond_store_obj = dap_db_driver_cdb_read_cond_store_obj;
+    a_drv_callback->deinit              = dap_db_driver_cdb_deinit;
+
+    closedir(dir);
+    return CDB_SUCCESS;
+}
+
+CDB *dap_cdb_get_db_by_group(const char *a_group) {
+    pcdb_instance l_cdb_i = NULL;
+    HASH_FIND_STR(s_cdb, a_group, l_cdb_i);
+    if (!l_cdb_i) {
+        return NULL;
+    }
+    return l_cdb_i->cdb;
+}
+
+int dap_cdb_add_group(const char *a_group) {
+    char l_cdb_path[strlen(s_cdb_path) + strlen(a_group) + 2];
+    memset(l_cdb_path, '\0', strlen(s_cdb_path) + strlen(a_group) + 2);
+    strcat(l_cdb_path, s_cdb_path);
+    strcat(l_cdb_path, "/");
+    strcat(l_cdb_path, a_group);
+    mkdir(l_cdb_path, 0755);
+    return 0;
+}
+
+bool dap_cdb_get_last_obj_iter_callback(void *arg, const char *key, int ksize, const char *val, int vsize, uint32_t expire, uint64_t oid) {
+    /* this is wrong! TODO: instead of 'oid' must checkout real 'arg->id' */
+    /*if (oid != ((pobj_arg)arg)->q) {
+        return true;
+    }*/
+    cdb_serialize_val_to_dap_store_obj((pdap_store_obj_t)(((pobj_arg)arg)->o), key, val);
+    return false;
+}
+
+bool dap_cdb_get_some_obj_iter_callback(void *arg, const char *key, int ksize, const char *val, int vsize, uint32_t expire, uint64_t oid) {
+    ((pobj_arg)arg)->q--;
+    uint64_t q = ((pobj_arg)arg)->q;
+    uint64_t n = ((pobj_arg)arg)->n;
+    pdap_store_obj_t l_obj = (pdap_store_obj_t)((pobj_arg)arg)->o;
+    cdb_serialize_val_to_dap_store_obj(&l_obj[n-q-1], key, val);
+    if (q == 0) {
+        return false;
+    }
+    return true;
+}
+
+bool dap_cdb_get_cond_obj_iter_callback(void *arg, const char *key, int ksize, const char *val, int vsize, uint32_t expire, uint64_t oid) {
+    /* No need due to this implementation design */
+}
+
+int dap_db_driver_cdb_deinit() {
+    cdb_instance *cur_cdb, *tmp;
+    HASH_ITER(hh, s_cdb, cur_cdb, tmp) {
+        HASH_DEL(s_cdb, cur_cdb);
+        cdb_destroy(cur_cdb->cdb);
+    }
+    return CDB_SUCCESS;
+}
+
+int dap_db_driver_cdb_options(pcdb_options l_opts, CDB* a_cdb) {
+        if (cdb_option(a_cdb,
+                       l_opts->hsize,
+                       l_opts->pcacheMB,
+                       l_opts->rcacheMB) != CDB_SUCCESS) return -1;
+    return CDB_SUCCESS;
+}
+
+dap_store_obj_t *dap_db_driver_cdb_read_last_store_obj(const char* a_group) {
+    if (!a_group) {
+        return NULL;
+    }
+    CDB *l_cdb = dap_cdb_get_db_by_group(a_group);
+    if (!l_cdb) {
+        return NULL;
+    }
+    CDBSTAT l_cdb_stat;
+    cdb_stat(l_cdb, &l_cdb_stat);
+    void *l_iter = cdb_iterate_new(l_cdb, l_cdb_stat.rnum);
+    obj_arg l_arg;
+    l_arg.o = DAP_NEW_Z(dap_store_obj_t);
+    l_arg.q = l_cdb_stat.rnum;
+    cdb_iterate(l_cdb, dap_cdb_get_last_obj_iter_callback, (void*)&l_arg, l_iter);
+    cdb_iterate_destroy(l_cdb, l_iter);
+    l_arg.o->group = dap_strdup(a_group);
+    return l_arg.o;
+}
+
+dap_store_obj_t *dap_db_driver_cdb_read_store_obj(const char *a_group, const char *a_key, size_t *a_count_out) {
+    if (!a_group) {
+        return NULL;
+    }
+    //CDB_group l_group = dap_db_cdb_define_group(a_group);
+    CDB *l_cdb = dap_cdb_get_db_by_group(a_group);
+    if (!l_cdb) {
+        return NULL;
+    }
+    dap_store_obj_t *l_obj = NULL;
+    if (a_key) {
+        char *l_value;
+        int l_vsize;
+        cdb_get(l_cdb, a_key, strlen(a_key), (void**)&l_value, &l_vsize);
+        if (!l_value) {
+            return NULL;
+        }
+        l_obj = DAP_NEW_Z(dap_store_obj_t);
+        cdb_serialize_val_to_dap_store_obj(l_obj, a_key, l_value);
+        l_obj->group = dap_strdup(a_group);
+        cdb_free_val((void**)&l_value);
+    } else {
+        uint64_t l_count_out = 0;
+        if(a_count_out) {
+            l_count_out = *a_count_out;
+        }
+        CDBSTAT l_cdb_stat;
+        cdb_stat(l_cdb, &l_cdb_stat);
+        if ((l_count_out == 0) || (l_count_out > l_cdb_stat.rnum)) {
+            l_count_out = l_cdb_stat.rnum;
+        }
+        obj_arg l_arg;
+        l_arg.o = DAP_NEW_Z_SIZE(dap_store_obj_t, l_count_out * sizeof(dap_store_obj_t));
+        l_arg.q = l_count_out;
+        l_arg.n = l_count_out;
+        void *l_iter = cdb_iterate_new(l_cdb, 0);
+        l_count_out = cdb_iterate(l_cdb, dap_cdb_get_some_obj_iter_callback, (void*)&l_arg, l_iter);
+        cdb_iterate_destroy(l_cdb, l_iter);
+        if(a_count_out) {
+            *a_count_out = l_count_out;
+        }
+        for (ulong i = 0; i < l_count_out; ++i) {
+            l_arg.o[i].group = dap_strdup(a_group);
+        }
+        l_obj = l_arg.o;
+    }
+    return l_obj;
+}
+
+dap_store_obj_t* dap_db_driver_cdb_read_cond_store_obj(const char *a_group, uint64_t a_id, size_t *a_count_out) {
+    if (!a_group) {
+        return NULL;
+    }
+    CDB *l_cdb = dap_cdb_get_db_by_group(a_group);
+    if (!l_cdb) {
+        return NULL;
+    }
+    dap_store_obj_t *l_obj = NULL;
+    uint64_t l_count_out = 0;
+    if(a_count_out) {
+        l_count_out = *a_count_out;
+    }
+    CDBSTAT l_cdb_stat;
+    cdb_stat(l_cdb, &l_cdb_stat);
+    if ((l_count_out == 0) || (l_count_out > l_cdb_stat.rnum)) {
+        l_count_out = l_cdb_stat.rnum;
+    }
+    obj_arg l_arg;
+    l_arg.o = DAP_NEW_Z_SIZE(dap_store_obj_t, l_count_out * sizeof(dap_store_obj_t));
+    l_arg.q = l_count_out;
+    l_arg.q = l_count_out;
+    void *l_iter = cdb_iterate_new(l_cdb, a_id + 1); // wrong! TODO: make use of obj->id
+    l_count_out = cdb_iterate(l_cdb, dap_cdb_get_some_obj_iter_callback, (void*)&l_arg, l_iter);
+    cdb_iterate_destroy(l_cdb, l_iter);
+    if(a_count_out) {
+        *a_count_out = l_count_out;
+    }
+    for (ulong i = 0; i < l_count_out; ++i) {
+        l_arg.o[i].group = dap_strdup(a_group);
+    }
+    l_obj = l_arg.o;
+}
+
+int dap_db_driver_cdb_apply_store_obj(pdap_store_obj_t a_store_obj) {
+    if(!a_store_obj || !a_store_obj->group) {
+        return -1;
+    }
+    CDB *l_cdb = dap_cdb_get_db_by_group(a_store_obj->group);
+    if (!l_cdb) {
+        dap_cdb_add_group(a_store_obj->group);
+        pcdb_instance l_cdb_i = dap_cdb_init_group(a_store_obj->group, CDB_CREAT | CDB_PAGEWARMUP);
+        l_cdb = l_cdb_i->cdb;
+    }
+    if(a_store_obj->type == 'a') {
+        if(!a_store_obj->key || !a_store_obj->value || !a_store_obj->value_len) return -2;
+        cdb_record l_rec;
+        l_rec.key = dap_strdup(a_store_obj->key);
+        int offset = 0;
+        char *l_val = DAP_NEW_Z_SIZE(char, uint64_size + sizeof(unsigned long) + a_store_obj->value_len + sizeof(time_t));
+        dap_cdb_uint_to_hex(l_val, a_store_obj->id, uint64_size);
+        offset += uint64_size;
+
+        dap_cdb_uint_to_hex(l_val + offset, a_store_obj->value_len, sizeof(unsigned long));
+        offset += sizeof(unsigned long);
+
+        memcpy(l_val + offset, a_store_obj->value, a_store_obj->value_len);
+        offset += a_store_obj->value_len;
+
+        unsigned long l_time = (unsigned long)a_store_obj->timestamp;
+        dap_cdb_uint_to_hex(l_val + offset, l_time, sizeof(time_t));
+        offset += sizeof(time_t);
+        l_rec.val = l_val;
+        cdb_set(l_cdb, l_rec.key, strlen(l_rec.key), l_rec.val, offset);
+        DAP_DELETE(l_rec.key);
+        DAP_DELETE(l_rec.val);
+    } else if(a_store_obj->type == 'd') {
+        if(a_store_obj->key) {
+            cdb_del(l_cdb, a_store_obj->key, strlen(a_store_obj->key));
+        } else {
+            cdb_destroy(l_cdb);
+            dap_cdb_init_group(a_store_obj->group, CDB_TRUNC | CDB_PAGEWARMUP);
+        }
+    }
+    return 0;
+}
diff --git a/dap_chain_global_db_driver_cdb.h b/dap_chain_global_db_driver_cdb.h
new file mode 100644
index 0000000000000000000000000000000000000000..e24254f06d64ffd3e4b7d7589b05d3f691a97861
--- /dev/null
+++ b/dap_chain_global_db_driver_cdb.h
@@ -0,0 +1,46 @@
+/*
+ * Authors:
+ * Konstantin Papizh <konstantin.papizh@demlabs.net>
+ * DeM Labs Inc.   https://demlabs.net
+ * Kelvin Project https://github.com/kelvinblockchain
+ * Copyright  (c) 2019
+ * All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+ DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ DAP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cuttdb.h"
+#include "dap_chain_global_db_driver.h"
+
+typedef struct _cdb_options {
+    int hsize;      // Main hash table size, 1%-10% of total records, immutable
+    int rcacheMB;   // Record cache in MBytes
+    int pcacheMB;   // Index page cache in MBytes
+} cdb_options, *pcdb_options;
+
+typedef struct _cdb_record {
+    char *key;
+    char *val;
+} cdb_record, *pcdb_record;
+
+int dap_db_driver_cdb_init(const char*, dap_db_driver_callbacks_t*);
+int dap_db_driver_cdb_deinit();
+
+int dap_db_driver_cdb_apply_store_obj(pdap_store_obj_t);
+
+dap_store_obj_t *dap_db_driver_cdb_read_last_store_obj(const char*);
+dap_store_obj_t *dap_db_driver_cdb_read_store_obj(const char*, const char*, size_t*);
+dap_store_obj_t* dap_db_driver_cdb_read_cond_store_obj(const char*, uint64_t, size_t*);
diff --git a/libcuttdb.a b/libcuttdb.a
new file mode 100644
index 0000000000000000000000000000000000000000..eb6157bf89f6a454980eafe4af9f5a92651f63fe
Binary files /dev/null and b/libcuttdb.a differ