diff --git a/3rdparty/libmaxminddb/CMakeLists.txt b/3rdparty/libmaxminddb/CMakeLists.txt
index e9ae197bd4727e21acbbafec2c0f3310aaad83bb..b0ad5a319ed1c00de246f500ab6d4c2ce3018637 100644
--- a/3rdparty/libmaxminddb/CMakeLists.txt
+++ b/3rdparty/libmaxminddb/CMakeLists.txt
@@ -5,11 +5,21 @@ project(maxminddb C)
 add_definitions ("-D_GNU_SOURCE")
 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
 
-file(GLOB maxminddb_src ${CMAKE_CURRENT_SOURCE_DIR} *.c)
-file(GLOB maxminddb_h ${CMAKE_CURRENT_SOURCE_DIR} *.h)
+file(GLOB maxminddb_src ${CMAKE_CURRENT_SOURCE_DIR}/*.c)
+file(GLOB maxminddb_h ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
 
 add_library(${PROJECT_NAME} STATIC ${maxminddb_src} ${maxminddb_h})
 set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE C)
 set_target_properties(${PROJECT_NAME} PROPERTIES COMPILER_LANGUAGE C)
 target_include_directories(${PROJECT_NAME} INTERFACE .)
-target_compile_options(${PROJECT_NAME} PRIVATE -Wno-sign-compare)
+target_compile_options(${PROJECT_NAME} PRIVATE -Wno-sign-compare -Wno-pointer-sign)
+
+if (INSTALL_SDK)
+set_target_properties(${PROJECT_NAME}  PROPERTIES PUBLIC_HEADER "${maxminddb_h}")
+
+INSTALL(TARGETS ${PROJECT_NAME} 
+        LIBRARY DESTINATION lib/modules/net/
+        ARCHIVE DESTINATION lib/modules/net/
+        PUBLIC_HEADER DESTINATION include/modules/net/maxminddb/        
+)
+endif()
diff --git a/3rdparty/libmaxminddb/maxminddb.c b/3rdparty/libmaxminddb/maxminddb.c
index 8ed51d5a6ec767e36ee07129eba662924f63246d..d5466cf2a158a9e31f806f0274abe9d4870c5818 100644
--- a/3rdparty/libmaxminddb/maxminddb.c
+++ b/3rdparty/libmaxminddb/maxminddb.c
@@ -220,6 +220,97 @@ LOCAL char *bytes_to_hex(uint8_t *bytes, uint32_t size);
 
 #define FREE_AND_SET_NULL(p) { free((void *)(p)); (p) = NULL; }
 
+int MMDB_open_memory(const char *const mem, ssize_t size, MMDB_s *const mmdb)
+{
+    if (NULL == mem || size <= 0) {
+        return  MMDB_OUT_OF_MEMORY_ERROR;
+    }
+
+    int status = MMDB_SUCCESS;
+
+    mmdb->file_content = NULL;
+    mmdb->data_section = NULL;
+    mmdb->metadata.database_type = NULL;
+    mmdb->metadata.languages.count = 0;
+    mmdb->metadata.languages.names = NULL;
+    mmdb->metadata.description.count = 0;
+    mmdb->filename = NULL;
+    
+    mmdb->file_size = size;
+    mmdb->file_content = mem;
+
+    
+#ifdef _WIN32
+    WSADATA wsa;
+    WSAStartup(MAKEWORD(2, 2), &wsa);
+#endif
+
+    uint32_t metadata_size = 0;
+    const uint8_t *metadata = find_metadata(mmdb->file_content, mmdb->file_size,
+                                            &metadata_size);
+    if (NULL == metadata) {
+        status = MMDB_INVALID_METADATA_ERROR;
+        goto cleanup;
+    }
+
+    mmdb->metadata_section = metadata;
+    mmdb->metadata_section_size = metadata_size;
+
+    status = read_metadata(mmdb);
+    if (MMDB_SUCCESS != status) {
+        goto cleanup;
+    }
+
+    if (mmdb->metadata.binary_format_major_version != 2) {
+        status = MMDB_UNKNOWN_DATABASE_FORMAT_ERROR;
+        goto cleanup;
+    }
+
+    uint32_t search_tree_size = mmdb->metadata.node_count *
+                                mmdb->full_record_byte_size;
+
+    mmdb->data_section = mmdb->file_content + search_tree_size
+                         + MMDB_DATA_SECTION_SEPARATOR;
+    if (search_tree_size + MMDB_DATA_SECTION_SEPARATOR >
+        (uint32_t)mmdb->file_size) {
+        status = MMDB_INVALID_METADATA_ERROR;
+        goto cleanup;
+    }
+    mmdb->data_section_size = (uint32_t)mmdb->file_size - search_tree_size -
+                              MMDB_DATA_SECTION_SEPARATOR;
+
+    // Although it is likely not possible to construct a database with valid
+    // valid metadata, as parsed above, and a data_section_size less than 3,
+    // we do this check as later we assume it is at least three when doing
+    // bound checks.
+    if (mmdb->data_section_size < 3) {
+        status = MMDB_INVALID_DATA_ERROR;
+        goto cleanup;
+    }
+
+    mmdb->metadata_section = metadata;
+    mmdb->ipv4_start_node.node_value = 0;
+    mmdb->ipv4_start_node.netmask = 0;
+
+    // We do this immediately as otherwise there is a race to set
+    // ipv4_start_node.node_value and ipv4_start_node.netmask.
+    if (mmdb->metadata.ip_version == 6) {
+        status = find_ipv4_start_node(mmdb);
+        if (status != MMDB_SUCCESS) {
+            goto cleanup;
+        }
+    }
+
+ cleanup:
+    if (MMDB_SUCCESS != status) {
+        int saved_errno = errno;
+        free_mmdb_struct(mmdb);
+        errno = saved_errno;
+    }
+    return status;
+}
+
+
 int MMDB_open(const char *const filename, uint32_t flags, MMDB_s *const mmdb)
 {
     int status = MMDB_SUCCESS;
diff --git a/3rdparty/libmaxminddb/maxminddb.h b/3rdparty/libmaxminddb/maxminddb.h
index f4b9bd24e48584ab6431775ec497e763bc4442f1..482861b39e961654a3963241dedfe76ab6382a6e 100644
--- a/3rdparty/libmaxminddb/maxminddb.h
+++ b/3rdparty/libmaxminddb/maxminddb.h
@@ -215,6 +215,9 @@ typedef struct MMDB_search_node_s {
 
 extern int MMDB_open(const char *const filename, uint32_t flags,
                      MMDB_s *const mmdb);
+
+extern int MMDB_open_memory(const char *const mem, ssize_t size, MMDB_s *const mmdb);
+
 extern MMDB_lookup_result_s MMDB_lookup_string(const MMDB_s *const mmdb,
                                                const char *const ipstr,
                                                int *const gai_error,
diff --git a/cellframe-sdk.pro b/cellframe-sdk.pro
index 146d21a1b283c2e9086378c4df1fae8483686a96..3df384da834cf270277071332016de2f0ba8d1c0 100644
--- a/cellframe-sdk.pro
+++ b/cellframe-sdk.pro
@@ -8,13 +8,10 @@ linux: !android {
 }
 
 win32 {
-    contains(QMAKE_HOST.os, "Windows") {
-        sdk_build.commands = $$PWD/../cellframe-sdk/prod_build/build.bat
-    }
-    else {
-        CONFIG(release, debug | release): sdk_build.commands = "$$shell_path($$PWD/../cellframe-sdk/prod_build/build.sh)" --target windows release -DINSTALL_SDK=1 -DCMAKE_INSTALL_PREFIX=/
-        CONFIG(debug, debug | release): sdk_build.commands = "$$shell_path($$PWD/../cellframe-sdk/prod_build/build.sh)" --target windows rwd -DINSTALL_SDK=1 -DCMAKE_INSTALL_PREFIX=/
-    }
+    
+    CONFIG(release, debug | release): sdk_build.commands = "$$shell_path($$PWD/../cellframe-sdk/prod_build/build.sh)" --target windows release -DINSTALL_SDK=1 -DCMAKE_INSTALL_PREFIX=/
+    CONFIG(debug, debug | release): sdk_build.commands = "$$shell_path($$PWD/../cellframe-sdk/prod_build/build.sh)" --target windows rwd -DINSTALL_SDK=1 -DCMAKE_INSTALL_PREFIX=/
+    
 }
 
 android {
diff --git a/dap-sdk b/dap-sdk
index b039b9d09b1ec1718ff93a296cf9f120f5c41762..c9259b9949a44daee571d2fff3bdc329758ba68a 160000
--- a/dap-sdk
+++ b/dap-sdk
@@ -1 +1 @@
-Subproject commit b039b9d09b1ec1718ff93a296cf9f120f5c41762
+Subproject commit c9259b9949a44daee571d2fff3bdc329758ba68a
diff --git a/modules/chain/dap_chain_ch.c b/modules/chain/dap_chain_ch.c
index 2f78d6ae01c91a5d326a9b847b990b12f921424b..7aa1455274b0da1bd73d2353ca68ef68f079ecd3 100644
--- a/modules/chain/dap_chain_ch.c
+++ b/modules/chain/dap_chain_ch.c
@@ -778,7 +778,8 @@ static bool s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
         if (s_debug_more) {
             char *l_atom_hash_str;
             dap_get_data_hash_str_static(l_chain_pkt->data, l_chain_pkt_data_size, l_atom_hash_str);
-            log_it(L_INFO, "In: CHAIN pkt: atom hash %s (size %zd)", l_atom_hash_str, l_chain_pkt_data_size);
+            log_it(L_INFO, "In: CHAIN pkt: atom hash %s, size %zd, net id %lu, chain id %lu",
+                    l_atom_hash_str, l_chain_pkt_data_size, l_chain_pkt->hdr.net_id, l_chain_pkt->hdr.chain_id);
         }
         dap_proc_thread_callback_add(a_ch->stream_worker->worker->proc_queue_input, s_sync_in_chains_callback, l_args);
     } break;
diff --git a/modules/net/dap_chain_net.c b/modules/net/dap_chain_net.c
index 4ba06647b8635c3beddbc5125ae6ab692884ab2c..8185ae38589468497113b1370d3175dea8676d1c 100644
--- a/modules/net/dap_chain_net.c
+++ b/modules/net/dap_chain_net.c
@@ -2368,37 +2368,32 @@ bool s_net_load(void *a_arg)
             l_chain->callback_created(l_chain, l_net->pub.config);
 
      if ( dap_config_get_item_bool_default(g_config, "server", "enabled", false) ) {
-        if ( !l_net_pvt->node_info->ext_port ) {
-            char l_host[DAP_HOSTADDR_STRLEN + 1] = { '\0' };
-            uint16_t l_ext_port = 0;
-            const char *l_ext_addr = dap_config_get_item_str_default(g_config, "server", "ext_address", NULL);
-            if (!l_ext_addr) {
-                log_it(L_INFO, "External address is not set, will be detected automatically");
-            } else {
-                struct sockaddr_storage l_saddr = { };
-                if ( 0 > dap_net_parse_config_address(l_ext_addr, l_host, &l_ext_port, &l_saddr, NULL) )
-                    log_it(L_ERROR, "Invalid server address \"%s\", fix config and restart node", l_ext_addr);
-                else {
-                    uint8_t l_hostlen = dap_strlen(l_host);
-                    l_net_pvt->node_info->ext_port = l_ext_port;
-                    l_net_pvt->node_info->ext_host_len = dap_strncpy(l_net_pvt->node_info->ext_host, l_host, l_hostlen) - l_net_pvt->node_info->ext_host;
-                }
-            }
-            if ( !l_net_pvt->node_info->ext_port ) {
-                const char **l_listening = dap_config_get_array_str(g_config, "server", DAP_CFG_PARAM_LISTEN_ADDRS, NULL);
-                l_net_pvt->node_info->ext_port =
-                    ( l_listening && dap_net_parse_config_address(*l_listening, NULL, &l_ext_port, NULL, NULL) > 0 && l_ext_port )
-                        ? l_ext_port
-                        : dap_config_get_item_int16_default(g_config, "server", DAP_CFG_PARAM_LEGACY_PORT, 8079); // TODO: default port?
+        char l_host[DAP_HOSTADDR_STRLEN + 1] = { '\0' };
+        uint16_t l_ext_port = 0;
+        const char *l_ext_addr = dap_config_get_item_str_default(g_config, "server", "ext_address", NULL);
+        if (l_ext_addr) {
+            struct sockaddr_storage l_saddr = { };
+            if ( 0 > dap_net_parse_config_address(l_ext_addr, l_host, &l_ext_port, &l_saddr, NULL) )
+                log_it(L_ERROR, "Invalid server address \"%s\", fix config and restart node", l_ext_addr);
+            else {
+                uint8_t l_hostlen = dap_strlen(l_host);
+                l_net_pvt->node_info->ext_port = l_ext_port;
+                l_net_pvt->node_info->ext_host_len = dap_strncpy(l_net_pvt->node_info->ext_host, l_host, l_hostlen) - l_net_pvt->node_info->ext_host;
             }
-        } // otherwise, we're in seed list - seed config predominates server config thus disambiguating the settings
-        
-        if (l_net_pvt->node_info->ext_host_len) {
-            log_it(L_INFO, "Server is configured with external address %s : %u",
-               l_net_pvt->node_info->ext_host, l_net_pvt->node_info->ext_port);
         }
-    } else
-        log_it(L_INFO, "Server is disabled");
+#ifdef DAP_TRY_DEFAULT_PORT
+        log_it(L_INFO, "External address is not set, will be detected automatically");
+        if ( !l_net_pvt->node_info->ext_port ) {
+            const char **l_listening = dap_config_get_array_str(g_config, "server", DAP_CFG_PARAM_LISTEN_ADDRS, NULL);
+            l_net_pvt->node_info->ext_port =
+                ( l_listening && dap_net_parse_config_address(*l_listening, NULL, &l_ext_port, NULL, NULL) > 0 && l_ext_port )
+                    ? l_ext_port
+                    : dap_config_get_item_int16_default(g_config, "server", DAP_CFG_PARAM_LEGACY_PORT, 8079);
+        }
+#endif
+        log_it(L_INFO, "Server is configured with external address %s : %u",
+            l_net_pvt->node_info->ext_host_len ? l_net_pvt->node_info->ext_host : "", l_net_pvt->node_info->ext_port);
+    }
 
     l_net_pvt->node_info->address.uint64 = g_node_addr.uint64;
 
@@ -2591,7 +2586,11 @@ static void s_sync_timer_callback(void *a_arg)
             l_net_pvt->sync_context.state = l_net_pvt->sync_context.last_state = SYNC_STATE_WAITING;
         } else {
             l_net_pvt->sync_context.cur_chain = l_net_pvt->sync_context.cur_chain->next;
-            log_it(L_DEBUG, "[%s:%d] Go to next chain %p", __FUNCTION__, __LINE__, l_net_pvt->sync_context.cur_chain);
+            if (l_net_pvt->sync_context.cur_chain)
+                log_it(L_DEBUG, "[%s:%d] Go to next chain \"%s\" for net %s", __FUNCTION__, __LINE__,
+                        l_net_pvt->sync_context.cur_chain->name, l_net_pvt->sync_context.cur_chain->net_name);
+            else 
+                log_it(L_DEBUG, "[%s:%d] Go to next chain: <NULL>",  __FUNCTION__, __LINE__);
             if (!l_net_pvt->sync_context.cur_chain) {
                 dap_chain_net_state_t l_prev_state = l_net_pvt->state;
                 if (l_net_pvt->sync_context.last_state == SYNC_STATE_SYNCED) {
@@ -2610,7 +2609,11 @@ static void s_sync_timer_callback(void *a_arg)
         if (l_net_pvt->sync_context.cur_chain->callback_load_from_gdb) {
             // This type of chain is GDB based and not synced by chains protocol
             l_net_pvt->sync_context.cur_chain = l_net_pvt->sync_context.cur_chain->next;
-            log_it(L_DEBUG, "[%s:%d] Go to next chain %p", __FUNCTION__, __LINE__, l_net_pvt->sync_context.cur_chain);
+            if (l_net_pvt->sync_context.cur_chain)
+                log_it(L_DEBUG, "[%s:%d] Go to next chain \"%s\" for net %s", __FUNCTION__, __LINE__,
+                        l_net_pvt->sync_context.cur_chain->name, l_net_pvt->sync_context.cur_chain->net_name);
+            else 
+                log_it(L_DEBUG, "[%s:%d] Go to next chain: <NULL>",  __FUNCTION__, __LINE__);
             l_net_pvt->sync_context.last_state = SYNC_STATE_SYNCED;
             return;
         }
@@ -3374,7 +3377,14 @@ uint256_t dap_chain_net_get_reward(dap_chain_net_t *a_net, uint64_t a_block_num)
     return uint256_0;
 }
 
-void dap_chain_net_announce_addrs(dap_chain_net_t *a_net)
+
+void dap_chain_net_announce_addr_all()
+{
+    for (dap_chain_net_item_t *it = s_net_items; it; it = it->hh.next)
+        dap_chain_net_announce_addr(it->chain_net);
+}
+
+void dap_chain_net_announce_addr(dap_chain_net_t *a_net)
 {
     dap_return_if_fail(a_net);
     dap_chain_net_pvt_t *l_net_pvt = PVT(a_net);
diff --git a/modules/net/include/dap_chain_net.h b/modules/net/include/dap_chain_net.h
index 5cfa52d7c014c2f8aa05af933eedc7ddd03c112a..56eb1c1973c2e771aed626a590146c739c906d9d 100644
--- a/modules/net/include/dap_chain_net.h
+++ b/modules/net/include/dap_chain_net.h
@@ -201,7 +201,8 @@ int dap_chain_datum_add(dap_chain_t * a_chain, dap_chain_datum_t *a_datum, size_
 int dap_chain_datum_remove(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, size_t a_datum_size, dap_hash_fast_t *a_datum_hash);
 
 bool dap_chain_net_get_load_mode(dap_chain_net_t * a_net);
-void dap_chain_net_announce_addrs(dap_chain_net_t *a_net);
+void dap_chain_net_announce_addr(dap_chain_net_t *a_net);
+void dap_chain_net_announce_addr_all();
 char *dap_chain_net_links_dump(dap_chain_net_t*);
 struct json_object *dap_chain_net_states_json_collect(dap_chain_net_t * l_net);