diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e644e875a0268e5803b7ade17c50af8b86eac13b
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8)
+project (dap_chain_global_db)
+  
+set(DAP_CHAIN_GLOBAL_DB_SRC
+    dap_chain_global_db.c
+   )
+
+set(DAP_CHAIN_GLOBAL_DB_HDR
+    dap_chain_global_db.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)
+target_include_directories(dap_chain_global_db INTERFACE .)
+
+set(${PROJECT_NAME}_DEFINITIONS CACHE INTERNAL "${PROJECT_NAME}: Definitions" FORCE)
+
+set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR} CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE)
diff --git a/dap_chain_global_db.c b/dap_chain_global_db.c
new file mode 100644
index 0000000000000000000000000000000000000000..b489ee7828bcf9cdf0fcd6003850e3e54698c5c8
--- /dev/null
+++ b/dap_chain_global_db.c
@@ -0,0 +1,251 @@
+#include <string.h>
+#include <stdio.h>
+//#include "talloc.h"
+#include "dap_chain_global_db.h"
+
+#define LOG_TAG "dap_global_db"
+#define TDB_PREFIX_LEN 7
+
+/* add single entry which is not supposed to be an array
+    e.g.: dap_db_add_record(&store_obj)
+*/
+#define CALL2(a, b, ...) (a), (b)
+#define dap_db_add_record(...) dap_db_merge(CALL2(__VA_ARGS__, 0))
+
+int dap_db_init(const char *path) {
+    mem_ctx = talloc_new(NULL);
+    if (ldb_global_init() != 0) {
+        log_it(L_ERROR, "Couldn't initialize LDB's global information");
+        return -1;
+    };
+    if ((ldb = ldb_init(mem_ctx, NULL)) != LDB_SUCCESS) {
+        log_it(L_INFO, "ldb context initialized");
+        char tdb_path[strlen(path) + TDB_PREFIX_LEN];
+        memset(tdb_path, '\0', strlen(path) + TDB_PREFIX_LEN);
+        strcat(tdb_path, "tdb://"); // using tdb for simplicity, no need for separate LDAP server
+        strcat(tdb_path, path);
+        struct ldb_result *data_message;
+        if (ldb_connect(ldb, tdb_path, 0, NULL) != LDB_SUCCESS) {
+            log_it(L_ERROR, "Couldn't connect to database");
+            return 1;
+        }
+        dap_db_path = strdup(tdb_path);
+        const char *query = "(dn=*)";
+        if (ldb_search(ldb, mem_ctx, &data_message, NULL, LDB_SCOPE_DEFAULT, NULL, query) != LDB_SUCCESS) {
+            log_it(L_ERROR, "Database querying failed");
+            return 2;
+        }
+        struct ldb_message *msg;
+        if (data_message->count == 0) {
+            // level 1: section record
+            msg = ldb_msg_new(ldb);
+            msg->dn = ldb_dn_new(mem_ctx, ldb,      "dc=kelvin_nodes");
+            ldb_msg_add_string(msg, "dc",           "kelvin_nodes");
+            ldb_msg_add_string(msg, "objectClass",  "top");
+            ldb_msg_add_string(msg, "objectClass",  "section");
+            dap_db_add_msg(msg);
+            talloc_free(msg->dn);
+            talloc_free(msg);
+
+            // level 2: group record
+            msg = ldb_msg_new(ldb);
+            msg->dn = ldb_dn_new(mem_ctx, ldb,      "ou=addrs_leased,dc=kelvin_nodes");
+            ldb_msg_add_string(msg, "ou",           "addrs_leased");
+            ldb_msg_add_string(msg, "objectClass",  "group");
+            ldb_msg_add_string(msg, "description",  "Whitelist of Kelvin blockchain nodes");
+            dap_db_add_msg(msg);
+            talloc_free(msg->dn);
+            talloc_free(msg);
+        }
+        talloc_free(data_message);
+        return 0;
+    }
+    else {
+        log_it(L_ERROR, "Couldn't initialize LDB context");
+        return -2;
+    }
+}
+
+int dap_db_add_msg(struct ldb_message *msg) {
+    if (ldb_msg_sanity_check(ldb, msg) != LDB_SUCCESS) {
+        log_it(L_ERROR, "LDB message is inconsistent: %s", ldb_errstring(ldb) );
+        return -1;
+    }
+    ldb_transaction_start(ldb);
+    int status = ldb_add(ldb, msg);
+    if (status != LDB_SUCCESS) {
+        if (status == LDB_ERR_ENTRY_ALREADY_EXISTS) {
+            log_it(L_INFO, "Entry %s already present, skipped", ldb_dn_get_linearized(msg->dn) );
+        }
+        else {
+            log_it(L_ERROR, "LDB adding error: %s", ldb_errstring(ldb) );
+        }
+        ldb_transaction_cancel(ldb);
+        return -2;
+    }
+    else {
+        ldb_transaction_commit(ldb);
+        log_it(L_INFO, "Entry %s added", ldb_dn_get_linearized(msg->dn) );
+        return 0;
+    }
+}
+
+/* path is supposed to have been obtained by smth like
+    dap_config_get_item_str(g_config, "resources", "dap_global_db_path");
+*/
+pdap_store_obj_t dap_db_read_data(void) {
+    struct ldb_result *data_message;
+    const char *query = "(objectClass=addr_leased)";
+    if (ldb_connect(ldb, dap_db_path, LDB_FLG_RDONLY, NULL) != LDB_SUCCESS) {
+        log_it(L_ERROR, "Couldn't connect to database");
+        return NULL;
+    }
+    if (ldb_search(ldb, NULL, &data_message, NULL, LDB_SCOPE_DEFAULT, NULL, query) != LDB_SUCCESS) {
+        log_it(L_ERROR, "Database querying failed");
+        return NULL;
+    }
+    log_it(L_INFO, "Obtained binary data, %d entries", data_message->count);
+
+    pdap_store_obj_t store_data = (pdap_store_obj_t)malloc(data_message->count * sizeof(struct dap_store_obj));
+    if (store_data != NULL) {
+        log_it(L_INFO, "We're about to put entries into store objects");
+    }
+    else {
+        log_it(L_ERROR, "Couldn't allocate memory, store objects unobtained");
+        talloc_free(data_message);
+        return NULL;
+    }
+    dap_store_len = data_message->count;
+    int q;
+    for (q = 0; q < dap_store_len; ++q) {
+        store_data[q].section = "kelvin_nodes";
+        store_data[q].group = "addrs_leased";
+        store_data[q].type = 1;
+        store_data[q].key = ldb_msg_find_attr_as_string(data_message->msgs[q], "cn", NULL);
+        store_data[q].value = ldb_msg_find_attr_as_string(data_message->msgs[q], "time", NULL);
+        log_it(L_INFO, "Record %s stored successfully", ldb_dn_get_linearized(data_message->msgs[q]->dn) );
+    }
+    talloc_free(data_message);
+    return store_data;
+}
+
+/* Get the entire content without using query expression
+ * This function is highly dissuaded from being used
+ * */
+pdap_store_obj_t dap_db_read_file_data(const char *path) {
+    struct ldb_ldif *ldif_msg;
+    FILE *fs = fopen(path, "r");
+    if (!fs) {
+        log_it(L_ERROR, "Can't open file %s", path);
+        return NULL;
+    }
+    pdap_store_obj_t store_data = (pdap_store_obj_t)malloc(256 * sizeof(dap_store_obj_t));
+    if (store_data != NULL) {
+        log_it(L_INFO, "We're about to put entries in store objects");
+    }
+    else {
+        log_it(L_ERROR, "Couldn't allocate memory, store objects unobtained");
+        return NULL;
+    }
+
+    int q = 0;
+    for (ldif_msg = ldb_ldif_read_file(ldb, fs); ldif_msg; ldif_msg = ldb_ldif_read_file(ldb, fs), q++) {
+        if (q % 256 == 0) {
+            store_data = (pdap_store_obj_t)realloc(store_data, (q + 256) * sizeof(dap_store_obj_t));
+        }
+        /* if (ldif_msg->changetype == LDB_CHANGETYPE_ADD) {
+          / ... /
+        } */ // in case we gonna use extra LDIF functionality
+        char *key = ldb_msg_find_attr_as_string(ldif_msg->msg, "cn", NULL);
+        if (key != NULL) {
+            store_data[q].section = "kelvin_nodes";
+            store_data[q].group = "addrs_leased";
+            store_data[q].type = 1;
+            store_data[q].key = key;
+            store_data[q].value = ldb_msg_find_attr_as_string(ldif_msg->msg, "time", NULL);
+            log_it(L_INFO, "Record %s stored successfully", ldb_dn_get_linearized(ldif_msg->msg->dn) );
+        }
+        ldb_ldif_read_free(ldb, ldif_msg);
+    }
+    return store_data;
+}
+
+/*
+ * Add multiple entries received from remote node to local database.
+ * Since we don't know the size, it must be supplied too
+ */
+int dap_db_merge(pdap_store_obj_t store_obj, int dap_store_size) {
+    if (store_obj == NULL) {
+        log_it(L_ERROR, "Invalid Dap store objects passed");
+        return -1;
+    }
+    if (ldb_connect(ldb, dap_db_path, NULL, NULL) != LDB_SUCCESS) {
+        log_it(L_ERROR, "Couldn't connect to database");
+        return -2;
+    }
+    log_it(L_INFO, "We're about to put %d records into database", dap_store_size);
+    struct ldb_message *msg;
+    int q;
+    if (dap_store_size == 0) {
+        dap_store_size = 1;
+    }
+    for (q = 0; q < dap_store_size; q++) {
+        // level 3: leased address, single whitelist entity
+        msg = ldb_msg_new(ldb);
+        char dn[128];
+        memset(dn, '\0', 128);
+        strcat(dn, "cn=");
+        strcat(dn, store_obj[q].key);
+        strcat(dn, ",ou=addrs_leased,dc=kelvin_nodes");
+        msg->dn = ldb_dn_new(mem_ctx, ldb, dn);
+        ldb_msg_add_string(msg, "cn", store_obj[q].key);
+        ldb_msg_add_string(msg, "objectClass", "addr_leased");
+        ldb_msg_add_string(msg, "description", "Approved Kelvin node");
+        ldb_msg_add_string(msg, "time", store_obj[q].value);
+        dap_db_add_msg(msg);
+        talloc_free(msg->dn);
+        talloc_free(msg);
+    }
+    return 0;
+}
+
+/* serialization */
+dap_store_obj_pkt_t *dap_store_packet_single(pdap_store_obj_t store_obj) {
+    dap_store_obj_pkt_t *pkt =
+            (dap_store_obj_pkt_t*)calloc(1, sizeof(int) + 4 + strlen(store_obj->group) + strlen(store_obj->key) + strlen(store_obj->section) + strlen(store_obj->value));
+    pkt->grp_size = strlen(store_obj->group) + 1;
+    pkt->name_size = strlen(store_obj->key) + 1;
+    pkt->sec_size = strlen(store_obj->section) + 1;
+    pkt->type = store_obj->type;
+    memcpy(pkt->data, &store_obj->section, pkt->sec_size);
+    memcpy(pkt->data+pkt->sec_size, &store_obj->group, pkt->grp_size);
+    memcpy(pkt->data+pkt->sec_size+pkt->grp_size, &store_obj->key, pkt->name_size);
+    memcpy(pkt->data+pkt->sec_size+pkt->grp_size+pkt->name_size, &store_obj->value, strlen(store_obj->value)+1);
+    return pkt;
+}
+
+dap_store_obj_pkt_t *dap_store_packet_multiple(pdap_store_obj_t store_obj) {
+    dap_store_obj_pkt_t *pkt =
+            (dap_store_obj_pkt_t*)calloc(1, sizeof(int) + 2 + strlen(store_obj->group) + dap_store_len*(1+strlen(store_obj->key)) + strlen(store_obj->section) + dap_store_len*(1+strlen(store_obj->value)));
+    pkt->grp_size = strlen(store_obj[0].group) + 1;
+    pkt->name_size = strlen(store_obj[0].key) + 1; // useless here since it can differ from one store_obj to another
+    pkt->sec_size = strlen(store_obj[0].section) + 1;
+    pkt->type = store_obj[0].type;
+    memcpy(pkt->data, &store_obj[0].section, pkt->sec_size);
+    memcpy(pkt->data+pkt->sec_size, &store_obj[0].group, pkt->grp_size);
+    uint64_t offset = pkt->sec_size+pkt->grp_size;
+    int q;
+    for (q = 0; q < dap_store_len; ++q) {
+        memcpy(pkt->data + offset, &store_obj[q].key, strlen(store_obj[q].key) + 1);
+        offset += strlen(store_obj[q].key) + 1;
+        memcpy(pkt->data + offset, &store_obj[q].value, strlen(store_obj[q].value) + 1);
+        offset += strlen(store_obj[q].value) + 1;
+    }
+    return pkt;
+}
+
+void dap_db_deinit() {
+    talloc_free(ldb);
+    talloc_free(mem_ctx);
+    free(dap_db_path);
+}
diff --git a/dap_chain_global_db.h b/dap_chain_global_db.h
new file mode 100644
index 0000000000000000000000000000000000000000..d8e6525fc176de63609c95456cde84fd25cf9270
--- /dev/null
+++ b/dap_chain_global_db.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <stdint.h>
+#include "dap_common.h"
+#include "ldb.h"
+
+typedef struct dap_store_obj {
+    char    *section;
+    char    *group;
+    char    *key;
+    uint8_t type;
+    char    *value;
+} DAP_ALIGN_PACKED dap_store_obj_t, *pdap_store_obj_t;
+
+typedef struct dap_store_obj_pkt {
+     uint8_t type;
+     uint8_t sec_size;
+     uint8_t grp_size;
+     uint8_t name_size;
+     uint8_t data[];
+} __attribute__((packed)) dap_store_obj_pkt_t;
+
+int dap_store_len = 0; // initialized only when reading from local db
+
+char *dap_db_path = NULL;
+
+static struct ldb_context *ldb  = NULL;
+static TALLOC_CTX *mem_ctx      = NULL;
+
+int     dap_db_init     (const char*);
+void    dap_db_deinit   (void);
+
+int dap_db_add_msg(struct ldb_message *);
+int dap_db_merge(pdap_store_obj_t, int);
+
+pdap_store_obj_t dap_db_read_data       (void);
+pdap_store_obj_t dap_db_read_file_data  (const char *); // state of emergency only, if LDB database is inaccessible
+dap_store_obj_pkt_t *dap_store_packet_single(pdap_store_obj_t);
+dap_store_obj_pkt_t *dap_store_packet_multiple(pdap_store_obj_t);