From 77fd968a06a994df671867b8a8b92efb85a92f11 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Al=D0=B5x=D0=B0nder=20Lysik=D0=BEv?=
 <alexander.lysikov@demlabs.net>
Date: Thu, 18 Jun 2020 21:27:33 +0500
Subject: [PATCH] added vpn client inside node

---
 dap-sdk/core/src/dap_common.c                 |   5 +-
 dap-sdk/crypto/include/dap_enc_SEED.h         |   2 +-
 dap-sdk/net/client/dap_client_pvt.c           |  13 +-
 dap-sdk/net/client/include/dap_client_pvt.h   |   2 +-
 dap-sdk/net/core/dap_events_socket.c          |   5 +-
 .../dap_stream_ch_chain_net_srv.c             |  52 ++-
 .../include/dap_stream_ch_chain_net_srv.h     |   3 +
 modules/net/dap_chain_node_cli.c              |   2 +-
 modules/net/dap_chain_node_cli_cmd.c          |  87 +++-
 modules/net/dap_chain_node_client.c           |  28 +-
 modules/net/srv/dap_chain_net_srv.c           |   1 +
 modules/net/srv/dap_chain_net_srv_client.c    |  29 ++
 modules/net/srv/dap_chain_net_srv_common.c    |   4 +
 modules/net/srv/include/dap_chain_net_srv.h   |  14 +
 .../srv/include/dap_chain_net_srv_client.h    |   5 +
 .../srv/include/dap_chain_net_srv_common.h    |   3 +
 modules/service/vpn/CMakeLists.txt            |   2 +-
 modules/service/vpn/dap_chain_net_srv_vpn.c   | 398 +++++++++++-----
 .../service/vpn/dap_chain_net_srv_vpn_cdb.c   |   4 +-
 .../vpn/dap_chain_net_srv_vpn_cdb_auth.c      |   8 +-
 .../dap_chain_net_srv_vpn_cdb_server_list.c   |  17 +-
 .../service/vpn/dap_chain_net_vpn_client.c    | 436 ++++++++++++------
 .../vpn/dap_chain_net_vpn_client_tun.c        | 137 +++++-
 .../vpn/include/dap_chain_net_srv_vpn.h       |   4 +
 .../include/dap_chain_net_srv_vpn_cdb_auth.h  |   4 +-
 .../vpn/include/dap_chain_net_vpn_client.h    |  13 +-
 26 files changed, 962 insertions(+), 316 deletions(-)

diff --git a/dap-sdk/core/src/dap_common.c b/dap-sdk/core/src/dap_common.c
index 78b63ee843..39d6bec006 100755
--- a/dap-sdk/core/src/dap_common.c
+++ b/dap-sdk/core/src/dap_common.c
@@ -166,8 +166,9 @@ static char* s_appname = NULL;
 
 DAP_STATIC_INLINE void s_update_log_time(char *a_datetime_str) {
     time_t t = time(NULL);
-    struct tm *tmptime = localtime(&t);
-    strftime(a_datetime_str, 32, "[%x-%X]", tmptime);
+    struct tm tmptime;
+    if(localtime_r(&t, &tmptime))
+        strftime(a_datetime_str, 32, "[%x-%X]", &tmptime);
 }
 
 /**
diff --git a/dap-sdk/crypto/include/dap_enc_SEED.h b/dap-sdk/crypto/include/dap_enc_SEED.h
index 8ff83d6c8f..8b9a31acea 100644
--- a/dap-sdk/crypto/include/dap_enc_SEED.h
+++ b/dap-sdk/crypto/include/dap_enc_SEED.h
@@ -3,7 +3,7 @@
 
 #include <stddef.h>
 #include "dap_enc_key.h"
-#include "seed.h"
+#include "seed/seed.h"
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/dap-sdk/net/client/dap_client_pvt.c b/dap-sdk/net/client/dap_client_pvt.c
index 04e2fb58d3..fd15bb670d 100644
--- a/dap-sdk/net/client/dap_client_pvt.c
+++ b/dap-sdk/net/client/dap_client_pvt.c
@@ -408,8 +408,12 @@ static void s_stage_status_after(dap_client_pvt_t * a_client_pvt)
                     l_key_str, DAP_ENC_DATA_TYPE_B64);
 
             log_it(L_DEBUG, "ENC request size %u", l_key_str_enc_size);
-            dap_client_pvt_request(a_client_pvt, DAP_UPLINK_PATH_ENC_INIT "/gd4y5yh78w42aaagh",
+            int l_res = dap_client_pvt_request(a_client_pvt, DAP_UPLINK_PATH_ENC_INIT "/gd4y5yh78w42aaagh",
                     l_key_str, l_key_str_enc_size, m_enc_init_response, m_enc_init_error);
+            // bad request
+            if(l_res<0){
+            	a_client_pvt->stage_status = STAGE_STATUS_ERROR;
+            }
             DAP_DELETE(l_key_str);
         }
             break;
@@ -668,7 +672,7 @@ void dap_client_pvt_stage_transaction_begin(dap_client_pvt_t * a_client_internal
  * @param a_request_size
  * @param a_response_proc
  */
-void dap_client_pvt_request(dap_client_pvt_t * a_client_internal, const char * a_path, void * a_request,
+int dap_client_pvt_request(dap_client_pvt_t * a_client_internal, const char * a_path, void * a_request,
         size_t a_request_size, dap_client_callback_data_size_t a_response_proc,
         dap_client_callback_int_t a_response_error)
 {
@@ -689,11 +693,14 @@ void dap_client_pvt_request(dap_client_pvt_t * a_client_internal, const char * a
 //        l_url = DAP_NEW_Z_SIZE(char, l_url_size_max);
 //        snprintf(l_url, l_url_size_max, "http://%s:%u", a_client_internal->uplink_addr, a_client_internal->uplink_port);
 //    }
-    dap_client_http_request(a_client_internal->uplink_addr,a_client_internal->uplink_port, a_request ? "POST" : "GET", "text/text", a_path, a_request,
+    void *l_ret = dap_client_http_request(a_client_internal->uplink_addr,a_client_internal->uplink_port, a_request ? "POST" : "GET", "text/text", a_path, a_request,
             a_request_size, NULL, m_request_response, m_request_error, a_client_internal, NULL);
 //    a_client_internal->curl = dap_http_client_simple_request(l_url, a_request ? "POST" : "GET", "text/text", a_request,
 //            a_request_size, NULL, m_request_response, m_request_error, &a_client_internal->curl_sockfd, a_client_internal, NULL);
 //    DAP_DELETE(l_url);
+    if(l_ret)
+    	return 0;
+    return -1;
 }
 
 /**
diff --git a/dap-sdk/net/client/include/dap_client_pvt.h b/dap-sdk/net/client/include/dap_client_pvt.h
index 3c27387fbe..9c93e03ad6 100644
--- a/dap-sdk/net/client/include/dap_client_pvt.h
+++ b/dap-sdk/net/client/include/dap_client_pvt.h
@@ -89,7 +89,7 @@ void dap_client_pvt_deinit();
 void dap_client_pvt_stage_transaction_begin(dap_client_pvt_t * dap_client_pvt_t, dap_client_stage_t a_stage_next,
                                                  dap_client_callback_t a_done_callback);
 
-void dap_client_pvt_request(dap_client_pvt_t * a_client_internal, const char * a_path, void * a_request,
+int dap_client_pvt_request(dap_client_pvt_t * a_client_internal, const char * a_path, void * a_request,
                     size_t a_request_size,  dap_client_callback_data_size_t a_response_proc, dap_client_callback_int_t a_response_error);
 
 void dap_client_pvt_request_enc(dap_client_pvt_t * a_client_internal, const char * a_path, const char * a_sub_url,
diff --git a/dap-sdk/net/core/dap_events_socket.c b/dap-sdk/net/core/dap_events_socket.c
index 6060e27a3f..22693a4597 100644
--- a/dap-sdk/net/core/dap_events_socket.c
+++ b/dap-sdk/net/core/dap_events_socket.c
@@ -264,9 +264,12 @@ int dap_events_socket_kill_socket( dap_events_socket_t *a_es )
     return -1;
   }
 
+  dap_worker_t *w = a_es->dap_worker;
+  // worker not initialized yet
+  if(!w)
+	  return -2;
   uint32_t tn = a_es->dap_worker->number_thread;
 
-  dap_worker_t *w = a_es->dap_worker;
   //dap_events_t *d_ev = w->events;
 
   pthread_mutex_lock( &w->locker_on_count );
diff --git a/modules/channel/chain-net-srv/dap_stream_ch_chain_net_srv.c b/modules/channel/chain-net-srv/dap_stream_ch_chain_net_srv.c
index c15223614f..2a1422804c 100644
--- a/modules/channel/chain-net-srv/dap_stream_ch_chain_net_srv.c
+++ b/modules/channel/chain-net-srv/dap_stream_ch_chain_net_srv.c
@@ -33,6 +33,7 @@ along with any CellFrame SDK based project.  If not, see <http://www.gnu.org/lic
 #include "dap_chain_mempool.h"
 
 #include "dap_chain_net_srv.h"
+#include "dap_chain_net_srv_common.h"
 #include "dap_chain_net_srv_stream_session.h"
 
 
@@ -48,6 +49,7 @@ along with any CellFrame SDK based project.  If not, see <http://www.gnu.org/lic
 
 typedef struct dap_stream_ch_chain_net_srv {
     pthread_mutex_t mutex;
+    dap_chain_net_srv_uid_t srv_uid;
 } dap_stream_ch_chain_net_srv_t;
 
 #define DAP_STREAM_CH_CHAIN_NET_SRV(a) ((dap_stream_ch_chain_net_srv_t *) ((a)->internal) )
@@ -57,7 +59,6 @@ static void s_stream_ch_delete(dap_stream_ch_t* ch , void* arg);
 static void s_stream_ch_packet_in(dap_stream_ch_t* ch , void* arg);
 static void s_stream_ch_packet_out(dap_stream_ch_t* ch , void* arg);
 
-
 /**
  * @brief dap_stream_ch_chain_net_init
  * @return
@@ -65,7 +66,7 @@ static void s_stream_ch_packet_out(dap_stream_ch_t* ch , void* arg);
 int dap_stream_ch_chain_net_srv_init(void)
 {
     log_it(L_NOTICE,"Chain network services channel initialized");
-    dap_stream_ch_proc_add('R',s_stream_ch_new,s_stream_ch_delete,s_stream_ch_packet_in,s_stream_ch_packet_out);
+    dap_stream_ch_proc_add(dap_stream_ch_chain_net_srv_get_id(),s_stream_ch_new,s_stream_ch_delete,s_stream_ch_packet_in,s_stream_ch_packet_out);
 
     return 0;
 }
@@ -78,6 +79,16 @@ void dap_stream_ch_chain_net_srv_deinit(void)
 
 }
 
+/**
+ * @brief Set srv uid - for client
+ */
+void dap_stream_ch_chain_net_srv_set_srv_uid(dap_stream_ch_t* a_ch, dap_chain_net_srv_uid_t a_srv_uid)
+{
+    // save srv id
+    dap_stream_ch_chain_net_srv_t * l_ch_chain_net_srv = DAP_STREAM_CH_CHAIN_NET_SRV(a_ch);
+    l_ch_chain_net_srv->srv_uid.uint64 = a_srv_uid.uint64;
+}
+
 /**
  * @brief s_stream_ch_new
  * @param a_ch
@@ -135,6 +146,7 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch , void* a_arg)
 
     if(l_ch_pkt ) {
         switch (l_ch_pkt->hdr.type) {
+        	// only for server
             case DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_REQUEST:{
                 if (l_ch_pkt->hdr.size < sizeof(dap_stream_ch_chain_net_srv_pkt_request_hdr_t) ){
                     log_it( L_WARNING, "Wrong request size, less than minimum");
@@ -312,10 +324,35 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch , void* a_arg)
                 //if(l_receipt)
                 //    DAP_DELETE(l_receipt);
             } break;
+            // only for client
             case DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_SIGN_REQUEST:{
                 log_it( L_NOTICE, "Requested smth to sign");
+                dap_chain_datum_tx_receipt_t * l_receipt = (dap_chain_datum_tx_receipt_t *) l_ch_pkt->data;
+                size_t l_receipt_size = l_ch_pkt->hdr.size;
+                // create receipt copy, because l_receipt may be reallocated inside dap_chain_datum_tx_receipt_create()!
+                dap_chain_datum_tx_receipt_t *l_receipt_new = dap_chain_datum_tx_receipt_create(l_receipt->receipt_info.srv_uid,
+                        l_receipt->receipt_info.units_type,
+                        l_receipt->receipt_info.units,
+                        l_receipt->receipt_info.value_datoshi,
+                        l_receipt->exts_n_signs, l_receipt->exts_size);
+
+
+                //l_srv_session->usages
+                ///l_usage->service->uid.uint64;
+                //dap_chain_net_srv_usage_t * l_usage = dap_chain_net_srv_usage_find( l_srv_session, l_pkt->hdr.usage_id );
+                dap_chain_net_srv_t * l_srv = dap_chain_net_srv_get(l_ch_chain_net_srv->srv_uid);
+            if(l_srv && l_srv->callback_client_sign_request) {
+                // Sign receipt
+                l_srv->callback_client_sign_request(l_srv, 0, NULL, &l_receipt_new, l_receipt_size);
+                if(dap_stream_ch_pkt_write(a_ch, DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_SIGN_RESPONSE,
+                        l_receipt_new, l_receipt_new->size)) {
+                    dap_stream_ch_set_ready_to_write(a_ch, true);
+                }
+            }
+                DAP_DELETE(l_receipt_new);
                 // TODO sign smth
             } break;
+            // only for server
             case DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_SIGN_RESPONSE:{
                 if ( l_ch_pkt->hdr.size > sizeof(dap_chain_receipt_info_t)+1 ){
                     dap_chain_datum_tx_receipt_t * l_receipt = (dap_chain_datum_tx_receipt_t *) l_ch_pkt->data;
@@ -485,6 +522,17 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch , void* a_arg)
             case DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_RESPONSE_SUCCESS:{
                 log_it( L_NOTICE, "Responsed with success");
                 // TODO code for service client mode
+                dap_stream_ch_chain_net_srv_pkt_success_t * l_success = (dap_stream_ch_chain_net_srv_pkt_success_t*)l_ch_pkt->data;
+                size_t l_success_size = l_ch_pkt->hdr.size;
+                dap_chain_net_srv_t * l_srv = dap_chain_net_srv_get(l_success->hdr.srv_uid);
+                if ( l_srv && l_srv->callback_client_success){
+                    // Create client for client)
+                    dap_chain_net_srv_client_t *l_clients = DAP_NEW_Z( dap_chain_net_srv_client_t);
+                    l_clients->ch = a_ch;
+                    l_clients->ts_created = time(NULL);
+                    l_srv->callback_client_success(l_srv, l_success->hdr.usage_id,  l_clients, l_success, l_success_size );
+                    //l_success->hdr.net_id, l_success->hdr.srv_uid, l_success->hdr.usage_id
+                }
             } break;
             case DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_DATA:{
                 if (l_ch_pkt->hdr.size < sizeof(dap_stream_ch_chain_net_srv_pkt_data_hdr_t) ){
diff --git a/modules/channel/chain-net-srv/include/dap_stream_ch_chain_net_srv.h b/modules/channel/chain-net-srv/include/dap_stream_ch_chain_net_srv.h
index e323beced4..d614b20b0c 100644
--- a/modules/channel/chain-net-srv/include/dap_stream_ch_chain_net_srv.h
+++ b/modules/channel/chain-net-srv/include/dap_stream_ch_chain_net_srv.h
@@ -25,6 +25,9 @@
 
 #pragma once
 
+#include "dap_chain_common.h"
+
 int dap_stream_ch_chain_net_srv_init(void);
 void dap_stream_ch_chain_net_srv_deinit(void);
 
+void dap_stream_ch_chain_net_srv_set_srv_uid(dap_stream_ch_t* a_ch, dap_chain_net_srv_uid_t a_srv_uid);
diff --git a/modules/net/dap_chain_node_cli.c b/modules/net/dap_chain_node_cli.c
index d3ebd6e8bc..f5efed9dc6 100644
--- a/modules/net/dap_chain_node_cli.c
+++ b/modules/net/dap_chain_node_cli.c
@@ -996,7 +996,7 @@ int dap_chain_node_cli_init(dap_config_t * g_config)
 
     // vpn client
     dap_chain_node_cli_cmd_item_create ("vpn_client", com_vpn_client, NULL, "VPN client control",
-    "vpn_client [start -addr <server address> -port <server port>| stop | status]\n");
+    "vpn_client [start -addr <server address> -port <server port>| stop | status] -net <net name>\n");
 
 
     // Log
diff --git a/modules/net/dap_chain_node_cli_cmd.c b/modules/net/dap_chain_node_cli_cmd.c
index ad07d981cb..94ee286ba0 100644
--- a/modules/net/dap_chain_node_cli_cmd.c
+++ b/modules/net/dap_chain_node_cli_cmd.c
@@ -3795,7 +3795,7 @@ int com_vpn_client(int a_argc, char ** a_argv, void *arg_func, char **a_str_repl
 {
 #ifndef _WIN32
     enum {
-        CMD_NONE, CMD_START, CMD_STOP, CMD_STATUS
+        CMD_NONE, CMD_INIT, CMD_START, CMD_STOP, CMD_STATUS
     };
     int l_arg_index = 1;
     // find net
@@ -3804,9 +3804,12 @@ int com_vpn_client(int a_argc, char ** a_argv, void *arg_func, char **a_str_repl
         return -2;
 
     int cmd_num = CMD_NONE;
-    if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "start", NULL)) {
-        cmd_num = CMD_START;
+    if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "init", NULL)) {
+        cmd_num = CMD_INIT;
     }
+    if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "start", NULL)) {
+            cmd_num = CMD_START;
+        }
     else if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "stop", NULL)) {
         cmd_num = CMD_STOP;
     }
@@ -3823,6 +3826,48 @@ int com_vpn_client(int a_argc, char ** a_argv, void *arg_func, char **a_str_repl
 
     switch (cmd_num)
     {
+    case CMD_INIT: {
+            const char * l_str_token = NULL; // token name
+            const char * l_str_value_datoshi = NULL;
+            const char * l_str_wallet = NULL; // wallet name
+            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-wallet", &l_str_wallet);
+            if(!l_str_wallet)
+                dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-w", &l_str_wallet);
+
+            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-token", &l_str_token);
+            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-value", &l_str_value_datoshi);
+
+            if(!l_str_wallet) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Wallet not defined, use -w <wallet_name> or -wallet <wallet_name> parameter");
+                break;
+            }
+            if(!l_str_token) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Token not defined, use -token <token_name> parameter");
+                break;
+            }
+            if(!l_str_value_datoshi) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Value of datoshi not defined, use -value <value of datoshi> parameter");
+                break;
+            }
+            uint64_t l_a_value_datoshi = strtoull(l_str_value_datoshi, NULL, 10);
+            if(!l_a_value_datoshi)
+                l_a_value_datoshi = strtoull(l_str_value_datoshi, NULL, 16);
+            if(!l_a_value_datoshi) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Value of datoshi have to be more then 0");
+                break;
+            }
+            int l_res = dap_chain_net_vpn_client_update(l_net, l_str_wallet, l_str_token, l_a_value_datoshi);
+            if(!l_res)
+                dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client init successfully");
+            else{
+                if(l_res==-3)
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client init successfully, but probably not enough founds in the wallet");
+                else
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client not init");
+            }
+            return l_res;
+    }
+        break;
     case CMD_START: {
         const char * l_str_addr = NULL; // for example, "192.168.100.93"
         const char * l_str_port = NULL; // for example, "8079"
@@ -3866,22 +3911,36 @@ int com_vpn_client(int a_argc, char ** a_argv, void *arg_func, char **a_str_repl
             dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client not stopped");
         return res;
     }
-        //break;
+        break;
     case CMD_STATUS:
+        {
+        char *l_wallet_name = NULL, *l_str_token = NULL;
+        uint64_t l_value_datoshi = 0;
+        dap_chain_net_vpn_client_get_wallet_info(l_net, &l_wallet_name, &l_str_token, &l_value_datoshi);
+
+        const char *l_status_txt = "";
         switch (dap_chain_net_vpn_client_status()) {
-//        switch (0){
-        case 0:
-            dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client stopped");
-            return 0;
-        case 1:
-            dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client started");
-            return 0;
-        case -1:
-            dap_chain_node_cli_set_reply_text(a_str_reply, "Can't get VPN state");
-            return -1;
+        case VPN_CLIENT_STATUS_NOT_STARTED:
+            l_status_txt = "VPN client not started";
+            break;
+        case VPN_CLIENT_STATUS_STARTED:
+            l_status_txt = "VPN client started";
+            break;
+        case VPN_CLIENT_STATUS_STOPPED:
+            l_status_txt = "VPN client stopped";
+            break;
+        case VPN_CLIENT_STATUS_CONN_LOST:
+            l_status_txt = "VPN client lost connection";
+            break;
+        default:
+            l_status_txt = "VPN client status unknown";
+            break;
         }
+        dap_chain_node_cli_set_reply_text(a_str_reply, "%s\nused:\nwallet:%s\nreceipt:%u*1e-9 %s", l_status_txt,
+                l_wallet_name, l_value_datoshi, l_str_token);
         break;
     }
+    }
 #endif
     return 0;
 }
diff --git a/modules/net/dap_chain_node_client.c b/modules/net/dap_chain_node_client.c
index b8d1c88a33..84d03e43b6 100644
--- a/modules/net/dap_chain_node_client.c
+++ b/modules/net/dap_chain_node_client.c
@@ -160,22 +160,31 @@ static void s_stage_connected_callback(dap_client_t *a_client, void *a_arg)
                 NODE_ADDR_FP_ARGS_S( l_node_client->remote_node_addr));
         pthread_mutex_lock(&l_node_client->wait_mutex);
         l_node_client->state = NODE_CLIENT_STATE_CONNECTED;
-        // find current channel code
+        pthread_mutex_unlock(&l_node_client->wait_mutex);
+        // set callbacks for C and N channels; for R and S it is not needed
         dap_client_pvt_t * l_client_internal = DAP_CLIENT_PVT(a_client);
-        dap_stream_ch_t * l_ch = NULL;
-        if(l_client_internal && l_client_internal->active_channels)
-            l_ch = dap_client_get_stream_ch(a_client, l_client_internal->active_channels[0]);
+        if(l_client_internal && l_client_internal->active_channels) {
+            size_t l_channels_count = dap_strlen(l_client_internal->active_channels);
+            for(size_t i = 0; i < l_channels_count; i++) {
+                if(dap_chain_node_client_set_callbacks(a_client, l_client_internal->active_channels[i]) == -1) {
+                    log_it(L_WARNING, "No ch_chain channel, can't init notify callback for pkt type CH_CHAIN");
+                }
+            }
+        }
+        /*
+             // find current channel code
+             dap_stream_ch_t * l_ch = NULL;
+             l_ch = dap_client_get_stream_ch(a_client, l_client_internal->active_channels[0]);
         //dap_stream_ch_t * l_ch = dap_client_get_stream_ch(a_client, dap_stream_ch_chain_get_id());
         if(l_ch) {
+            dap_chain_node_client_set_callbacks(dap_client_t *a_client, uint8_t a_ch_id)
             dap_stream_ch_chain_t * l_ch_chain = DAP_STREAM_CH_CHAIN(l_ch);
             l_ch_chain->callback_notify_packet_out = s_ch_chain_callback_notify_packet_out;
             l_ch_chain->callback_notify_packet_in = s_ch_chain_callback_notify_packet_in;
             l_ch_chain->callback_notify_arg = l_node_client;
         } else {
             log_it(L_WARNING, "No ch_chain channel, can't init notify callback for pkt type CH_CHAIN");
-        }
-
-        pthread_mutex_unlock(&l_node_client->wait_mutex);
+        }*/
         if(l_node_client->callback_connected)
             l_node_client->callback_connected(l_node_client, a_arg);
         l_node_client->keep_connection = true;
@@ -494,6 +503,11 @@ dap_chain_node_client_t* dap_chain_client_connect(dap_chain_node_info_t *a_node_
     //dap_client_pvt_ref(DAP_CLIENT_PVT(l_node_client->client));
     // Handshake & connect
     dap_client_go_stage(l_node_client->client, a_stage_target, s_stage_connected_callback);
+    dap_client_pvt_t *l_client_internal = DAP_CLIENT_PVT(l_node_client->client);
+    if(!l_client_internal || l_client_internal->stage_status == STAGE_STATUS_ERROR){
+    	dap_chain_node_client_close(l_node_client);
+    	return NULL;
+    }
     return l_node_client;
 }
 
diff --git a/modules/net/srv/dap_chain_net_srv.c b/modules/net/srv/dap_chain_net_srv.c
index 15b1167389..1fda5aca22 100644
--- a/modules/net/srv/dap_chain_net_srv.c
+++ b/modules/net/srv/dap_chain_net_srv.c
@@ -509,6 +509,7 @@ dap_chain_net_srv_t* dap_chain_net_srv_add(dap_chain_net_srv_uid_t a_uid,dap_cha
         HASH_ADD(hh, s_srv_list, uid, sizeof(l_srv->uid), l_sdata);
     }else{
         log_it(L_ERROR, "Already present service with 0x%016llX ", a_uid.uint64);
+        //l_srv = l_sdata->srv;
     }
     pthread_mutex_unlock(&s_srv_list_mutex);
     return l_srv;
diff --git a/modules/net/srv/dap_chain_net_srv_client.c b/modules/net/srv/dap_chain_net_srv_client.c
index 35c69cbf8d..c9f43454f3 100644
--- a/modules/net/srv/dap_chain_net_srv_client.c
+++ b/modules/net/srv/dap_chain_net_srv_client.c
@@ -24,7 +24,36 @@ along with any CellFrame SDK based project.  If not, see <http://www.gnu.org/lic
 
 #include "dap_common.h"
 
+#include "dap_chain_net_srv.h"
 #include "dap_chain_net_srv_client.h"
 
 
 #define LOG_TAG "dap_chain_net_srv_client"
+
+/*
+ * Init service client
+ * l_uid service id
+ * a_callback_client_success callback to start client service
+ */
+//
+int dap_chain_net_srv_client_init(dap_chain_net_srv_uid_t a_uid,
+        dap_chain_net_srv_callback_data_t a_callback_request,
+        dap_chain_net_srv_callback_data_t a_callback_response_success,
+        dap_chain_net_srv_callback_data_t a_callback_response_error,
+        dap_chain_net_srv_callback_data_t a_callback_receipt_next_success,
+        dap_chain_net_srv_callback_data_t a_callback_client_success,
+        dap_chain_net_srv_callback_data_t a_callback_client_sign_request,
+        void *a_inhertor) {
+
+    dap_chain_net_srv_t *l_srv_custom = dap_chain_net_srv_get(a_uid);
+    if(!l_srv_custom) {
+        l_srv_custom = dap_chain_net_srv_add(a_uid, a_callback_request,
+                a_callback_response_success, a_callback_response_error,
+                a_callback_receipt_next_success);
+    }
+    l_srv_custom->callback_client_success = a_callback_client_success;
+    l_srv_custom->callback_client_sign_request = a_callback_client_sign_request;
+    if(a_inhertor)
+        l_srv_custom->_inhertor = a_inhertor;
+    return 0;
+}
diff --git a/modules/net/srv/dap_chain_net_srv_common.c b/modules/net/srv/dap_chain_net_srv_common.c
index 494923184e..23618345a6 100644
--- a/modules/net/srv/dap_chain_net_srv_common.c
+++ b/modules/net/srv/dap_chain_net_srv_common.c
@@ -46,3 +46,7 @@
 #include "dap_stream.h"
 #include "dap_chain_net_srv_common.h"
 
+uint8_t dap_stream_ch_chain_net_srv_get_id()
+{
+    return 'R';
+}
diff --git a/modules/net/srv/include/dap_chain_net_srv.h b/modules/net/srv/include/dap_chain_net_srv.h
index f12364a226..4673bff67e 100755
--- a/modules/net/srv/include/dap_chain_net_srv.h
+++ b/modules/net/srv/include/dap_chain_net_srv.h
@@ -60,6 +60,11 @@ typedef struct dap_chain_net_srv
     dap_chain_net_srv_callback_ch_t callback_stream_ch_write;
     dap_chain_net_srv_callback_ch_t      callback_stream_ch_closed;
 
+    // Client have to start service
+    dap_chain_net_srv_callback_data_t callback_client_success;
+    // Client have to sign receipt
+    dap_chain_net_srv_callback_data_t callback_client_sign_request;
+
     // Pointer to inheritor object
     void * _inhertor;
 } dap_chain_net_srv_t;
@@ -96,3 +101,12 @@ dap_chain_datum_tx_receipt_t * dap_chain_net_srv_issue_receipt(dap_chain_net_srv
                 dap_chain_net_srv_price_t * a_price, const void * a_ext, size_t a_ext_size
                 );
 
+
+int dap_chain_net_srv_client_init(dap_chain_net_srv_uid_t a_uid,
+        dap_chain_net_srv_callback_data_t a_callback_request,
+        dap_chain_net_srv_callback_data_t a_callback_response_success,
+        dap_chain_net_srv_callback_data_t a_callback_response_error,
+        dap_chain_net_srv_callback_data_t a_callback_receipt_next_success,
+        dap_chain_net_srv_callback_data_t a_callback_client_success,
+        dap_chain_net_srv_callback_data_t a_callback_client_sign_request,
+        void *a_inhertor);
diff --git a/modules/net/srv/include/dap_chain_net_srv_client.h b/modules/net/srv/include/dap_chain_net_srv_client.h
index d385f29c3d..d25801c9cf 100644
--- a/modules/net/srv/include/dap_chain_net_srv_client.h
+++ b/modules/net/srv/include/dap_chain_net_srv_client.h
@@ -23,9 +23,14 @@ along with any CellFrame SDK based project.  If not, see <http://www.gnu.org/lic
 */
 #pragma once
 
+#include <stdint.h>
+#include <time.h>
+
+
 #include "dap_chain_net_srv_common.h"
 #include "dap_chain_net_remote.h"
 
+
 typedef struct dap_chain_net_srv_client
 {
     time_t ts_created;
diff --git a/modules/net/srv/include/dap_chain_net_srv_common.h b/modules/net/srv/include/dap_chain_net_srv_common.h
index 94886a340c..5fd998bc36 100755
--- a/modules/net/srv/include/dap_chain_net_srv_common.h
+++ b/modules/net/srv/include/dap_chain_net_srv_common.h
@@ -176,3 +176,6 @@ DAP_STATIC_INLINE const char * dap_chain_net_srv_price_unit_uid_to_str( dap_chai
         default: return "UNKNOWN";
     }
 }
+
+uint8_t dap_stream_ch_chain_net_srv_get_id();
+
diff --git a/modules/service/vpn/CMakeLists.txt b/modules/service/vpn/CMakeLists.txt
index b5aff03684..e897e26558 100644
--- a/modules/service/vpn/CMakeLists.txt
+++ b/modules/service/vpn/CMakeLists.txt
@@ -15,7 +15,7 @@ endif()
 
 add_library(${PROJECT_NAME} STATIC ${DAP_CHAIN_NET_SRV_VPN_SRCS} ${DAP_CHAIN_NET_SRV_VPN_HEADERS})
 
-target_link_libraries(${PROJECT_NAME} dap_core dap_crypto dap_stream dap_chain dap_chain_crypto dap_chain_net dap_chain_net_srv)
+target_link_libraries(${PROJECT_NAME} dap_core dap_crypto dap_stream dap_stream_ch_chain_net_srv dap_chain dap_chain_crypto dap_chain_net dap_chain_net_srv)
 
 target_include_directories(${PROJECT_NAME} INTERFACE .)
 target_include_directories(${PROJECT_NAME} PUBLIC include)
diff --git a/modules/service/vpn/dap_chain_net_srv_vpn.c b/modules/service/vpn/dap_chain_net_srv_vpn.c
index 4a8f310518..b95b5479d4 100644
--- a/modules/service/vpn/dap_chain_net_srv_vpn.c
+++ b/modules/service/vpn/dap_chain_net_srv_vpn.c
@@ -63,14 +63,19 @@
 
 #include "dap_chain_net.h"
 #include "dap_chain_net_srv.h"
+#include "dap_chain_net_srv_client.h"
 #include "dap_chain_net_srv_vpn.h"
+#include "dap_chain_net_srv_vpn_cdb.h"
 #include "dap_chain_net_srv_stream_session.h"
 #include "dap_chain_net_vpn_client.h"
+#include "dap_chain_net_vpn_client_tun.h"
 #include "dap_chain_ledger.h"
 #include "dap_events.h"
 
 #define LOG_TAG "dap_chain_net_srv_vpn"
 
+#define DAP_TUN_IN_WORKER
+
 #define SF_MAX_EVENTS 256
 
 typedef struct usage_client {
@@ -143,7 +148,7 @@ static void s_tun_destroy(void);
 // Stream callbacks
 static void s_new(dap_stream_ch_t* ch, void* arg);
 static void srv_ch_vpn_delete(dap_stream_ch_t* ch, void* arg);
-static void s_ch_packet_in(dap_stream_ch_t* ch, void* arg);
+static void s_ch_packet_in(dap_stream_ch_t* ch, void* a_arg);
 static void s_ch_packet_out(dap_stream_ch_t* ch, void* arg);
 
 //static int srv_ch_sf_raw_write(uint8_t op_code, const void * data, size_t data_size);
@@ -160,12 +165,21 @@ static void m_es_tun_delete(dap_events_socket_t * a_es, void * arg);
 static void m_es_tun_read(dap_events_socket_t * a_es, void * arg);
 static void m_es_tun_error(dap_events_socket_t * a_es, void * arg);
 
+bool is_dap_tun_in_worker(void)
+{
+#ifdef DAP_TUN_IN_WORKER
+    return true;
+#else
+    return false;
+#endif
+}
+
 //TODO: create .new_callback for event sockets
 int s_tun_event_stream_create()
 {
   static dap_events_socket_callbacks_t l_s_callbacks = {
-          .read_callback = m_es_tun_read,
-          .write_callback = NULL,
+          .read_callback = m_es_tun_read,// for server
+          .write_callback = NULL,// for client
           .error_callback = m_es_tun_error,
           .delete_callback = m_es_tun_delete
   };
@@ -180,6 +194,137 @@ int s_tun_event_stream_create()
 }
 #endif
 
+static int s_callback_client_success(dap_chain_net_srv_t * a_srv, uint32_t a_usage_id, dap_chain_net_srv_client_t * a_srv_client,
+                    const void * a_success, size_t a_success_size)
+{
+    if(!a_srv || !a_srv_client || !a_srv_client->ch || !a_success || a_success_size < sizeof(dap_stream_ch_chain_net_srv_pkt_success_t))
+        return -1;
+    dap_stream_ch_chain_net_srv_pkt_success_t * l_success = (dap_stream_ch_chain_net_srv_pkt_success_t*) a_success;
+
+    dap_chain_net_srv_stream_session_t * l_srv_session =
+            (dap_chain_net_srv_stream_session_t *) a_srv_client->ch->stream->session->_inheritor;
+    dap_chain_net_srv_vpn_t* l_srv_vpn = (dap_chain_net_srv_vpn_t*) a_srv->_inhertor;
+    //a_srv_client->ch->
+    dap_chain_net_t * l_net = dap_chain_net_by_id(l_success->hdr.net_id);
+    dap_chain_net_srv_usage_t *l_usage = dap_chain_net_srv_usage_add(l_srv_session, l_net, a_srv);
+    if(!l_usage)
+        return -2;
+
+    dap_chain_net_srv_ch_vpn_t * l_srv_ch_vpn =
+            (dap_chain_net_srv_ch_vpn_t*) a_srv_client->ch->stream->channel[DAP_CHAIN_NET_SRV_VPN_ID] ?
+                    a_srv_client->ch->stream->channel[DAP_CHAIN_NET_SRV_VPN_ID]->internal : NULL;
+    if ( ! l_srv_ch_vpn ){
+        log_it(L_ERROR, "No VPN service stream channel, its closed?");
+        return -3;
+    }
+    l_srv_ch_vpn->usage_id = l_usage->id;
+    l_usage->is_active = true;
+    l_usage->is_free = true;
+
+    dap_stream_ch_t *l_ch = dap_chain_net_vpn_client_get_stream_ch();
+
+    int remote_sock_id = 0;//l_vpn_pkt->header.sock_id;
+    ch_vpn_socket_proxy_t * sf_sock = NULL;
+    sf_sock = DAP_NEW_Z(ch_vpn_socket_proxy_t);
+    sf_sock->id = remote_sock_id;
+    sf_sock->sock = l_ch->stream->events_socket->socket;
+    sf_sock->ch = l_ch;
+    pthread_mutex_init(&sf_sock->mutex, NULL);
+    dap_chain_net_srv_ch_vpn_t *f = CH_VPN(a_srv_client->ch);
+    //pthread_mutex_lock(&s_sf_socks_mutex);
+    pthread_mutex_lock(&l_srv_ch_vpn->mutex);
+    HASH_ADD_INT(l_srv_ch_vpn->socks, id, sf_sock);
+    pthread_mutex_unlock(&l_srv_ch_vpn->mutex);
+    //HASH_ADD_INT(CH_VPN(a_srv_client->ch)->socks, id, sf_sock);
+    log_it(L_DEBUG, "Added %d sock_id with sock %d to the hash table", sf_sock->id, sf_sock->sock);
+
+    //!!!//l_usage->receipt = ;
+
+    /*
+     dap_chain_net_srv_stream_session_t * l_srv_session = DAP_CHAIN_NET_SRV_STREAM_SESSION( a_ch->stream->session );
+     dap_chain_net_srv_ch_vpn_t *l_ch_vpn = CH_VPN(a_ch);
+     dap_chain_net_srv_usage_t * l_usage = dap_chain_net_srv_usage_find(l_srv_session,  l_ch_vpn->usage_id);
+     if ( ! l_usage->is_active
+     */
+
+    if(l_ch) { // Is present in hash table such destination address
+        size_t l_ipv4_str_len = 0; //dap_strlen(a_ipv4_str);
+        ch_vpn_pkt_t *pkt_out = (ch_vpn_pkt_t*) calloc(1, sizeof(pkt_out->header) + l_ipv4_str_len);
+
+        pkt_out->header.op_code = VPN_PACKET_OP_CODE_VPN_ADDR_REQUEST;
+        //pkt_out->header.sock_id = l_stream->stream->events_socket->socket;
+        //pkt_out->header.op_connect.addr_size = l_ipv4_str_len; //remoteAddrBA.length();
+        //pkt_out->header.op_connect.port = a_port;
+        //memcpy(pkt_out->data, a_ipv4_str, l_ipv4_str_len);
+        sf_sock->pkt_out[sf_sock->pkt_out_size] = pkt_out;
+        sf_sock->pkt_out_size++;
+
+        dap_stream_ch_pkt_write(l_ch, DAP_STREAM_CH_PKT_TYPE_NET_SRV_VPN_DATA, pkt_out,
+                pkt_out->header.op_data.data_size + sizeof(pkt_out->header));
+        dap_stream_ch_set_ready_to_write(l_ch, true);
+        //DAP_DELETE(pkt_out);
+    }
+
+
+
+
+    // usage is present, we've accepted packets
+    dap_stream_ch_set_ready_to_read( l_srv_ch_vpn->ch , true );
+    return 0;
+}
+
+static int callback_client_sign_request(dap_chain_net_srv_t * a_srv, uint32_t a_usage_id, dap_chain_net_srv_client_t * a_srv_client,
+                    const void **a_receipt, size_t a_receipt_size)
+{
+    dap_chain_datum_tx_receipt_t *l_receipt = (dap_chain_datum_tx_receipt_t*)*a_receipt;
+    char *l_gdb_group = dap_strdup_printf("local.%s", DAP_CHAIN_NET_SRV_VPN_CDB_GDB_PREFIX);
+    char *l_wallet_name = dap_chain_global_db_gr_get(dap_strdup("wallet_name"), NULL, l_gdb_group);
+
+    dap_chain_wallet_t *l_wallet = dap_chain_wallet_open(l_wallet_name, dap_chain_wallet_get_path(g_config));
+    if(l_wallet) {
+        dap_enc_key_t *l_enc_key = dap_chain_wallet_get_key(l_wallet, 0);
+        dap_chain_datum_tx_receipt_sign_add(&l_receipt, dap_chain_datum_tx_receipt_get_size(l_receipt), l_enc_key);
+        dap_chain_wallet_close(l_wallet);
+        *a_receipt = l_receipt;
+    }
+    DAP_DELETE(l_gdb_group);
+    DAP_DELETE(l_wallet_name);
+    return 0;
+}
+
+
+/*
+ * Client VPN init (after dap_chain_net_srv_vpn_init!)
+ */
+int dap_chain_net_srv_client_vpn_init(dap_config_t * g_config) {
+    dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_VPN_ID };
+    dap_chain_net_srv_t *l_srv = dap_chain_net_srv_get(l_uid);
+    dap_chain_net_srv_vpn_t* l_srv_vpn = l_srv ? (dap_chain_net_srv_vpn_t*) l_srv->_inhertor : NULL;
+    // if vpn server disabled
+    if(!l_srv_vpn) {
+        l_srv_vpn = DAP_NEW_Z(dap_chain_net_srv_vpn_t);
+        if(l_srv)
+            l_srv->_inhertor = l_srv_vpn;
+        dap_stream_ch_proc_add(DAP_STREAM_CH_ID_NET_SRV_VPN, s_new, srv_ch_vpn_delete, s_ch_packet_in, s_ch_packet_out);
+        pthread_mutex_init(&s_sf_socks_mutex, NULL);
+        pthread_cond_init(&s_sf_socks_cond, NULL);
+    }
+    if(!dap_chain_net_srv_client_init(l_uid, s_callback_requested,
+            s_callback_response_success, s_callback_response_error,
+            s_callback_receipt_next_success,
+            s_callback_client_success,
+            callback_client_sign_request,
+            l_srv_vpn)) {
+        l_srv = dap_chain_net_srv_get(l_uid);
+        //l_srv_vpn = l_srv ? (dap_chain_net_srv_vpn_t*)l_srv->_inhertor : NULL;
+        //l_srv_vpn->parent = l_srv;
+        l_srv->_inhertor = l_srv_vpn;
+    }
+    l_srv_vpn->parent = (dap_chain_net_srv_t*) l_srv;
+
+    return 0;
+}
+
 /**
  * @brief dap_stream_ch_vpn_init Init actions for VPN stream channel
  * @param vpn_addr Zero if only client mode. Address if the node shares its local VPN
@@ -631,101 +776,6 @@ static ch_vpn_pkt_t* srv_ch_sf_raw_read()
     return ret;
 }
 
-/**
- * @brief stream_sf_packet_out Packet Out Ch callback
- * @param ch
- * @param arg
- */
-static void s_ch_packet_out(dap_stream_ch_t* a_ch, void* a_arg)
-{
-    (void) a_arg;
-    ch_vpn_socket_proxy_t * cur, *tmp;
-    dap_chain_net_srv_stream_session_t * l_srv_session = DAP_CHAIN_NET_SRV_STREAM_SESSION( a_ch->stream->session );
-    dap_chain_net_srv_ch_vpn_t *l_ch_vpn = CH_VPN(a_ch);
-
-    dap_chain_net_srv_usage_t * l_usage = dap_chain_net_srv_usage_find(l_srv_session,  l_ch_vpn->usage_id);
-    if ( ! l_usage){
-        log_it(L_NOTICE, "No active usage in list, possible disconnected. Send nothin on this channel");
-        dap_stream_ch_set_ready_to_write(a_ch,false);
-        dap_stream_ch_set_ready_to_read(a_ch,false);
-        return;
-    }
-
-    if ( ! l_usage->is_active ){
-        log_it(L_INFO, "Usage inactivation: switch off packet output channel");
-        dap_stream_ch_set_ready_to_write(a_ch,false);
-        dap_stream_ch_set_ready_to_read(a_ch,false);
-        if (l_usage->clients)
-            dap_stream_ch_pkt_write( l_usage->clients->ch , DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_NOTIFY_STOPPED , NULL, 0 );
-        return;
-    }
-    if ( (! l_usage->is_free) && (! l_usage->receipt) ){
-        log_it(L_WARNING, "No active receipt, switching off");
-        dap_stream_ch_set_ready_to_write(a_ch,false);
-        dap_stream_ch_set_ready_to_read(a_ch,false);
-        if (l_usage->clients)
-            dap_stream_ch_pkt_write( l_usage->clients->ch , DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_NOTIFY_STOPPED , NULL, 0 );
-        return;
-    }
-
-    bool l_is_smth_out = false;
-//    log_it(L_DEBUG,"Socket forwarding packet out callback: %u sockets in hashtable", HASH_COUNT(CH_SF(ch)->socks) );
-    HASH_ITER(hh, l_ch_vpn->socks , cur, tmp)
-    {
-        bool l_signal_to_break = false;
-        pthread_mutex_lock(&(cur->mutex));
-        size_t i;
-        //log_it(L_DEBUG, "Socket with id %d has %u packets in output buffer", cur->id, cur->pkt_out_size);
-        if(cur->pkt_out_size) {
-            for(i = 0; i < cur->pkt_out_size; i++) {
-                ch_vpn_pkt_t * pout = cur->pkt_out[i];
-                if(pout) {
-                    size_t l_wrote_size;
-                    if((l_wrote_size = dap_stream_ch_pkt_write(a_ch, DAP_STREAM_CH_PKT_TYPE_NET_SRV_VPN_DATA, pout,
-                            pout->header.op_data.data_size + sizeof(pout->header)))>0 ) {
-                        l_is_smth_out = true;
-                        DAP_DELETE(pout);
-                        cur->pkt_out[i] = NULL;
-                    } else {
-                        //log_it(L_WARNING,
-                        //        "Buffer is overflowed, breaking cycle to let the upper level cycle drop data to the output socket");
-                        l_is_smth_out = true;
-                        l_signal_to_break = true;
-                        break;
-                    }
-                    s_update_limits (a_ch, l_srv_session, l_usage,l_wrote_size );
-                }
-            }
-        }
-
-        if(l_signal_to_break) {
-            pthread_mutex_unlock(&(cur->mutex));
-            break;
-        }
-        cur->pkt_out_size = 0;
-        if(cur->signal_to_delete) {
-            log_it(L_NOTICE, "Socket id %d got signal to be deleted", cur->id);
-            pthread_mutex_lock(&( CH_VPN(a_ch)->mutex));
-            HASH_DEL(l_ch_vpn->socks, cur);
-            pthread_mutex_unlock(&( CH_VPN(a_ch)->mutex));
-
-            pthread_mutex_lock(&(s_sf_socks_mutex));
-            HASH_DELETE(hh2, sf_socks, cur);
-            HASH_DELETE(hh_sock, sf_socks_client, cur);
-            pthread_mutex_unlock(&(s_sf_socks_mutex));
-
-            pthread_mutex_unlock(&(cur->mutex));
-            s_ch_proxy_delete(cur);
-        } else
-            pthread_mutex_unlock(&(cur->mutex));
-    }
-    if(l_is_smth_out) {
-        a_ch->stream->conn_http->state_write = DAP_HTTP_CLIENT_STATE_DATA;
-    }
-
-    dap_stream_ch_set_ready_to_write(a_ch, l_is_smth_out);
-}
-
 /**
  * @brief s_check_limits
  * @param a_ch
@@ -831,14 +881,27 @@ static void s_update_limits(dap_stream_ch_t * a_ch ,
 
 }
 
+
+static void send_pong_pkt(dap_stream_ch_t* a_ch)
+{
+//    log_it(L_DEBUG,"---------------------------------- PONG!");
+    ch_vpn_pkt_t *pkt_out = (ch_vpn_pkt_t*) calloc(1, sizeof(pkt_out->header));
+    pkt_out->header.op_code = VPN_PACKET_OP_CODE_PONG;
+
+    dap_stream_ch_pkt_write(a_ch, 'd', pkt_out,
+            pkt_out->header.op_data.data_size + sizeof(pkt_out->header));
+    dap_stream_ch_set_ready_to_write(a_ch, true);
+    free(pkt_out);
+}
+
 /**
  * @brief stream_sf_packet_in
  * @param ch
  * @param arg
  */
-void s_ch_packet_in(dap_stream_ch_t* a_ch, void* arg)
+void s_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
 {
-    dap_stream_ch_pkt_t * pkt = (dap_stream_ch_pkt_t *) arg;
+    dap_stream_ch_pkt_t * l_pkt = (dap_stream_ch_pkt_t *) a_arg;
     dap_chain_net_srv_stream_session_t * l_srv_session = DAP_CHAIN_NET_SRV_STREAM_SESSION (a_ch->stream->session );
     dap_chain_net_srv_ch_vpn_t *l_ch_vpn = CH_VPN(a_ch);
     dap_chain_net_srv_usage_t * l_usage = dap_chain_net_srv_usage_find(l_srv_session,  l_ch_vpn->usage_id);
@@ -860,18 +923,33 @@ void s_ch_packet_in(dap_stream_ch_t* a_ch, void* arg)
     // TODO move address leasing to this structure
     dap_chain_net_srv_vpn_t * l_srv_vpn =(dap_chain_net_srv_vpn_t *) l_usage->service->_inhertor;
 
-    if ( pkt->hdr.type == DAP_STREAM_CH_PKT_TYPE_NET_SRV_VPN_CLIENT )
-        dap_chain_net_vpn_client_pkt_in( a_ch, pkt);
-    else {
+    //if ( pkt->hdr.type == DAP_STREAM_CH_PKT_TYPE_NET_SRV_VPN_CLIENT )
+    //    dap_chain_net_vpn_client_pkt_in( a_ch, l_pkt);
+    if(l_pkt->hdr.type == DAP_STREAM_CH_PKT_TYPE_NET_SRV_VPN_DATA) {
         static bool client_connected = false;
-        ch_vpn_pkt_t * l_vpn_pkt = (ch_vpn_pkt_t *) pkt->data;
-        size_t l_vpn_pkt_size = pkt->hdr.size - sizeof (l_vpn_pkt->header);
+        ch_vpn_pkt_t * l_vpn_pkt = (ch_vpn_pkt_t *) l_pkt->data;
+        size_t l_vpn_pkt_size = l_pkt->hdr.size - sizeof (l_vpn_pkt->header);
 
         int remote_sock_id = l_vpn_pkt->header.sock_id;
 
         //log_it(L_DEBUG, "Got SF packet with id %d op_code 0x%02x", remote_sock_id, sf_pkt->header.op_code);
         if(l_vpn_pkt->header.op_code >= 0xb0) { // Raw packets
             switch (l_vpn_pkt->header.op_code) {
+            case VPN_PACKET_OP_CODE_PING:
+                a_ch->stream->events_socket->last_ping_request = time(NULL);
+                send_pong_pkt(a_ch);
+                break;
+            case VPN_PACKET_OP_CODE_PONG:
+                a_ch->stream->events_socket->last_ping_request = time(NULL);
+                break;
+            // for client
+            case VPN_PACKET_OP_CODE_VPN_ADDR_REPLY: { // Assigned address for peer
+                if(ch_sf_tun_addr_leased(CH_VPN(a_ch), l_vpn_pkt, l_vpn_pkt_size) < 0) {
+                    log_it(L_ERROR, "Can't create tun");
+                }
+            }
+            break;
+            // for server
             case VPN_PACKET_OP_CODE_VPN_ADDR_REQUEST: { // Client request after L3 connection the new IP address
                 log_it(L_INFO, "Received address request  ");
                 if ( l_ch_vpn->addr_ipv4.s_addr ){
@@ -978,6 +1056,14 @@ void s_ch_packet_in(dap_stream_ch_t* a_ch, void* arg)
                 }
             }
                 break;
+            // for client only
+            case VPN_PACKET_OP_CODE_VPN_RECV:{
+                a_ch->stream->events_socket->last_ping_request = time(NULL); // not ping, but better  ;-)
+                            ch_sf_tun_send(CH_VPN(a_ch), l_vpn_pkt->data, l_vpn_pkt->header.op_data.data_size);
+            }
+            break;
+
+            // for servier only
             case VPN_PACKET_OP_CODE_VPN_SEND: {
                 struct in_addr in_saddr, in_daddr;
                 in_saddr.s_addr = ((struct iphdr*) l_vpn_pkt->data)->saddr;
@@ -1070,7 +1156,7 @@ void s_ch_packet_in(dap_stream_ch_t* a_ch, void* arg)
                             pthread_mutex_unlock(&sf_sock->mutex);
                         }
                         //log_it(L_INFO, "Send action from %d sock_id (sf_packet size %lu,  ch packet size %lu, have sent %d)"
-                        //        , sf_sock->id, sf_pkt->header.op_data.data_size, pkt->hdr.size, ret);
+                        //        , sf_sock->id, sf_pkt->header.op_data.data_size, l_pkt->hdr.size, ret);
                     }
                         break;
                     case VPN_PACKET_OP_CODE_DISCONNECT: {
@@ -1192,6 +1278,101 @@ void s_ch_packet_in(dap_stream_ch_t* a_ch, void* arg)
     }
 }
 
+/**
+ * @brief stream_sf_packet_out Packet Out Ch callback
+ * @param ch
+ * @param arg
+ */
+static void s_ch_packet_out(dap_stream_ch_t* a_ch, void* a_arg)
+{
+    (void) a_arg;
+    ch_vpn_socket_proxy_t * cur, *tmp;
+    dap_chain_net_srv_stream_session_t * l_srv_session = DAP_CHAIN_NET_SRV_STREAM_SESSION( a_ch->stream->session );
+    dap_chain_net_srv_ch_vpn_t *l_ch_vpn = CH_VPN(a_ch);
+
+    dap_chain_net_srv_usage_t * l_usage = dap_chain_net_srv_usage_find(l_srv_session,  l_ch_vpn->usage_id);
+    if ( ! l_usage){
+        log_it(L_NOTICE, "No active usage in list, possible disconnected. Send nothin on this channel");
+        dap_stream_ch_set_ready_to_write(a_ch,false);
+        dap_stream_ch_set_ready_to_read(a_ch,false);
+        return;
+    }
+
+    if ( ! l_usage->is_active ){
+        log_it(L_INFO, "Usage inactivation: switch off packet output channel");
+        dap_stream_ch_set_ready_to_write(a_ch,false);
+        dap_stream_ch_set_ready_to_read(a_ch,false);
+        if (l_usage->clients)
+            dap_stream_ch_pkt_write( l_usage->clients->ch , DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_NOTIFY_STOPPED , NULL, 0 );
+        return;
+    }
+    if ( (! l_usage->is_free) && (! l_usage->receipt) ){
+        log_it(L_WARNING, "No active receipt, switching off");
+        dap_stream_ch_set_ready_to_write(a_ch,false);
+        dap_stream_ch_set_ready_to_read(a_ch,false);
+        if (l_usage->clients)
+            dap_stream_ch_pkt_write( l_usage->clients->ch , DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_NOTIFY_STOPPED , NULL, 0 );
+        return;
+    }
+
+    bool l_is_smth_out = false;
+//    log_it(L_DEBUG,"Socket forwarding packet out callback: %u sockets in hashtable", HASH_COUNT(CH_SF(ch)->socks) );
+    HASH_ITER(hh, l_ch_vpn->socks , cur, tmp)
+    {
+        bool l_signal_to_break = false;
+        pthread_mutex_lock(&(cur->mutex));
+        size_t i;
+        //log_it(L_DEBUG, "Socket with id %d has %u packets in output buffer", cur->id, cur->pkt_out_size);
+        if(cur->pkt_out_size) {
+            for(i = 0; i < cur->pkt_out_size; i++) {
+                ch_vpn_pkt_t * pout = cur->pkt_out[i];
+                if(pout) {
+                    size_t l_wrote_size;
+                    if((l_wrote_size = dap_stream_ch_pkt_write(a_ch, DAP_STREAM_CH_PKT_TYPE_NET_SRV_VPN_DATA, pout,
+                            pout->header.op_data.data_size + sizeof(pout->header)))>0 ) {
+                        l_is_smth_out = true;
+                        DAP_DELETE(pout);
+                        cur->pkt_out[i] = NULL;
+                    } else {
+                        log_it(L_WARNING, "Buffer is overflowed, breaking cycle to let the upper level cycle drop data to the output socket");
+                        l_is_smth_out = true;
+                        l_signal_to_break = true;
+                        break;
+                    }
+                    s_update_limits (a_ch, l_srv_session, l_usage,l_wrote_size );
+                }
+            }
+        }
+
+        if(l_signal_to_break) {
+            pthread_mutex_unlock(&(cur->mutex));
+            break;
+        }
+        cur->pkt_out_size = 0;
+        if(cur->signal_to_delete) {
+            log_it(L_NOTICE, "Socket id %d got signal to be deleted", cur->id);
+            pthread_mutex_lock(&( CH_VPN(a_ch)->mutex));
+            HASH_DEL(l_ch_vpn->socks, cur);
+            pthread_mutex_unlock(&( CH_VPN(a_ch)->mutex));
+
+            pthread_mutex_lock(&(s_sf_socks_mutex));
+            HASH_DELETE(hh2, sf_socks, cur);
+            HASH_DELETE(hh_sock, sf_socks_client, cur);
+            pthread_mutex_unlock(&(s_sf_socks_mutex));
+
+            pthread_mutex_unlock(&(cur->mutex));
+            s_ch_proxy_delete(cur);
+        } else
+            pthread_mutex_unlock(&(cur->mutex));
+    }
+    if(l_is_smth_out) {
+        if(a_ch->stream->conn_http)
+            a_ch->stream->conn_http->state_write = DAP_HTTP_CLIENT_STATE_DATA;
+    }
+
+    dap_stream_ch_set_ready_to_write(a_ch, l_is_smth_out);
+}
+
 /**
  * @brief stream_sf_disconnect
  * @param sf
@@ -1452,6 +1633,11 @@ void m_es_tun_delete(dap_events_socket_t * a_es, void * arg)
   s_tun_destroy();
 }
 
+void m_es_tun_write(dap_events_socket_t * a_es, void * arg)
+{
+
+}
+
 void m_es_tun_read(dap_events_socket_t * a_es, void * arg)
 {
     const static int tun_MTU = 100000; /// TODO Replace with detection of MTU size
diff --git a/modules/service/vpn/dap_chain_net_srv_vpn_cdb.c b/modules/service/vpn/dap_chain_net_srv_vpn_cdb.c
index 236f73f401..5fe3f20061 100644
--- a/modules/service/vpn/dap_chain_net_srv_vpn_cdb.c
+++ b/modules/service/vpn/dap_chain_net_srv_vpn_cdb.c
@@ -226,9 +226,9 @@ static int s_cli_vpn_cdb(int a_argc, char ** a_argv, void *arg_func, char **a_st
     dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "user", &l_user_str);
 
 
-    // Selected 'user' subcoummand
+    // Selected 'user' subcommand
     if ( l_user_str ){
-        return dap_chain_net_srv_vpn_cdb_auth_cli_cmd(l_user_str,l_arg_index,  a_argc,  a_argv,a_str_reply);
+        return dap_chain_net_srv_vpn_cdb_auth_cli_cmd_user(l_user_str,l_arg_index,  a_argc,  a_argv,a_str_reply);
     }
     return l_ret;
 }
diff --git a/modules/service/vpn/dap_chain_net_srv_vpn_cdb_auth.c b/modules/service/vpn/dap_chain_net_srv_vpn_cdb_auth.c
index 06aef6a66f..9e1fa7b4a0 100644
--- a/modules/service/vpn/dap_chain_net_srv_vpn_cdb_auth.c
+++ b/modules/service/vpn/dap_chain_net_srv_vpn_cdb_auth.c
@@ -139,7 +139,7 @@ void dap_chain_net_srv_vpn_cdb_auth_set_callback(dap_enc_http_callback_t a_callb
  * @param a_password
  * @return
  */
-int dap_chain_net_srv_vpn_cdb_auth_check(const char * a_login, const char * a_password)
+int dap_chain_net_srv_vpn_cdb_auth_check_login(const char * a_login, const char * a_password)
 {
     int l_ret;
 
@@ -204,7 +204,7 @@ static int s_input_validation(const char * str)
  * @param a_str_reply
  * @return
  */
-int dap_chain_net_srv_vpn_cdb_auth_cli_cmd (    const char *a_user_str,int a_arg_index, int a_argc, char ** a_argv, char **a_str_reply)
+int dap_chain_net_srv_vpn_cdb_auth_cli_cmd_user (    const char *a_user_str,int a_arg_index, int a_argc, char ** a_argv, char **a_str_reply)
 {
     int l_ret = 0;
     dap_string_t * l_ret_str = dap_string_new("");
@@ -319,7 +319,7 @@ int dap_chain_net_srv_vpn_cdb_auth_cli_cmd (    const char *a_user_str,int a_arg
         const char * l_password_str = NULL;
         dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, "--password", &l_password_str);
         if ( l_login_str && l_password_str) {
-            int l_check = dap_chain_net_srv_vpn_cdb_auth_check (l_login_str, l_password_str);
+            int l_check = dap_chain_net_srv_vpn_cdb_auth_check_login (l_login_str, l_password_str);
             if ( l_check == 0){
                 dap_string_append_printf(l_ret_str,"OK: Passed password check for '%s'\n",l_login_str );
                 l_ret = 0;
@@ -521,7 +521,7 @@ static void s_http_enc_proc(enc_http_delegate_t *a_delegate, void * a_arg)
                         return;
                     }
 
-                    int l_login_result = dap_chain_net_srv_vpn_cdb_auth_check( l_login, l_password );
+                    int l_login_result = dap_chain_net_srv_vpn_cdb_auth_check_login( l_login, l_password );
                     switch (l_login_result) {
                         case 0:{
                             size_t l_tmp_size;
diff --git a/modules/service/vpn/dap_chain_net_srv_vpn_cdb_server_list.c b/modules/service/vpn/dap_chain_net_srv_vpn_cdb_server_list.c
index e3c807118e..67f7e54c9b 100644
--- a/modules/service/vpn/dap_chain_net_srv_vpn_cdb_server_list.c
+++ b/modules/service/vpn/dap_chain_net_srv_vpn_cdb_server_list.c
@@ -156,7 +156,7 @@ static void s_http_simple_proc(dap_http_simple_t *a_http_simple, void *a_arg)
     dap_string_t *l_reply_str = dap_string_new("[\n");
 
 
-    char *l_client_ip = a_http_simple->http->client->s_ip;//"77.222.110.44"
+    char *l_client_ip = a_http_simple->http->client->s_ip;//"134.209.97.195"
     geoip_info_t *l_geoip_info = chain_net_geoip_get_ip_info(l_client_ip);
 
     log_it(L_DEBUG, "Have %zd chain networks for cdb lists", s_cdb_net_count );
@@ -232,11 +232,22 @@ static void s_http_simple_proc(dap_http_simple_t *a_http_simple, void *a_arg)
 
             int8_t l_client_continent = l_geoip_info ? dap_chain_net_srv_order_continent_to_num(l_geoip_info->continent) : 0;
             // random node on client's continent
-			if (l_client_continent) {
+			if (l_client_continent>0 && l_continents_numbers[l_client_continent]>1) {
 				int l_count = 0;
 				while (l_orders_num > 0) {
 					size_t k = rand() % l_continents_numbers[l_client_continent];
-					dap_chain_net_srv_order_t *l_order = l_orders_pos[k];
+					size_t l_node_pos = -1;
+                    for(size_t j2 = 0; j2 <= l_orders_num; j2++) {
+                        if(k == l_node_numbering[l_client_continent][j2]) {
+                            l_node_pos = j2;
+                            break;
+                        }
+                    }
+                    if(l_node_pos == -1){
+                        // random node for the whole world
+                        l_node_pos = rand() % l_orders_num;
+                    }
+                    dap_chain_net_srv_order_t *l_order = l_orders_pos[l_node_pos];
 					const char *country_code = dap_chain_net_srv_order_get_country_code(l_order);
 					if (country_code) {
 						// only for other countries
diff --git a/modules/service/vpn/dap_chain_net_vpn_client.c b/modules/service/vpn/dap_chain_net_vpn_client.c
index 6ffa1cf15a..7a673697cd 100644
--- a/modules/service/vpn/dap_chain_net_vpn_client.c
+++ b/modules/service/vpn/dap_chain_net_vpn_client.c
@@ -26,6 +26,7 @@
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <time.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <stdlib.h>
@@ -42,19 +43,30 @@
 #include "dap_chain_node_client.h"
 
 #include "dap_stream_ch_proc.h"
+//#include "dap_stream_ch_chain_net_srv.h"
 
+#include "dap_chain_common.h"
+#include "dap_chain_mempool.h"
 #include "dap_chain_net_srv_vpn.h"
+#include "dap_chain_net_srv_vpn_cdb.h" // for DAP_CHAIN_NET_SRV_VPN_CDB_GDB_PREFIX
 #include "dap_chain_net_vpn_client.h"
 
 #include "dap_stream_ch_pkt.h"
+#include "dap_stream_ch_chain_net_srv.h"
+//#include "dap_stream_ch_chain_net_srv.h"
 #include "dap_chain_net_vpn_client_tun.h"
+//#include "dap_chain_net_vpn_client_data.h"
 
-typedef enum dap_http_client_state {
-    DAP_HTTP_CLIENT_STATE_NONE = 0,
-    DAP_HTTP_CLIENT_STATE_START = 1,
-    DAP_HTTP_CLIENT_STATE_HEADERS = 2,
-    DAP_HTTP_CLIENT_STATE_DATA = 3
-} dap_http_client_state_t;
+/*
+ #if !defined( dap_http_client_state_t )
+ typedef enum dap_http_client_state {
+ DAP_HTTP_CLIENT_STATE_NONE = 0,
+ DAP_HTTP_CLIENT_STATE_START = 1,
+ DAP_HTTP_CLIENT_STATE_HEADERS = 2,
+ DAP_HTTP_CLIENT_STATE_DATA = 3
+ } dap_http_client_state_t;
+ #endif
+ */
 
 #define LOG_TAG "vpn_client"
 
@@ -72,44 +84,223 @@ dap_stream_ch_t* dap_chain_net_vpn_client_get_stream_ch(void)
 {
     if(!s_vpn_client)
         return NULL;
-    dap_stream_ch_t *l_stream = dap_client_get_stream_ch(s_vpn_client->client, DAP_STREAM_CH_ID_NET_SRV_VPN );
+    dap_stream_ch_t *l_stream = dap_client_get_stream_ch(s_vpn_client->client, DAP_STREAM_CH_ID_NET_SRV_VPN);
     return l_stream;
 }
 
-
-
 /// TODO convert below callback to processor of stage
 /*
-void s_stage_callback()
+ void s_stage_callback()
+ {
+ char* l_full_path = NULL;
+ const char * l_path = "stream";
+ const char *l_suburl = "globaldb";
+ int l_full_path_size = snprintf(l_full_path, 0, "%s/%s?session_id=%s", DAP_UPLINK_PATH_STREAM, l_suburl,
+ dap_client_get_stream_id(a_client_pvt->client));
+ l_full_path = DAP_NEW_Z_SIZE(char, l_full_path_size + 1);
+ snprintf(l_full_path, l_full_path_size + 1, "%s/%s?session_id=%s", DAP_UPLINK_PATH_STREAM, l_suburl,
+ dap_client_get_stream_id(a_client_pvt->client));
+
+ //dap_client_request(a_client_pvt->client, l_full_path, "12345", 0, m_stream_response, m_stream_error);
+
+ const char *l_add_str = "";
+ // if connect to vpn server
+ const char l_active_vpn_channels[] = { VPN_CLIENT_ID, 0 };
+ if(!dap_strcmp(a_client_pvt->active_channels, l_active_vpn_channels))
+ l_add_str = "\r\nService-Key: test";
+
+ {
+ char *l_message = dap_strdup_printf("GET /%s HTTP/1.1\r\nHost: %s:%d%s\r\n\r\n",
+ l_full_path, a_client_pvt->uplink_addr, a_client_pvt->uplink_port, l_add_str);
+ size_t l_message_size = dap_strlen(l_message);
+ int count = send(a_client_pvt->stream_socket, l_message, l_message_size, 0);
+ DAP_DELETE(l_message);
+ }
+ DAP_DELETE(l_full_path);
+
+ }*/
+
+/**
+ * Get tx_cond_hash
+ *
+ * return: 0 Ok, 1 Already started, <0 Error
+ */
+static dap_chain_hash_fast_t* dap_chain_net_vpn_client_tx_cond_hash(dap_chain_net_t *a_net,
+        dap_chain_wallet_t *a_wallet, const char *a_token_ticker, uint64_t a_value_datoshi)
 {
-    char* l_full_path = NULL;
-    const char * l_path = "stream";
-    const char *l_suburl = "globaldb";
-    int l_full_path_size = snprintf(l_full_path, 0, "%s/%s?session_id=%s", DAP_UPLINK_PATH_STREAM, l_suburl,
-            dap_client_get_stream_id(a_client_pvt->client));
-    l_full_path = DAP_NEW_Z_SIZE(char, l_full_path_size + 1);
-    snprintf(l_full_path, l_full_path_size + 1, "%s/%s?session_id=%s", DAP_UPLINK_PATH_STREAM, l_suburl,
-            dap_client_get_stream_id(a_client_pvt->client));
-
-    //dap_client_request(a_client_pvt->client, l_full_path, "12345", 0, m_stream_response, m_stream_error);
-
-    const char *l_add_str = "";
-    // if connect to vpn server
-    const char l_active_vpn_channels[] = { VPN_CLIENT_ID, 0 };
-    if(!dap_strcmp(a_client_pvt->active_channels, l_active_vpn_channels))
-        l_add_str = "\r\nService-Key: test";
+    uint8_t *l_pkey_b64 = NULL;
+    size_t l_pkey_b64_size = 0;
+
+    // Try to load from gdb
+    size_t l_gdb_group_size = 0;
+    char *l_gdb_group = dap_strdup_printf("local.%s", DAP_CHAIN_NET_SRV_VPN_CDB_GDB_PREFIX);
+    dap_chain_hash_fast_t *l_tx_cond_hash = (dap_chain_hash_fast_t*) dap_chain_global_db_gr_get(
+            dap_strdup("client_tx_cond_hash"), &l_gdb_group_size, l_gdb_group);
+
+    time_t l_tx_cond_ts = 0;
+    // Check for entry size
+    if(l_tx_cond_hash && l_gdb_group_size && l_gdb_group_size != sizeof(dap_chain_hash_fast_t)) {
+        log_it(L_ERROR, "Wrong size of tx condition on database (%zd but expected %zd), may be old entry",
+                l_gdb_group_size, sizeof(dap_chain_hash_fast_t));
+        l_tx_cond_hash = NULL;
+    }
+    // If loaded lets check is it spent or not
+    if(l_tx_cond_hash) {
+        log_it(L_DEBUG, "2791: Search for unspent tx, net %s", a_net);
+        dap_chain_datum_tx_t *l_tx = dap_chain_net_get_tx_by_hash(a_net, l_tx_cond_hash, TX_SEARCH_TYPE_NET_UNSPENT);
+        if(!l_tx) { // If not found - all outs are used. Create new one
+            // pass all chains
+            l_tx = dap_chain_net_get_tx_by_hash(a_net, l_tx_cond_hash, TX_SEARCH_TYPE_NET);
+            DAP_DELETE(l_tx_cond_hash);
+            l_tx_cond_hash = NULL;
+            if(l_tx) {
+                l_tx_cond_ts = (time_t) l_tx->header.ts_created;
+                log_it(L_DEBUG, "2791: got some tx, created %d", l_tx->header.ts_created);
+            }
+        }
+    }
+    if(l_tx_cond_hash)
+        return l_tx_cond_hash;
+
+    //l_pkey_b64 = (char*) dap_chain_global_db_gr_get(dap_strdup("client_pkey"), &l_gdb_group_size, l_gdb_group);
+    dap_enc_key_t *l_enc_key = NULL;
+    if(a_wallet) {
+        l_enc_key = dap_chain_wallet_get_key(a_wallet, 0);
+    }
+    // use default pkey
+    else {
 
-    {
-        char *l_message = dap_strdup_printf("GET /%s HTTP/1.1\r\nHost: %s:%d%s\r\n\r\n",
-                l_full_path, a_client_pvt->uplink_addr, a_client_pvt->uplink_port, l_add_str);
-        size_t l_message_size = dap_strlen(l_message);
-        int count = send(a_client_pvt->stream_socket, l_message, l_message_size, 0);
-        DAP_DELETE(l_message);
     }
-    DAP_DELETE(l_full_path);
+    /*
+     // generate new pub key
+     if(!l_pkey_b64){
+     //if(!l_pub_key_data || !l_pub_key_data_size){
+     char *l_certs_name_str = dap_strdup_printf("client.%s", DAP_CHAIN_NET_SRV_VPN_CDB_GDB_PREFIX);
+     dap_cert_t ** l_certs = NULL;
+     size_t l_certs_size = 0;
+     dap_cert_t * l_cert = NULL;
+     // Load certs or create if not found
+     if(!dap_cert_parse_str_list(l_certs_name_str, &l_certs, &l_certs_size)) { // Load certs
+     const char *l_cert_folder = dap_cert_get_folder(0);
+     // create new cert
+     if(l_cert_folder) {
+     char *l_cert_path = dap_strdup_printf("%s/%s.dcert", l_cert_folder, l_certs_name_str);
+     l_cert = dap_cert_generate(l_certs_name_str, l_cert_path, DAP_ENC_KEY_TYPE_SIG_DILITHIUM);
+     DAP_DELETE(l_cert_path);
+     }
+     }
+     if(l_certs_size > 0)
+     l_cert = l_certs[0];
+     if(l_cert) {
+     size_t l_pub_key_data_size = 0;
+     uint8_t *l_pub_key_data = dap_enc_key_serealize_pub_key(l_cert->enc_key, &l_pub_key_data_size);
+     // save pub key
+     if(l_pub_key_data && l_pub_key_data_size > 0){
+     if(dap_chain_global_db_gr_set(dap_strdup("client_pkey"), l_pub_key_data, l_pub_key_data_size,
+     l_gdb_group)){
+     l_pkey_b64 = l_pub_key_data;
+     l_pkey_b64_size = l_pub_key_data_size;
+     }
+     else
+     DAP_DELETE(l_pub_key_data);
+     }
+     }
+     DAP_DELETE(l_certs_name_str);
+     }*/
+
+    if(!l_enc_key)
+        return NULL;
+
+    // Try to create condition
+    if(!l_tx_cond_hash) {
+        dap_chain_wallet_t *l_wallet_from = a_wallet;
+        log_it(L_DEBUG, "Create tx from wallet %s", l_wallet_from->name);
+        dap_enc_key_t *l_key_from = l_enc_key; //dap_chain_wallet_get_key(l_wallet_from, 0);
+        dap_enc_key_t *l_client_key = l_enc_key;
+        //dap_chain_cell_id_t *xccell = dap_chain_net_get_cur_cell(l_tpl->net);
+        //uint64_t uint64 =dap_chain_net_get_cur_cell(l_tpl->net)->uint64;
+
+        size_t l_pub_key_data_size = 0;
+        uint8_t *l_pub_key_data = dap_enc_key_serealize_pub_key(l_enc_key, &l_pub_key_data_size);
+        // where to take coins for service
+        dap_chain_addr_t *l_addr_from = dap_chain_wallet_get_addr(l_wallet_from, a_net->pub.id);
+        dap_chain_net_srv_price_unit_uid_t l_price_unit = { .enm = SERV_UNIT_SEC };
+        dap_chain_net_srv_uid_t l_srv_uid = { .uint64 = DAP_CHAIN_NET_SRV_VPN_ID };
+        l_tx_cond_hash = dap_chain_proc_tx_create_cond(a_net, l_key_from, l_client_key, l_addr_from,
+                a_token_ticker, a_value_datoshi, 0, l_price_unit, l_srv_uid, 0, l_pub_key_data, l_pub_key_data_size);
+        //char *l_addr_from_str = dap_chain_addr_to_str(l_addr_from);
+        DAP_DELETE(l_addr_from);
+        if(!l_tx_cond_hash) {
+            log_it(L_ERROR, "Can't create condition for user");
+        } else {
+            // save transaction for login
+            dap_chain_global_db_gr_set("client_tx_cond_hash", l_tx_cond_hash, sizeof(dap_chain_hash_fast_t),
+                    l_gdb_group);
+        }
+        //DAP_DELETE(l_addr_from_str);
+        DAP_DELETE(l_pub_key_data);
+    }
+    DAP_DELETE(l_tx_cond_hash);
+    dap_enc_key_delete(l_enc_key);
+    DAP_DELETE(l_gdb_group);
+    return l_tx_cond_hash;
+}
+
+/**
+ * Init VPN client
+ *
+ * return: 0 Ok, 1 Ok, <0 Error
+ */
+
+int dap_chain_net_vpn_client_update(dap_chain_net_t *a_net, const char *a_wallet_name, const char *a_str_token,
+        uint64_t a_value_datoshi)
+{
+    dap_chain_wallet_t *l_wallet = dap_chain_wallet_open(a_wallet_name, dap_chain_wallet_get_path(g_config));
+    if(!l_wallet) {
+        return -1;
+    }
+    size_t l_gdb_group_size = 0;
+    char *l_gdb_group = dap_strdup_printf("local.%s", DAP_CHAIN_NET_SRV_VPN_CDB_GDB_PREFIX);
+    if(!dap_chain_global_db_gr_set(dap_strdup("wallet_name"), (void*) a_wallet_name, dap_strlen(a_wallet_name) + 1,
+            l_gdb_group))
+        return -2;
+    if(!dap_chain_global_db_gr_set(dap_strdup("token_name"), (void*) a_str_token, dap_strlen(a_str_token) + 1,
+            l_gdb_group))
+        return -2;
+    if(!dap_chain_global_db_gr_set(dap_strdup("value_datoshi"), &a_value_datoshi, sizeof(a_value_datoshi), l_gdb_group))
+        return -2;
+    DAP_DELETE(l_gdb_group);
+    dap_chain_hash_fast_t *l_hash = dap_chain_net_vpn_client_tx_cond_hash(a_net, l_wallet, a_str_token,
+            a_value_datoshi);
+    dap_chain_wallet_close(l_wallet);
+    if(!l_hash)
+        return -3;
+    DAP_DELETE(l_hash);
+    return 0;
+}
 
-}*/
+/**
+ * Init VPN client
+ *
+ * return: 0 Ok, 1 Ok, <0 Error
+ */
 
+int dap_chain_net_vpn_client_get_wallet_info(dap_chain_net_t *a_net, char **a_wallet_name, char **a_str_token,
+        uint64_t *a_value_datoshi)
+{
+    size_t l_gdb_group_size = 0;
+    char *l_gdb_group = dap_strdup_printf("local.%s", DAP_CHAIN_NET_SRV_VPN_CDB_GDB_PREFIX);
+    if(a_wallet_name)
+        *a_wallet_name = (char*) dap_chain_global_db_gr_get("wallet_name", NULL, l_gdb_group);
+    if(a_str_token)
+        *a_str_token = (char*) dap_chain_global_db_gr_get("token_name", NULL, l_gdb_group);
+    if(a_value_datoshi) {
+        uint64_t *l_value_datoshi = (uint64_t*) dap_chain_global_db_gr_get("value_datoshi", NULL, l_gdb_group);
+        *a_value_datoshi = l_value_datoshi ? *l_value_datoshi : 0;
+        DAP_DELETE(l_value_datoshi);
+    }
+    return 0;
+}
 
 /**
  * Start VPN client
@@ -121,29 +312,17 @@ int dap_chain_net_vpn_client_start(dap_chain_net_t *a_net, const char *a_ipv4_st
     int l_ret = 0;
     if(!a_ipv4_str) // && !a_ipv6_str)
         return -1;
-    /*
-     dap_client_t *l_client = DAP_NEW_Z(dap_client_t);
-     dap_events_t *l_events = NULL; //dap_events_new();
-     l_client = dap_client_new(l_events, s_stage_status_callback, s_stage_status_error_callback);
-     char l_channels[2] = { VPN_CLIENT_ID, 0 };
-     dap_client_set_active_channels(l_client, l_channels);
-     dap_client_set_uplink(l_client, strdup(a_ip_v4), a_port);
-     dap_client_go_stage(l_client, STAGE_STREAM_STREAMING, s_stage_connected_callback);
-     */
-
     if(!s_node_info)
         s_node_info = DAP_NEW_Z(dap_chain_node_info_t);
     s_node_info->hdr.ext_port = a_port;
 
     dap_client_stage_t l_stage_target = STAGE_STREAM_STREAMING; //DAP_CLIENT_STAGE_STREAM_CTL;//STAGE_STREAM_STREAMING;
-    const char l_active_channels[] = { DAP_STREAM_CH_ID_NET_SRV_VPN , 0 };
+    const char l_active_channels[] = { dap_stream_ch_chain_net_srv_get_id(), DAP_STREAM_CH_ID_NET_SRV_VPN, 0 }; //R, S
     if(a_ipv4_str)
         inet_pton(AF_INET, a_ipv4_str, &(s_node_info->hdr.ext_addr_v4));
     if(a_ipv6_str)
         inet_pton(AF_INET6, a_ipv6_str, &(s_node_info->hdr.ext_addr_v6));
 
-
-
     s_vpn_client = dap_chain_client_connect(s_node_info, l_stage_target, l_active_channels);
     if(!s_vpn_client) {
         log_it(L_ERROR, "Can't connect to VPN server=%s:%d", a_ipv4_str, a_port);
@@ -154,7 +333,7 @@ int dap_chain_net_vpn_client_start(dap_chain_net_t *a_net, const char *a_ipv4_st
         return -2;
     }
     // wait connected
-    int timeout_ms = 500000; //5 sec = 5000 ms
+    int timeout_ms = 5000; //5 sec = 5000 ms
     int res = dap_chain_node_client_wait(s_vpn_client, NODE_CLIENT_STATE_CONNECTED, timeout_ms);
     if(res) {
         log_it(L_ERROR, "No response from VPN server=%s:%d", a_ipv4_str, a_port);
@@ -168,77 +347,36 @@ int dap_chain_net_vpn_client_start(dap_chain_net_t *a_net, const char *a_ipv4_st
     l_ret = dap_chain_net_vpn_client_tun_init(a_ipv4_str);
 
     // send first packet to server
-//    if(0)
     {
-        dap_stream_ch_t *l_ch = dap_chain_net_vpn_client_get_stream_ch();
-        if(l_ch) { // Is present in hash table such destination address
-            size_t l_ipv4_str_len = 0; //dap_strlen(a_ipv4_str);
-            ch_vpn_pkt_t *pkt_out = (ch_vpn_pkt_t*) calloc(1, sizeof(pkt_out->header) + l_ipv4_str_len);
-
-            pkt_out->header.op_code = VPN_PACKET_OP_CODE_VPN_ADDR_REQUEST;
-            //pkt_out->header.sock_id = l_stream->stream->events_socket->socket;
-            //pkt_out->header.op_connect.addr_size = l_ipv4_str_len; //remoteAddrBA.length();
-            //pkt_out->header.op_connect.port = a_port;
-            //memcpy(pkt_out->data, a_ipv4_str, l_ipv4_str_len);
-            dap_stream_ch_pkt_write(l_ch, DAP_STREAM_CH_PKT_TYPE_NET_SRV_VPN_DATA, pkt_out,
-                    pkt_out->header.op_data.data_size + sizeof(pkt_out->header));
+        uint8_t l_ch_id = dap_stream_ch_chain_net_srv_get_id(); // Channel id for chain net request = 'R'
+        dap_stream_ch_t *l_ch = dap_client_get_stream_ch(s_vpn_client->client, l_ch_id);
+        if(l_ch) {
+            dap_stream_ch_chain_net_srv_pkt_request_t l_request;
+            memset(&l_request, 0, sizeof(dap_stream_ch_chain_net_srv_pkt_request_t));
+            l_request.hdr.net_id.uint64 = a_net->pub.id.uint64;
+            l_request.hdr.srv_uid.uint64 = DAP_CHAIN_NET_SRV_VPN_ID;
+            dap_chain_hash_fast_t *l_tx_cond = dap_chain_net_vpn_client_tx_cond_hash(a_net, NULL, NULL, 0);
+            if(l_tx_cond) {
+                memcpy(&l_request.hdr.tx_cond, l_tx_cond, sizeof(dap_chain_hash_fast_t));
+                DAP_DELETE(l_tx_cond);
+            }
+            // set srv id
+            dap_stream_ch_chain_net_srv_set_srv_uid(l_ch, l_request.hdr.srv_uid);
+            //dap_chain_hash_fast_t l_request
+            //.hdr.tx_cond = a_txCond.value();
+//    	    strncpy(l_request->hdr.token, a_token.toLatin1().constData(),sizeof (l_request->hdr.token)-1);
+            dap_stream_ch_pkt_write(l_ch, DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_REQUEST, &l_request, sizeof(l_request));
             dap_stream_ch_set_ready_to_write(l_ch, true);
-            DAP_DELETE(pkt_out);
         }
     }
 
-    /*    dap_stream_ch_t *l_stream = dap_client_get_stream_ch(s_vpn_client->client, VPN_CLIENT_ID);//dap_stream_ch_chain_get_id());
-     size_t l_res = dap_stream_ch_chain_pkt_write(l_stream,
-     VPN_PACKET_OP_CODE_CONNECT, a_net->pub.id, (dap_chain_id_t ) { { 0 } },
-     a_net->pub.cell_id, NULL, 0);*/
-
-    //return l_ret;
-    // send connect packet to server
-    /*    {
-     dap_stream_ch_t *l_stream = dap_chain_net_vpn_client_get_stream();
-     if(l_stream) { // Is present in hash table such destination address
-     size_t l_ipv4_str_len = dap_strlen(a_ipv4_str);
-     ch_vpn_pkt_t *pkt_out = (ch_vpn_pkt_t*) calloc(1, sizeof(pkt_out->header) + l_ipv4_str_len);
-
-     pkt_out->header.op_code = VPN_PACKET_OP_CODE_CONNECT;
-     pkt_out->header.sock_id = l_stream->stream->events_socket->socket;
-     pkt_out->header.op_connect.addr_size = l_ipv4_str_len; //remoteAddrBA.length();
-     pkt_out->header.op_connect.port = a_port;
-     memcpy(pkt_out->data, a_ipv4_str, l_ipv4_str_len);
-
-     //            pkt_out->header.op_code = VPN_PACKET_OP_CODE_VPN_RECV;
-     //            pkt_out->header.sock_id = 123;
-     //            pkt_out->header.op_data.data_size = 0;
-     //memcpy(pkt_out->data, 0, 0);
-     dap_stream_ch_pkt_write(l_stream, DATA_CHANNEL_ID, pkt_out,
-     pkt_out->header.op_data.data_size + sizeof(pkt_out->header));
-     dap_stream_ch_set_ready_to_write(l_stream, true);
-     DAP_DELETE(pkt_out);
-     }
-     }*/
-
-    //l_ret = dap_chain_net_vpn_client_tun_init(a_ipv4_str);
-    /*    {
-     dap_stream_ch_t *l_stream = dap_chain_net_vpn_client_get_stream();
-     if(l_stream) { // Is present in hash table such destination address
-     ch_vpn_pkt_t *pkt_out = (ch_vpn_pkt_t*) calloc(1, sizeof(pkt_out->header) + 0);
-     pkt_out->header.op_code = VPN_PACKET_OP_CODE_VPN_RECV;
-     pkt_out->header.sock_id = 123;
-     pkt_out->header.op_data.data_size = 0;
-     //memcpy(pkt_out->data, 0, 0);
-     dap_stream_ch_pkt_write(l_stream, DATA_CHANNEL_ID, pkt_out,
-     pkt_out->header.op_data.data_size + sizeof(pkt_out->header));
-     dap_stream_ch_set_ready_to_write(l_stream, true);
-     }
-     }*/
-
     return l_ret;
 }
 
 int dap_chain_net_vpn_client_stop(void)
 {
     // delete connection with VPN server
-    if(!s_vpn_client) {
+    if(s_vpn_client) {
         dap_chain_node_client_close(s_vpn_client);
         s_vpn_client = NULL;
     }
@@ -249,12 +387,20 @@ int dap_chain_net_vpn_client_stop(void)
     return l_ret;
 }
 
-int dap_chain_net_vpn_client_status(void)
+dap_chain_net_vpn_client_status_t dap_chain_net_vpn_client_status(void)
 {
+    if(s_vpn_client) {
+        uint8_t l_ch_id = dap_stream_ch_chain_net_srv_get_id(); // Channel id for chain net request = 'R'
+        dap_stream_ch_t *l_ch = dap_client_get_stream_ch(s_vpn_client->client, l_ch_id);
+        if(!l_ch)
+            return VPN_CLIENT_STATUS_CONN_LOST;
+    }
+    else
+        return VPN_CLIENT_STATUS_NOT_STARTED;
     if(!dap_chain_net_vpn_client_tun_status())
         // VPN client started
-        return 1;
-    return 0;
+        return VPN_CLIENT_STATUS_STARTED;
+    return VPN_CLIENT_STATUS_STOPPED;
 }
 
 static void vpn_socket_delete(ch_vpn_socket_proxy_t * sf)
@@ -265,18 +411,6 @@ static void vpn_socket_delete(ch_vpn_socket_proxy_t * sf)
         free(sf);
 }
 
-static void send_pong_pkt(dap_stream_ch_t* a_ch)
-{
-//    log_it(L_DEBUG,"---------------------------------- PONG!");
-    ch_vpn_pkt_t *pkt_out = (ch_vpn_pkt_t*) calloc(1, sizeof(pkt_out->header));
-    pkt_out->header.op_code = VPN_PACKET_OP_CODE_PONG;
-
-    dap_stream_ch_pkt_write(a_ch, 'd', pkt_out,
-            pkt_out->header.op_data.data_size + sizeof(pkt_out->header));
-    dap_stream_ch_set_ready_to_write(a_ch, true);
-    free(pkt_out);
-}
-
 /**
  * @brief dap_chain_net_vpn_client_pkt_in
  * @param a_ch
@@ -301,30 +435,32 @@ void dap_chain_net_vpn_client_pkt_in(dap_stream_ch_t* a_ch, dap_stream_ch_pkt_t*
 //           ,remote_sock_id, l_sf_pkt->header.op_code, l_sf_pkt_data_size );
     if(l_sf_pkt->header.op_code >= 0xb0) { // Raw packets
         switch (l_sf_pkt->header.op_code) {
-        case VPN_PACKET_OP_CODE_VPN_ADDR_REPLY: { // Assigned address for peer
-            if(ch_sf_tun_addr_leased(CH_VPN(a_ch), l_sf_pkt, l_sf_pkt_data_size) < 0) {
-                log_it(L_WARNING, "Can't create tun");
-            }
-        }
-            break;
-        case VPN_PACKET_OP_CODE_VPN_ADDR_REQUEST: // Client request after L3 connection the new IP address
-            log_it(L_WARNING, "Got VPN_PACKET_OP_CODE_VPN_ADDR_REQUEST packet with id %d, it's very strange' ",
-                    remote_sock_id);
-            break;
-        case VPN_PACKET_OP_CODE_VPN_SEND:
-            log_it(L_WARNING, "Got VPN_PACKET_OP_CODE_VPN_SEND packet with id %d, it's very strange' ", remote_sock_id);
-
-        case VPN_PACKET_OP_CODE_VPN_RECV:
-            a_ch->stream->events_socket->last_ping_request = time(NULL); // not ping, but better  ;-)
-            ch_sf_tun_send(CH_VPN(a_ch), l_sf_pkt->data, l_sf_pkt->header.op_data.data_size);
-            break;
-        case VPN_PACKET_OP_CODE_PING:
-            a_ch->stream->events_socket->last_ping_request = time(NULL);
-            send_pong_pkt(a_ch);
-            break;
-        case VPN_PACKET_OP_CODE_PONG:
-            a_ch->stream->events_socket->last_ping_request = time(NULL);
-            break;
+        /*        case VPN_PACKET_OP_CODE_VPN_ADDR_REPLY: { // Assigned address for peer
+         if(ch_sf_tun_addr_leased(CH_VPN(a_ch), l_sf_pkt, l_sf_pkt_data_size) < 0) {
+         log_it(L_WARNING, "Can't create tun");
+         }
+         }
+         break;
+         case VPN_PACKET_OP_CODE_VPN_ADDR_REQUEST: // Client request after L3 connection the new IP address
+         log_it(L_WARNING, "Got VPN_PACKET_OP_CODE_VPN_ADDR_REQUEST packet with id %d, it's very strange' ",
+         remote_sock_id);
+         break;
+         case VPN_PACKET_OP_CODE_VPN_SEND:
+         log_it(L_WARNING, "Got VPN_PACKET_OP_CODE_VPN_SEND packet with id %d, it's very strange' ", remote_sock_id);
+
+         case VPN_PACKET_OP_CODE_VPN_RECV:
+         a_ch->stream->events_socket->last_ping_request = time(NULL); // not ping, but better  ;-)
+         ch_sf_tun_send(CH_VPN(a_ch), l_sf_pkt->data, l_sf_pkt->header.op_data.data_size);
+         break;*/
+        /*
+         case VPN_PACKET_OP_CODE_PING:
+         a_ch->stream->events_socket->last_ping_request = time(NULL);
+         send_pong_pkt(a_ch);
+         break;
+         case VPN_PACKET_OP_CODE_PONG:
+         a_ch->stream->events_socket->last_ping_request = time(NULL);
+         break;
+         */
         default:
             log_it(L_WARNING, "Can't process SF type 0x%02x", l_sf_pkt->header.op_code);
         }
@@ -540,7 +676,8 @@ void dap_chain_net_vpn_client_pkt_out(dap_stream_ch_t* a_ch)
             for(i = 0; i < l_cur->pkt_out_size; i++) {
                 ch_vpn_pkt_t * pout = l_cur->pkt_out[i];
                 if(pout) {
-                    if(dap_stream_ch_pkt_write(a_ch, DAP_STREAM_CH_PKT_TYPE_NET_SRV_VPN_DATA, pout, pout->header.op_data.data_size + sizeof(pout->header))) {
+                    if(dap_stream_ch_pkt_write(a_ch, DAP_STREAM_CH_PKT_TYPE_NET_SRV_VPN_DATA, pout,
+                            pout->header.op_data.data_size + sizeof(pout->header))) {
                         l_is_smth_out = true;
                         if(pout)
                             free(pout);
@@ -587,8 +724,7 @@ void dap_chain_net_vpn_client_pkt_out(dap_stream_ch_t* a_ch)
 int dap_chain_net_vpn_client_init(dap_config_t * g_config)
 {
     pthread_mutex_init(&sf_socks_mutex, NULL);
-
-    return 0;
+    return dap_chain_net_srv_client_vpn_init(g_config);
 }
 
 void dap_chain_net_vpn_client_deinit()
diff --git a/modules/service/vpn/dap_chain_net_vpn_client_tun.c b/modules/service/vpn/dap_chain_net_vpn_client_tun.c
index ef786d30f0..cde51a3266 100644
--- a/modules/service/vpn/dap_chain_net_vpn_client_tun.c
+++ b/modules/service/vpn/dap_chain_net_vpn_client_tun.c
@@ -73,6 +73,7 @@ static char *s_last_used_connection_name = NULL, *s_last_used_connection_device
 
 static pthread_t s_thread_read_tun_id;
 static pthread_mutex_t s_clients_mutex;
+static dap_events_socket_t * s_tun_events_socket = NULL;
 
 //list_addr_element * list_addr_head = NULL;
 //ch_sf_tun_server_t * m_tun_server = NULL;
@@ -146,13 +147,20 @@ static char* get_def_gateway(void)
  *
  * return: connection name or NULL
  */
-static char* get_connection(const char *a_conn_name)
+static char* get_connection(const char *a_conn_name, char **a_connection_dev)
 {
     if(!a_conn_name)
         return NULL;
+    // NAME                UUID                                  TYPE      DEVICE
+    //nodeVPNClient       a2b4cbc4-b8d2-4dd9-ac7f-81d9bf6fa276  tun       --
     char *l_cmd = dap_strdup_printf("nmcli connection show | grep %s | awk '{print $1}'", a_conn_name);
     char* l_connection_name = run_bash_cmd(l_cmd);
     DAP_DELETE(l_cmd);
+    if(a_connection_dev) {
+        l_cmd = dap_strdup_printf("nmcli connection show | grep %s | awk '{print $4}'", a_conn_name);
+        *a_connection_dev = run_bash_cmd(l_cmd);
+        DAP_DELETE(l_cmd);
+    }
     return l_connection_name;
 }
 
@@ -363,6 +371,71 @@ int dap_chain_net_vpn_client_tun_init(const char *a_ipv4_server_str)
     return 0;
 }
 
+
+static void m_client_tun_delete(dap_events_socket_t * a_es, void * arg)
+{
+  log_it(L_DEBUG, __PRETTY_FUNCTION__);
+  //dap_chain_net_vpn_client_tun_delete();
+  log_it(L_NOTICE, "Raw sockets listen thread is stopped");
+}
+
+static void m_client_tun_write(dap_events_socket_t * a_es, void * arg)
+{
+//    log_it(L_WARNING, __PRETTY_FUNCTION__);
+}
+
+static void m_client_tun_read(dap_events_socket_t * a_es, void * arg)
+{
+    const static int tun_MTU = 100000; /// TODO Replace with detection of MTU size
+    uint8_t l_tmp_buf[tun_MTU];
+
+    size_t l_read_ret;
+    log_it(L_WARNING, __PRETTY_FUNCTION__);
+
+    do{
+        l_read_ret = dap_events_socket_read(a_es, l_tmp_buf, sizeof(l_tmp_buf));
+
+        if(l_read_ret > 0) {
+            struct iphdr *iph = (struct iphdr*) l_tmp_buf;
+            struct in_addr in_daddr, in_saddr;
+            in_daddr.s_addr = iph->daddr;
+            in_saddr.s_addr = iph->saddr;
+            char str_daddr[42], str_saddr[42];
+            dap_snprintf(str_saddr, sizeof(str_saddr), "%s",inet_ntoa(in_saddr) );
+            dap_snprintf(str_daddr, sizeof(str_daddr), "%s",inet_ntoa(in_daddr) );
+
+            dap_stream_ch_t *l_stream = dap_chain_net_vpn_client_get_stream_ch();
+            if(l_stream) {
+                // form packet to vpn-server
+                ch_vpn_pkt_t *pkt_out = (ch_vpn_pkt_t*) calloc(1, sizeof(pkt_out->header) + l_read_ret);
+                pkt_out->header.op_code = VPN_PACKET_OP_CODE_VPN_SEND; //VPN_PACKET_OP_CODE_VPN_RECV
+                pkt_out->header.sock_id = s_fd_tun;
+                pkt_out->header.op_data.data_size = l_read_ret;
+                memcpy(pkt_out->data, l_tmp_buf, l_read_ret);
+
+                pthread_mutex_lock(&s_clients_mutex);
+                // sent packet to vpn server
+                dap_stream_ch_pkt_write(l_stream, DAP_STREAM_CH_PKT_TYPE_NET_SRV_VPN_DATA, pkt_out,
+                        pkt_out->header.op_data.data_size + sizeof(pkt_out->header));
+                dap_stream_ch_set_ready_to_write(l_stream, true);
+                pthread_mutex_unlock(&s_clients_mutex);
+
+                DAP_DELETE(pkt_out);
+            }
+            else {
+                log_it(L_DEBUG, "No remote client for income IP packet with addr %s", inet_ntoa(in_daddr));
+            }
+        }
+    }while(l_read_ret > 0);
+
+    dap_events_socket_set_readable(a_es, true);
+}
+
+static void m_client_tun_error(dap_events_socket_t * a_es, void * arg)
+{
+  log_it(L_DEBUG, __PRETTY_FUNCTION__);
+}
+
 int dap_chain_net_vpn_client_tun_create(const char *a_ipv4_addr_str, const char *a_ipv4_gw_str)
 {
     //    char dev[IFNAMSIZ] = { 0 };
@@ -382,9 +455,11 @@ int dap_chain_net_vpn_client_tun_create(const char *a_ipv4_addr_str, const char
     char *l_cmd_del_gw = dap_strdup_printf("ip route del default via %s", s_cur_gw);
     char *l_cmd_ret = run_bash_cmd(l_cmd_del_gw);
     DAP_DELETE(l_cmd_del_gw);
-    if(!l_cmd_del_gw) {
-        log_it(L_ERROR, "Can't delete dafault gateway %s)", s_cur_gw);
-        DAP_DELETE(s_cur_gw);
+    // check gateway
+    char *s_cur_gw_tmp = get_def_gateway();
+    if(s_cur_gw_tmp) {
+        log_it(L_ERROR, "Can't delete default gateway %s)", s_cur_gw);
+        DAP_DELETE(s_cur_gw_tmp);
         return -3;
     }
     DAP_DELETE(l_cmd_ret);
@@ -397,8 +472,7 @@ int dap_chain_net_vpn_client_tun_create(const char *a_ipv4_addr_str, const char
     disableIPV6(s_last_used_connection_device);
 
     // add new default gateway for vpn-server address
-    if(!is_local_address(s_cur_ipv4_server))
-            {
+    if(!is_local_address(s_cur_ipv4_server)) {
         // This route don't need if address is local
         char *l_str_cmd = dap_strdup_printf("route add -host %s gw %s metric 10", s_cur_ipv4_server, s_cur_gw);
         char *l_cmd_ret = run_bash_cmd(l_str_cmd);
@@ -407,7 +481,7 @@ int dap_chain_net_vpn_client_tun_create(const char *a_ipv4_addr_str, const char
     }
 
     // check and delete present connection
-    char *l_conn_present = get_connection(s_conn_name);
+    char *l_conn_present = get_connection(s_conn_name, NULL);
     if(!dap_strcmp(l_conn_present, s_conn_name)) {
         char *l_str_cmd = dap_strdup_printf("nmcli c delete %s", s_conn_name);
         exe_bash_cmd(l_str_cmd);
@@ -422,7 +496,7 @@ int dap_chain_net_vpn_client_tun_create(const char *a_ipv4_addr_str, const char
                 "nmcli connection add type tun con-name %s autoconnect false ifname %s mode tun ip4 %s gw4 %s",
                 s_conn_name, s_dev, a_ipv4_addr_str, a_ipv4_gw_str);
         char *l_cmd_ret = run_bash_cmd(l_cmd_add_con);
-        l_conn_present = get_connection(s_conn_name);
+        l_conn_present = get_connection(s_conn_name, NULL);
         if(dap_strcmp(l_conn_present, s_conn_name))
             l_ret = -1;
         DAP_DELETE(l_cmd_ret);
@@ -468,7 +542,28 @@ int dap_chain_net_vpn_client_tun_create(const char *a_ipv4_addr_str, const char
     }
 
     pthread_mutex_init(&s_clients_mutex, NULL);
-    pthread_create(&s_thread_read_tun_id, NULL, thread_read_tun, NULL);
+
+    if(is_dap_tun_in_worker()) {
+
+        static dap_events_socket_callbacks_t l_s_callbacks = {
+                .read_callback = m_client_tun_read,// for server
+                .write_callback = m_client_tun_write,// for client
+                .error_callback = m_client_tun_error,
+                .delete_callback = m_client_tun_delete
+        };
+
+        s_tun_events_socket = dap_events_socket_wrap_no_add(NULL, s_fd_tun, &l_s_callbacks);
+        s_tun_events_socket->type = DESCRIPTOR_TYPE_FILE;
+        dap_events_socket_create_after(s_tun_events_socket);
+        s_tun_events_socket->_inheritor = NULL;
+
+        return 0;
+    }
+    else {
+        pthread_create(&s_thread_read_tun_id, NULL, thread_read_tun, NULL);
+    }
+
+
     //m_tunDeviceName = dev;
     //m_tunSocket = fd;
     return l_ret;
@@ -476,6 +571,14 @@ int dap_chain_net_vpn_client_tun_create(const char *a_ipv4_addr_str, const char
 
 int dap_chain_net_vpn_client_tun_delete(void)
 {
+    if(is_dap_tun_in_worker())
+    {
+        pthread_mutex_lock(&s_clients_mutex);
+        dap_events_socket_kill_socket(s_tun_events_socket);
+        s_tun_events_socket = NULL;
+        pthread_mutex_unlock(&s_clients_mutex);
+    }
+
     // restore previous routing
     if(!s_conn_name || !s_last_used_connection_name)
         return -1;
@@ -518,27 +621,31 @@ int dap_chain_net_vpn_client_tun_delete(void)
 
 int dap_chain_net_vpn_client_tun_status(void)
 {
-    char *l_str_cmd = get_connection(s_conn_name);
+    char *l_conn_dev = NULL;
+    char *l_str_cmd = get_connection(s_conn_name, &l_conn_dev);
     if(!l_str_cmd)
-        return 0;
+        return -1;
     // connection must be present
-    if(dap_strcmp(l_str_cmd, s_conn_name)) {
+    if(dap_strcmp(l_str_cmd, s_conn_name) || dap_strcmp(l_conn_dev, s_dev)) {
         DAP_DELETE(l_str_cmd);
-        return 0;
+        DAP_DELETE(l_conn_dev);
+        return -2;
     }
     DAP_DELETE(l_str_cmd);
+    DAP_DELETE(l_conn_dev);
 
+    /* alternative method
     char *l_used_connection_name = NULL;
     char *l_used_connection_device = NULL;
     save_current_connection_interface_data(&l_used_connection_name, &l_used_connection_device);
     // connection must be upped
-    if(dap_strcmp(l_used_connection_name, s_conn_name) || dap_strcmp(l_used_connection_device, s_dev)) {
+    if(!s_dev || dap_strcmp(l_used_connection_name, s_conn_name) || dap_strcmp(l_used_connection_device, s_dev)) {
         DAP_DELETE(l_used_connection_name);
         DAP_DELETE(l_used_connection_device);
         return -1;
     }
     DAP_DELETE(l_used_connection_name);
-    DAP_DELETE(l_used_connection_device);
+    DAP_DELETE(l_used_connection_device);*/
 
     // VPN client started
     return 0;
diff --git a/modules/service/vpn/include/dap_chain_net_srv_vpn.h b/modules/service/vpn/include/dap_chain_net_srv_vpn.h
index 014a4225f9..b7896676ff 100644
--- a/modules/service/vpn/include/dap_chain_net_srv_vpn.h
+++ b/modules/service/vpn/include/dap_chain_net_srv_vpn.h
@@ -155,6 +155,10 @@ typedef struct dap_chain_net_srv_vpn
 
 #define CH_VPN(a) ((dap_chain_net_srv_ch_vpn_t *) ((a)->internal) )
 
+bool is_dap_tun_in_worker(void);
+
+int dap_chain_net_srv_client_vpn_init(dap_config_t * g_config);
+
 int dap_chain_net_srv_vpn_init(dap_config_t * g_config);
 void dap_chain_net_srv_vpn_deinit(void);
 
diff --git a/modules/service/vpn/include/dap_chain_net_srv_vpn_cdb_auth.h b/modules/service/vpn/include/dap_chain_net_srv_vpn_cdb_auth.h
index a5df0ae3e7..53345d54e2 100644
--- a/modules/service/vpn/include/dap_chain_net_srv_vpn_cdb_auth.h
+++ b/modules/service/vpn/include/dap_chain_net_srv_vpn_cdb_auth.h
@@ -32,6 +32,6 @@ void dap_chain_net_srv_vpn_cdb_auth_deinit();
 
 void dap_chain_net_srv_vpn_cdb_auth_add_proc(dap_http_t * a_http, const char * a_url);
 void dap_chain_net_srv_vpn_cdb_auth_set_callback(dap_enc_http_callback_t a_callback_success);
-int dap_chain_net_srv_vpn_cdb_auth_cli_cmd (    const char *a_user_str,int a_arg_index, int a_argc, char ** a_argv, char **a_str_reply);
+int dap_chain_net_srv_vpn_cdb_auth_cli_cmd_user (    const char *a_user_str,int a_arg_index, int a_argc, char ** a_argv, char **a_str_reply);
 
-int dap_chain_net_srv_vpn_cdb_auth_check(const char * a_login, const char * a_password);
+int dap_chain_net_srv_vpn_cdb_auth_check_login(const char * a_login, const char * a_password);
diff --git a/modules/service/vpn/include/dap_chain_net_vpn_client.h b/modules/service/vpn/include/dap_chain_net_vpn_client.h
index a75a307f73..4f613e02c8 100644
--- a/modules/service/vpn/include/dap_chain_net_vpn_client.h
+++ b/modules/service/vpn/include/dap_chain_net_vpn_client.h
@@ -29,11 +29,22 @@
 #include "dap_chain_net.h"
 #include "dap_chain_net_srv_vpn.h"
 
+typedef enum dap_chain_net_vpn_client_status_enum{
+    VPN_CLIENT_STATUS_NOT_STARTED=0,
+    VPN_CLIENT_STATUS_STARTED,
+    VPN_CLIENT_STATUS_STOPPED,
+    VPN_CLIENT_STATUS_CONN_LOST,
+} dap_chain_net_vpn_client_status_t;
+
+
 dap_stream_ch_t* dap_chain_net_vpn_client_get_stream_ch(void);
 
+int dap_chain_net_vpn_client_update(dap_chain_net_t *a_net, const char *a_wallet_name, const char *a_str_token, uint64_t a_value_datoshi);
+int dap_chain_net_vpn_client_get_wallet_info(dap_chain_net_t *a_net, char **a_wallet_name, char **a_str_token, uint64_t *a_value_datoshi);
+
 int dap_chain_net_vpn_client_start(dap_chain_net_t *a_net, const char *a_ipv4_str, const char *a_ipv6_str, int a_port);
 int dap_chain_net_vpn_client_stop(void);
-int dap_chain_net_vpn_client_status(void);
+dap_chain_net_vpn_client_status_t dap_chain_net_vpn_client_status(void);
 
 int dap_chain_net_vpn_client_init(dap_config_t * g_config);
 void dap_chain_net_vpn_client_deinit();
-- 
GitLab