From 97d60bbd276161914f6813236d2a441c759e2939 Mon Sep 17 00:00:00 2001
From: Roman Khlopkov <roman.khlopkov@demlabs.net>
Date: Tue, 8 Mar 2022 20:39:44 +0300
Subject: [PATCH] [*] Python services almast ready

---
 dap-sdk/core/src/dap_strfuncs.c               |   2 +-
 dap-sdk/net/client/dap_client_pvt.c           |  16 +-
 dap-sdk/net/client/include/dap_client_pvt.h   |   1 +
 dap-sdk/net/stream/stream/CMakeLists.txt      |   2 +-
 dap-sdk/net/stream/stream/dap_stream.c        | 173 ++++++++++++------
 dap-sdk/net/stream/stream/dap_stream_pkt.c    |   1 +
 .../net/stream/stream/include/dap_stream.h    |   5 +-
 modules/chain/dap_chain_ledger.c              |   6 +-
 .../dap_stream_ch_chain_net_srv.c             |  16 +-
 modules/common/dap_chain_datum_tx_receipt.c   |   5 +-
 modules/common/include/dap_chain_common.h     |   2 +-
 .../include/dap_chain_datum_tx_receipt.h      |   3 +-
 modules/mempool/dap_chain_mempool.c           |   2 +-
 modules/net/dap_chain_node_cli.c              |   4 +-
 modules/net/srv/dap_chain_net_srv.c           |  19 +-
 modules/net/srv/dap_chain_net_srv_client.c    |  13 +-
 modules/net/srv/include/dap_chain_net_srv.h   |   1 +
 17 files changed, 173 insertions(+), 98 deletions(-)

diff --git a/dap-sdk/core/src/dap_strfuncs.c b/dap-sdk/core/src/dap_strfuncs.c
index 58a01af802..11622f085b 100755
--- a/dap-sdk/core/src/dap_strfuncs.c
+++ b/dap-sdk/core/src/dap_strfuncs.c
@@ -29,7 +29,7 @@ bool dap_isstralnum(const char *c)
 
     for (size_t i = 0; i < str_len; i++)
     {
-        if (!isalnum(c[i]))
+        if (!isalnum(c[i]) && c[i] != '_' && c[i] != '-')
             return false;
     }
 
diff --git a/dap-sdk/net/client/dap_client_pvt.c b/dap-sdk/net/client/dap_client_pvt.c
index 9f9bf8a958..dc92421fe3 100644
--- a/dap-sdk/net/client/dap_client_pvt.c
+++ b/dap-sdk/net/client/dap_client_pvt.c
@@ -239,7 +239,7 @@ static bool s_stream_timer_timeout_check(void * a_arg)
     dap_events_socket_t * l_es = dap_worker_esocket_find_uuid(l_worker, *l_es_uuid_ptr);
     if(l_es){
         if (l_es->flags & DAP_SOCK_CONNECTING ){
-            dap_client_pvt_t * l_client_pvt = (dap_client_pvt_t *)l_es->_inheritor;
+            dap_client_pvt_t * l_client_pvt = ESOCKET_CLIENT_PVT(l_es);
             if (dap_client_pvt_find(l_client_pvt->uuid)) {
                 log_it(L_WARNING,"Connecting timeout for stream uplink request http://%s:%u/, possible network problems or host is down",
                        l_client_pvt->uplink_addr, l_client_pvt->uplink_port);
@@ -280,7 +280,7 @@ static bool s_stream_timer_timeout_after_connected_check(void * a_arg)
 
     dap_events_socket_t * l_es = dap_worker_esocket_find_uuid(l_worker, *l_es_uuid_ptr);
     if( l_es ){
-        dap_client_pvt_t * l_client_pvt = (dap_client_pvt_t *)l_es->_inheritor;
+        dap_client_pvt_t * l_client_pvt = ESOCKET_CLIENT_PVT(l_es);
         if (dap_client_pvt_find(l_client_pvt->uuid)) {
             if ( time(NULL)- l_client_pvt->ts_last_active >= s_client_timeout_active_after_connect_seconds){
 
@@ -321,7 +321,7 @@ static bool s_enc_init_delay_before_request_timer_callback(void * a_arg)
     dap_worker_t * l_worker = dap_events_get_current_worker(dap_events_get_default());
     dap_events_socket_t * l_es = dap_worker_esocket_find_uuid(l_worker, *l_es_uuid_ptr);
     if(l_es){
-        dap_client_pvt_t * l_client_pvt =(dap_client_pvt_t*) l_es->_inheritor;
+        dap_client_pvt_t *l_client_pvt = ESOCKET_CLIENT_PVT(l_es);
         s_stage_status_after(l_client_pvt);
     }
     DAP_DEL_Z(l_es_uuid_ptr);
@@ -1220,7 +1220,7 @@ static void s_stage_stream_streaming(dap_client_t * a_client, void* arg)
  */
 static void s_stream_es_callback_connected(dap_events_socket_t * a_es)
 {
-    dap_client_pvt_t * l_client_pvt =(dap_client_pvt_t*) a_es->_inheritor;
+    dap_client_pvt_t *l_client_pvt = ESOCKET_CLIENT_PVT(a_es);
     s_stream_connected(l_client_pvt);
 }
 
@@ -1234,7 +1234,7 @@ static void s_stream_es_callback_delete(dap_events_socket_t *a_es, void *arg)
     (void) arg;
     log_it(L_INFO, "Stream delete callback");
 
-    dap_client_pvt_t *l_client_pvt = (dap_client_pvt_t *)a_es->_inheritor;
+    dap_client_pvt_t *l_client_pvt = ESOCKET_CLIENT_PVT(a_es);
     a_es->_inheritor = NULL; // To prevent delete in reactor
 
     if(l_client_pvt == NULL) {
@@ -1270,7 +1270,7 @@ static void s_stream_es_callback_delete(dap_events_socket_t *a_es, void *arg)
 static void s_stream_es_callback_read(dap_events_socket_t * a_es, void * arg)
 {
     (void) arg;
-    dap_client_pvt_t * l_client_pvt =(dap_client_pvt_t *) a_es->_inheritor;
+    dap_client_pvt_t *l_client_pvt = ESOCKET_CLIENT_PVT(a_es);
 
     l_client_pvt->ts_last_active = time(NULL);
     switch (l_client_pvt->stage) {
@@ -1316,7 +1316,7 @@ static void s_stream_es_callback_read(dap_events_socket_t * a_es, void * arg)
 static void s_stream_es_callback_write(dap_events_socket_t * a_es, void * arg)
 {
     (void) arg;
-    dap_client_pvt_t * l_client_pvt = a_es->_inheritor;
+    dap_client_pvt_t *l_client_pvt = ESOCKET_CLIENT_PVT(a_es);
     l_client_pvt->ts_last_active = time(NULL);
     if (l_client_pvt->stage_status == STAGE_STATUS_ERROR || !l_client_pvt->stream)
         return;
@@ -1351,7 +1351,7 @@ static void s_stream_es_callback_write(dap_events_socket_t * a_es, void * arg)
  */
 static void s_stream_es_callback_error(dap_events_socket_t * a_es, int a_error)
 {
-    dap_client_pvt_t *l_client_pvt = (dap_client_pvt_t *) a_es->_inheritor;
+    dap_client_pvt_t *l_client_pvt = ESOCKET_CLIENT_PVT(a_es);
     if (!l_client_pvt)
         return;
     l_client_pvt = dap_client_pvt_find(l_client_pvt->uuid);
diff --git a/dap-sdk/net/client/include/dap_client_pvt.h b/dap-sdk/net/client/include/dap_client_pvt.h
index f663c1affb..ce9e00607d 100644
--- a/dap-sdk/net/client/include/dap_client_pvt.h
+++ b/dap-sdk/net/client/include/dap_client_pvt.h
@@ -100,6 +100,7 @@ typedef struct dap_client_internal
 } dap_client_pvt_t;
 
 #define DAP_CLIENT_PVT(a) (a ? (dap_client_pvt_t*) a->_internal : NULL)
+#define ESOCKET_CLIENT_PVT(a) (a ? (dap_client_pvt_t *) a->_inheritor : NULL)
 
 int dap_client_pvt_init();
 void dap_client_pvt_deinit();
diff --git a/dap-sdk/net/stream/stream/CMakeLists.txt b/dap-sdk/net/stream/stream/CMakeLists.txt
index 16fb98fc4e..11d4b6896a 100755
--- a/dap-sdk/net/stream/stream/CMakeLists.txt
+++ b/dap-sdk/net/stream/stream/CMakeLists.txt
@@ -7,7 +7,7 @@ file(GLOB STREAM_HDRS include/*.h)
 add_library(${PROJECT_NAME} STATIC ${STREAM_SRCS} ${STREAM_HDRS})
 
 target_link_libraries(dap_stream dap_core dap_server_core dap_crypto
-    dap_http_server dap_enc_server dap_session dap_stream_ch)
+    dap_http_server dap_enc_server dap_session dap_stream_ch dap_client)
 
 target_include_directories(dap_stream INTERFACE .)
 target_include_directories(${PROJECT_NAME} PUBLIC include)
diff --git a/dap-sdk/net/stream/stream/dap_stream.c b/dap-sdk/net/stream/stream/dap_stream.c
index 9b65107b3a..05299ac450 100644
--- a/dap-sdk/net/stream/stream/dap_stream.c
+++ b/dap-sdk/net/stream/stream/dap_stream.c
@@ -52,6 +52,7 @@
 #include "dap_http_client.h"
 #include "dap_http_header.h"
 #include "dap_stream_worker.h"
+#include "dap_client_pvt.h"
 
 #define LOG_TAG "dap_stream"
 #define HEADER_WITH_SIZE_FIELD 12  //This count of bytes enough for allocate memory for stream packet
@@ -67,6 +68,8 @@ static void s_http_client_data_read(dap_http_client_t * a_http_client, void * a_
 
 static void s_esocket_callback_worker_assign(dap_events_socket_t * a_esocket, dap_worker_t * a_worker);
 static void s_esocket_callback_worker_unassign(dap_events_socket_t * a_esocket, dap_worker_t * a_worker);
+static void s_client_callback_worker_assign(dap_events_socket_t * a_esocket, dap_worker_t * a_worker);
+static void s_client_callback_worker_unassign(dap_events_socket_t * a_esocket, dap_worker_t * a_worker);
 
 static void s_esocket_data_read(dap_events_socket_t* a_esocket, void * a_arg);
 static void s_esocket_write(dap_events_socket_t* a_esocket, void * a_arg);
@@ -78,7 +81,8 @@ static dap_stream_t * s_stream_new(dap_http_client_t * a_http_client); // Create
 static void s_http_client_new(dap_http_client_t * a_esocket, void * a_arg);
 static void s_http_client_delete(dap_http_client_t * a_esocket, void * a_arg);
 
-static bool s_callback_keepalive( void * a_arg);
+static bool s_callback_server_keepalive(void *a_arg);
+static bool s_callback_client_keepalive(void *a_arg);
 
 static bool s_dump_packet_headers = false;
 static bool s_debug = false;
@@ -259,19 +263,44 @@ void check_session( unsigned int a_id, dap_events_socket_t *a_esocket )
  */
 dap_stream_t * s_stream_new(dap_http_client_t * a_http_client)
 {
-    dap_stream_t * ret= DAP_NEW_Z(dap_stream_t);
-
-    ret->esocket = a_http_client->esocket;
-    ret->stream_worker = (dap_stream_worker_t*) a_http_client->esocket->worker->_inheritor;
-    ret->conn_http=a_http_client;
-    ret->buf_defrag_size = 0;
-    ret->seq_id = 0;
-    ret->client_last_seq_id_packet = (size_t)-1;
+    dap_stream_t *l_ret= DAP_NEW_Z(dap_stream_t);
+
+    l_ret->esocket = a_http_client->esocket;
+    l_ret->stream_worker = (dap_stream_worker_t *)a_http_client->esocket->worker->_inheritor;
+    l_ret->conn_http=a_http_client;
+    l_ret->buf_defrag_size = 0;
+    l_ret->seq_id = 0;
+    l_ret->client_last_seq_id_packet = (size_t)-1;
+    // Start server keep-alive timer
+    dap_events_socket_uuid_t *l_es_uuid = DAP_NEW_Z(dap_events_socket_uuid_t);
+    *l_es_uuid = l_ret->esocket->uuid;
+    l_ret->keepalive_timer = dap_timerfd_start_on_worker(l_ret->esocket->worker,
+                                                         STREAM_KEEPALIVE_TIMEOUT * 1000,
+                                                         (dap_timerfd_callback_t)s_callback_server_keepalive,
+                                                         l_es_uuid);
+    l_ret->esocket->callbacks.worker_assign_callback = s_esocket_callback_worker_assign;
+    l_ret->esocket->callbacks.worker_unassign_callback = s_esocket_callback_worker_unassign;
+    a_http_client->_inheritor = l_ret;
+    log_it(L_NOTICE,"New stream instance");
+    return l_ret;
+}
 
-    a_http_client->_inheritor=ret;
 
-    log_it(L_NOTICE,"New stream instance");
-    return ret;
+/**
+ * @brief dap_stream_new_es
+ * @param a_es
+ * @return
+ */
+dap_stream_t* dap_stream_new_es_client(dap_events_socket_t * a_esocket)
+{
+    dap_stream_t *l_ret = DAP_NEW_Z(dap_stream_t);
+    l_ret->esocket = a_esocket;
+    l_ret->esocket_uuid = a_esocket->uuid;
+    l_ret->buf_defrag_size=0;
+    l_ret->is_client_to_uplink = true;
+    l_ret->esocket->callbacks.worker_assign_callback = s_client_callback_worker_assign;
+    l_ret->esocket->callbacks.worker_unassign_callback = s_client_callback_worker_unassign;
+    return l_ret;
 }
 
 
@@ -314,23 +343,6 @@ static void s_esocket_callback_delete(dap_events_socket_t* a_esocket, void * a_a
     dap_stream_delete(l_stream);
 }
 
-
-/**
- * @brief dap_stream_new_es
- * @param a_es
- * @return
- */
-dap_stream_t* dap_stream_new_es_client(dap_events_socket_t * a_esocket)
-{
-    dap_stream_t * ret= DAP_NEW_Z(dap_stream_t);
-    ret->esocket = a_esocket;
-    ret->esocket_uuid = a_esocket->uuid;
-    ret->buf_defrag_size=0;
-    ret->is_client_to_uplink = true;
-    return ret;
-}
-
-
 /**
  * @brief stream_header_read Read headers callback for HTTP
  * @param a_http_client HTTP client structure
@@ -412,16 +424,6 @@ static void s_http_client_headers_write(dap_http_client_t * a_http_client, void
 
         a_http_client->state_read=DAP_HTTP_CLIENT_STATE_DATA;
         dap_events_socket_set_readable_unsafe(a_http_client->esocket,true);
-        // Connection is established, setting up keepalive timer
-        if (!l_stream->keepalive_timer) {
-            dap_events_socket_uuid_t * l_es_uuid= DAP_NEW_Z(dap_events_socket_uuid_t);
-            *l_es_uuid = a_http_client->esocket->uuid;
-            l_stream->keepalive_timer = dap_timerfd_start_on_worker(a_http_client->esocket->worker,
-                                                                    STREAM_KEEPALIVE_TIMEOUT * 1000,
-                                                                    s_callback_keepalive,
-                                                                    l_es_uuid);
-        }
-
     }
 }
 
@@ -452,16 +454,14 @@ static void s_esocket_callback_worker_assign(dap_events_socket_t * a_esocket, da
     assert(l_http_client);
     dap_stream_t * l_stream = DAP_STREAM(l_http_client);
     assert(l_stream);
-    if (a_esocket->type == DESCRIPTOR_TYPE_SOCKET_UDP ||
-            (l_http_client->state_read == DAP_HTTP_CLIENT_STATE_DATA && l_http_client->state_write == DAP_HTTP_CLIENT_STATE_DATA )) {
-        if (!l_stream->keepalive_timer) {
-            dap_events_socket_uuid_t * l_es_uuid= DAP_NEW_Z(dap_events_socket_uuid_t);
-            *l_es_uuid = a_esocket->uuid;
-            l_stream->keepalive_timer = dap_timerfd_start_on_worker(a_worker,
-                                                                    STREAM_KEEPALIVE_TIMEOUT * 1000,
-                                                                    (dap_timerfd_callback_t)s_callback_keepalive,
-                                                                    l_es_uuid);
-        }
+    // Restart server keepalive timer if it was unassigned before
+    if (!l_stream->keepalive_timer) {
+        dap_events_socket_uuid_t * l_es_uuid= DAP_NEW_Z(dap_events_socket_uuid_t);
+        *l_es_uuid = a_esocket->uuid;
+        l_stream->keepalive_timer = dap_timerfd_start_on_worker(a_worker,
+                                                                STREAM_KEEPALIVE_TIMEOUT * 1000,
+                                                                (dap_timerfd_callback_t)s_callback_server_keepalive,
+                                                                l_es_uuid);
     }
 }
 
@@ -482,6 +482,35 @@ static void s_esocket_callback_worker_unassign(dap_events_socket_t * a_esocket,
     l_stream->keepalive_timer = NULL;
 }
 
+static void s_client_callback_worker_assign(dap_events_socket_t * a_esocket, dap_worker_t * a_worker)
+{
+    dap_client_pvt_t *l_client_pvt = ESOCKET_CLIENT_PVT(a_esocket);
+    assert(l_client_pvt);
+    dap_stream_t *l_stream = l_client_pvt->stream;
+    assert(l_stream);
+    // Start client keepalive timer or restart it, if it was unassigned before
+    if (!l_stream->keepalive_timer) {
+        dap_events_socket_uuid_t * l_es_uuid= DAP_NEW_Z(dap_events_socket_uuid_t);
+        *l_es_uuid = a_esocket->uuid;
+        l_stream->keepalive_timer = dap_timerfd_start_on_worker(a_worker,
+                                                                STREAM_KEEPALIVE_TIMEOUT * 1000,
+                                                                (dap_timerfd_callback_t)s_callback_client_keepalive,
+                                                                l_es_uuid);
+    }
+}
+
+static void s_client_callback_worker_unassign(dap_events_socket_t * a_esocket, dap_worker_t * a_worker)
+{
+    UNUSED(a_worker);
+    dap_client_pvt_t *l_client_pvt = ESOCKET_CLIENT_PVT(a_esocket);
+    assert(l_client_pvt);
+    dap_stream_t *l_stream = l_client_pvt->stream;
+    assert(l_stream);
+    DAP_DEL_Z(l_stream->keepalive_timer->callback_arg);
+    dap_timerfd_delete(l_stream->keepalive_timer);
+    l_stream->keepalive_timer = NULL;
+}
+
 
 /**
  * @brief s_data_read
@@ -558,8 +587,7 @@ static void s_http_client_data_read(dap_http_client_t * a_http_client, void * ar
  */
 static void s_http_client_new(dap_http_client_t * a_http_client, void * arg)
 {
-    a_http_client->esocket->callbacks.worker_assign_callback = s_esocket_callback_worker_assign;
-    a_http_client->esocket->callbacks.worker_unassign_callback = s_esocket_callback_worker_unassign;
+
 }
 
 
@@ -760,6 +788,7 @@ size_t dap_stream_data_proc_read (dap_stream_t *a_stream)
  */
 static void s_stream_proc_pkt_in(dap_stream_t * a_stream)
 {
+    a_stream->is_active = true;
     dap_stream_pkt_t * l_pkt = a_stream->pkt_buf_in;
     size_t l_pkt_size = a_stream->pkt_buf_in_data_size;
     a_stream->pkt_buf_in=NULL;
@@ -814,18 +843,15 @@ static void s_stream_proc_pkt_in(dap_stream_t * a_stream)
         DAP_DELETE(srv_pkt);
     } break;
     case STREAM_PKT_TYPE_KEEPALIVE: {
-        log_it(L_DEBUG, "Keep alive check recieved");
+        //log_it(L_DEBUG, "Keep alive check recieved");
         dap_stream_pkt_hdr_t l_ret_pkt = {};
         l_ret_pkt.type = STREAM_PKT_TYPE_ALIVE;
         memcpy(l_ret_pkt.sig, c_dap_stream_sig, sizeof(l_ret_pkt.sig));
         dap_events_socket_write_unsafe(a_stream->esocket, &l_ret_pkt, sizeof(l_ret_pkt));
-        // Reset client keepalive timer
-        if (a_stream->keepalive_timer) {
-            dap_timerfd_reset(a_stream->keepalive_timer);
-        }
     } break;
     case STREAM_PKT_TYPE_ALIVE:
-        log_it(L_DEBUG, "Keep alive response recieved");
+        a_stream->is_active = false; // To prevent keep-alive concurrency
+        //log_it(L_DEBUG, "Keep alive response recieved");
         break;
     default:
         log_it(L_WARNING, "Unknown header type");
@@ -868,15 +894,30 @@ static bool s_detect_loose_packet(dap_stream_t * a_stream)
  * @param a_arg
  * @return
  */
-static bool s_callback_keepalive( void * a_arg)
+static bool s_callback_keepalive(void *a_arg, bool a_server_side)
 {
     if (!a_arg)
         return false;
     dap_events_socket_uuid_t * l_es_uuid = (dap_events_socket_uuid_t*) a_arg;
     dap_worker_t * l_worker = dap_events_get_current_worker(dap_events_get_default());
     dap_events_socket_t * l_es = dap_worker_esocket_find_uuid(l_worker, *l_es_uuid);
-    if( l_es){
-        //if(s_debug)
+    if (l_es) {
+        dap_stream_t *l_stream = NULL;
+        if (a_server_side) {
+            dap_http_client_t *l_http_client = DAP_HTTP_CLIENT(l_es);
+            assert(l_http_client);
+            l_stream = DAP_STREAM(l_http_client);
+        } else {
+            dap_client_pvt_t *l_client_pvt = ESOCKET_CLIENT_PVT(l_es);
+            assert(l_client_pvt);
+            l_stream = l_client_pvt->stream;
+        }
+        assert(l_stream);
+        if (l_stream->is_active) {
+            l_stream->is_active = false;
+            return true;
+        }
+        if(s_debug)
             log_it(L_DEBUG,"Keepalive for sock fd %"DAP_FORMAT_SOCKET" uuid 0x%016"DAP_UINT64_FORMAT_x, l_es->socket, *l_es_uuid);
         dap_stream_pkt_hdr_t l_pkt = {};
         l_pkt.type = STREAM_PKT_TYPE_KEEPALIVE;
@@ -884,9 +925,19 @@ static bool s_callback_keepalive( void * a_arg)
         dap_events_socket_write_unsafe( l_es, &l_pkt, sizeof(l_pkt));
         return true;
     }else{
-        //if(s_debug)
+        if(s_debug)
             log_it(L_INFO,"Keepalive for sock uuid %016"DAP_UINT64_FORMAT_x" removed", *l_es_uuid);
         DAP_DELETE(l_es_uuid);
         return false; // Socket is removed from worker
     }
 }
+
+static bool s_callback_client_keepalive(void *a_arg)
+{
+    return s_callback_keepalive(a_arg, false);
+}
+
+static bool s_callback_server_keepalive(void *a_arg)
+{
+    return s_callback_keepalive(a_arg, true);
+}
diff --git a/dap-sdk/net/stream/stream/dap_stream_pkt.c b/dap-sdk/net/stream/stream/dap_stream_pkt.c
index 38d14061d3..5031551bd0 100644
--- a/dap-sdk/net/stream/stream/dap_stream_pkt.c
+++ b/dap-sdk/net/stream/stream/dap_stream_pkt.c
@@ -138,6 +138,7 @@ size_t dap_stream_pkt_read_unsafe( dap_stream_t * a_stream, dap_stream_pkt_t * a
 
 size_t dap_stream_pkt_write_unsafe(dap_stream_t * a_stream, const void * a_data, size_t a_data_size)
 {
+    a_stream->is_active = true;
     size_t ret=0;
     dap_stream_pkt_hdr_t pkt_hdr;
 
diff --git a/dap-sdk/net/stream/stream/include/dap_stream.h b/dap-sdk/net/stream/stream/include/dap_stream.h
index b6685a93c4..519a4c2066 100644
--- a/dap-sdk/net/stream/stream/include/dap_stream.h
+++ b/dap-sdk/net/stream/stream/include/dap_stream.h
@@ -53,16 +53,15 @@ typedef struct dap_stream {
     int id;
     dap_stream_session_t * session;
     dap_events_socket_t * esocket; // Connection
-    // uint128_t esocket_uuid;
-    uint64_t esocket_uuid;
+    dap_events_socket_uuid_t esocket_uuid;
     dap_stream_worker_t * stream_worker;
     struct dap_http_client * conn_http; // HTTP-specific
 
     dap_timerfd_t *keepalive_timer;
+    bool is_active;
 
     char * service_key;
 
-    bool is_live;
     bool is_client_to_uplink ;
 
     struct dap_stream_pkt * in_pkt;
diff --git a/modules/chain/dap_chain_ledger.c b/modules/chain/dap_chain_ledger.c
index 7745696550..397d98c451 100644
--- a/modules/chain/dap_chain_ledger.c
+++ b/modules/chain/dap_chain_ledger.c
@@ -2384,9 +2384,11 @@ int dap_chain_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx,
         // If debug mode dump the UTXO
         if (dap_log_level_get() == L_DEBUG && s_debug_more) {
             for (size_t i =0; i < (size_t) l_item_tmp->cache_data.n_outs; i++){
-                dap_chain_tx_out_t * l_tx_out = l_tist_tmp->data;
+                dap_chain_tx_out_t *l_tx_out = l_tist_tmp->data;
+                if (l_tx_out->header.type != TX_ITEM_TYPE_OUT)
+                    continue;
                 char * l_tx_out_addr_str = dap_chain_addr_to_str( &l_tx_out->addr );
-                log_it(L_DEBUG,"Added tx out to %s",l_tx_out_addr_str );
+                log_it(L_DEBUG, "Added tx out to %s", l_tx_out_addr_str);
                 DAP_DELETE (l_tx_out_addr_str);
             }
         }
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 918931a368..4480e07e57 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
@@ -134,6 +134,7 @@ static bool s_grace_period_control(dap_chain_net_srv_grace_t *a_grace)
     dap_stream_ch_chain_net_srv_pkt_error_t l_err;
     memset(&l_err, 0, sizeof(l_err));
     dap_chain_net_srv_t * l_srv = NULL;
+    dap_chain_net_srv_usage_t *l_usage = NULL;
     dap_stream_ch_t *l_ch = dap_stream_ch_find_by_uuid_unsafe(a_grace->stream_worker, a_grace->ch_uuid);
 
     if (l_ch== NULL )
@@ -202,7 +203,6 @@ static bool s_grace_period_control(dap_chain_net_srv_grace_t *a_grace)
             }
         }
     }
-    dap_chain_net_srv_usage_t *l_usage = NULL;
     if (!a_grace->usage) {
         l_usage = dap_chain_net_srv_usage_add(l_srv_session, l_net, l_srv);
         if ( !l_usage ){ // Usage can't add
@@ -533,8 +533,9 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch , void* a_arg)
                 // Store receipt if any problems with transactions
                 dap_chain_hash_fast_t l_receipt_hash={0};
                 dap_hash_fast(l_receipt,l_receipt_size,&l_receipt_hash);
-                char * l_receipt_hash_str = dap_chain_hash_fast_to_str_new(&l_receipt_hash);
-                dap_chain_global_db_gr_set( l_receipt_hash_str,l_receipt,l_receipt_size,"local.receipts");
+                char * l_receipt_hash_str = dap_chain_hash_fast_to_str_new(&l_receipt_hash);   
+                dap_chain_global_db_gr_set(l_receipt_hash_str, DAP_DUP_SIZE(l_receipt, l_receipt_size),
+                                           l_receipt_size, "local.receipts");
                 l_receipt_hash_str = NULL; // To prevent usage of this pointer when it will be free by GDB processor
                 size_t l_success_size;
                 dap_chain_hash_fast_t *l_tx_in_hash  = NULL;
@@ -577,18 +578,13 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch , void* a_arg)
                 if ( l_is_first_sign && l_usage->service->callback_response_success){
                     if( l_usage->service->callback_response_success(l_usage->service,l_usage->id,  l_usage->client,
                                                                 l_receipt, l_receipt_size ) !=0 ){
-                        log_it(L_NOTICE, "No success by service callback, inactivating service usage");
+                        log_it(L_NOTICE, "No success by service success callback, inactivating service usage");
                         l_usage->is_active = false;
                     }
-                    // issue receipt next
-                    l_usage->receipt_next = dap_chain_net_srv_issue_receipt(l_usage->service, l_usage->price, NULL, 0);
-                    dap_stream_ch_pkt_write_unsafe(a_ch, DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_SIGN_REQUEST,
-                                                   l_usage->receipt_next, l_usage->receipt_next->size);
-
                 }else if ( l_usage->service->callback_receipt_next_success){
                     if (l_usage->service->callback_receipt_next_success(l_usage->service, l_usage->id, l_usage->client,
                                                                 l_receipt, l_receipt_size ) != 0 ){
-                        log_it(L_NOTICE, "No success by service callback, inactivating service usage");
+                        log_it(L_NOTICE, "No success by service receipt_next callback, inactivating service usage");
                         l_usage->is_active = false;
                     }
                 }
diff --git a/modules/common/dap_chain_datum_tx_receipt.c b/modules/common/dap_chain_datum_tx_receipt.c
index 0d50f5f3fc..b25e659f85 100644
--- a/modules/common/dap_chain_datum_tx_receipt.c
+++ b/modules/common/dap_chain_datum_tx_receipt.c
@@ -45,13 +45,14 @@ dap_chain_datum_tx_receipt_t * dap_chain_datum_tx_receipt_create( dap_chain_net_
                                                                     uint64_t a_units, uint64_t a_value_datoshi,
                                                                   const void * a_ext, size_t a_ext_size)
 {
-    dap_chain_datum_tx_receipt_t * l_ret = DAP_NEW_Z_SIZE(dap_chain_datum_tx_receipt_t, dap_chain_datum_tx_receipt_get_size_hdr() +a_ext_size );
+    dap_chain_datum_tx_receipt_t *l_ret = DAP_NEW_Z_SIZE(dap_chain_datum_tx_receipt_t,
+                                                         sizeof(dap_chain_datum_tx_receipt_t) + a_ext_size);
     l_ret->type = TX_ITEM_TYPE_RECEIPT;
     l_ret->receipt_info.units_type = a_units_type;
     l_ret->receipt_info.srv_uid = a_srv_uid;
     l_ret->receipt_info.units = a_units;
     l_ret->receipt_info.value_datoshi = a_value_datoshi;
-    l_ret->size = dap_chain_datum_tx_receipt_get_size_hdr()+a_ext_size;
+    l_ret->size = sizeof(dap_chain_datum_tx_receipt_t) + a_ext_size;
 
     if( a_ext_size && a_ext){
         l_ret->exts_size = a_ext_size;
diff --git a/modules/common/include/dap_chain_common.h b/modules/common/include/dap_chain_common.h
index 40b9e92b44..c6b60a080f 100644
--- a/modules/common/include/dap_chain_common.h
+++ b/modules/common/include/dap_chain_common.h
@@ -207,7 +207,7 @@ enum dap_chain_tx_item_type {
 typedef byte_t dap_chain_tx_item_type_t;
 
 
-typedef struct dap_chain_receipt {
+typedef struct dap_chain_receipt_info {
     dap_chain_net_srv_uid_t srv_uid; // Service UID
 #if DAP_CHAIN_NET_SRV_UID_SIZE == 8
     uint64_t addition;
diff --git a/modules/common/include/dap_chain_datum_tx_receipt.h b/modules/common/include/dap_chain_datum_tx_receipt.h
index a2f7c2528c..dc46788999 100644
--- a/modules/common/include/dap_chain_datum_tx_receipt.h
+++ b/modules/common/include/dap_chain_datum_tx_receipt.h
@@ -33,6 +33,7 @@
  */
 typedef struct dap_chain_datum_tx_receipt {
     dap_chain_tx_item_type_t type; // Transaction item type
+    byte_t padding[3];
     dap_chain_receipt_info_t receipt_info; // Receipt itself
     uint64_t size;
     uint64_t exts_size;
@@ -44,8 +45,6 @@ typedef struct dap_chain_datum_tx_receipt {
 extern "C" {
 #endif
 
-static inline size_t dap_chain_datum_tx_receipt_get_size_hdr(){ return 1+sizeof (dap_chain_receipt_info_t)+sizeof (uint16_t) +sizeof (uint16_t); }
-
 dap_chain_datum_tx_receipt_t * dap_chain_datum_tx_receipt_create( dap_chain_net_srv_uid_t srv_uid,
                                                                   dap_chain_net_srv_price_unit_uid_t units_type,
                                                                     uint64_t units, uint64_t value_datoshi, const void * a_ext, size_t a_ext_size);
diff --git a/modules/mempool/dap_chain_mempool.c b/modules/mempool/dap_chain_mempool.c
index ec071e08dc..bd098ec041 100644
--- a/modules/mempool/dap_chain_mempool.c
+++ b/modules/mempool/dap_chain_mempool.c
@@ -557,7 +557,7 @@ dap_chain_hash_fast_t* dap_chain_mempool_tx_create_cond(dap_chain_net_t * a_net,
     char * l_gdb_group = dap_chain_net_get_gdb_group_mempool_by_chain_type( a_net ,CHAIN_TYPE_TX);
     if( dap_chain_global_db_gr_set( dap_strdup(l_key_str), (uint8_t *) l_datum, dap_chain_datum_size(l_datum)
                                    , l_gdb_group ) ) {
-        log_it(L_NOTICE, "Transaction %s placed in mempool", l_key_str);
+        log_it(L_NOTICE, "Transaction %s placed in mempool group %s", l_key_str, l_gdb_group);
     }
     DAP_DELETE(l_gdb_group);
     DAP_DELETE(l_key_str);
diff --git a/modules/net/dap_chain_node_cli.c b/modules/net/dap_chain_node_cli.c
index 40159b3fc9..8d70b8514e 100644
--- a/modules/net/dap_chain_node_cli.c
+++ b/modules/net/dap_chain_node_cli.c
@@ -1083,8 +1083,8 @@ int dap_chain_node_cli_init(dap_config_t * g_config)
     dap_chain_node_cli_cmd_item_create ("tx_create", com_tx_create, "Make transaction",
             "tx_create -net <net name> -chain <chain name> -from_wallet <name> -to_addr <addr> -token <token ticker> -value <value> [-fee <addr> -value_fee <val>]\n" );
     dap_chain_node_cli_cmd_item_create ("tx_cond_create", com_tx_cond_create, "Make cond transaction",
-            "tx_cond_create -net <net name> -token <token_ticker> -wallet_f <wallet_from> -wallet_t <wallet_to>"
-                                        "-value <value_datoshi> -unit <mb|kb|b|sec|day> -service <vpn>\n" );
+            "tx_cond_create -net <net name> -token <token ticker> -wallet <from wallet> -cert <public cert>"
+                                        "-value <value datoshi> -unit <mb | kb | b | sec | day> -srv_uid <numeric uid>\n" );
     dap_chain_node_cli_cmd_item_create ("tx_verify", com_tx_verify, "Verifing transaction in mempool",
             "tx_verify -net <net name> -chain <chain name> -tx <tx_hash>\n" );
 
diff --git a/modules/net/srv/dap_chain_net_srv.c b/modules/net/srv/dap_chain_net_srv.c
index 504ea5598e..6e61ace127 100644
--- a/modules/net/srv/dap_chain_net_srv.c
+++ b/modules/net/srv/dap_chain_net_srv.c
@@ -593,6 +593,23 @@ static int s_cli_net_srv( int argc, char **argv, char **a_str_reply)
     return ret;
 }
 
+bool dap_chain_net_srv_pay_verificator(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx, bool a_owner)
+{
+    if (!a_owner)
+        return false;
+    dap_chain_datum_tx_receipt_t *l_receipt = (dap_chain_datum_tx_receipt_t *)
+                                               dap_chain_datum_tx_item_get(a_tx, NULL, TX_ITEM_TYPE_RECEIPT, NULL);
+    if (!l_receipt)
+        return false;
+    dap_sign_t *l_sign = dap_chain_datum_tx_receipt_sign_get(l_receipt, l_receipt->size, 1);
+    if (!l_sign)
+        return false;
+    dap_hash_fast_t l_pkey_hash;
+    if (!dap_sign_get_pkey_hash(l_sign, &l_pkey_hash))
+        return false;
+    return dap_hash_fast_compare(&l_pkey_hash, &a_cond->subtype.srv_pay.pkey_hash);
+}
+
 /**
  * @brief dap_chain_net_srv_add
  * @param a_uid
@@ -648,7 +665,7 @@ dap_chain_net_srv_t* dap_chain_net_srv_add(dap_chain_net_srv_uid_t a_uid,
                         }
                         continue;
                     case 1:
-                        l_price->value_datoshi = dap_chain_coins_to_datoshi(strtold(l_price_token, NULL));
+                        l_price->value_datoshi = dap_chain_uint128_to(dap_chain_coins_to_balance(l_price_token));
                         if (!l_price->value_datoshi) {
                             log_it(L_ERROR, "Error parsing pricelist: text on 2nd position \"%s\" is not floating number", l_price_token);
                             l_iter = 0;
diff --git a/modules/net/srv/dap_chain_net_srv_client.c b/modules/net/srv/dap_chain_net_srv_client.c
index 5f42cd3e4c..a597b7db58 100644
--- a/modules/net/srv/dap_chain_net_srv_client.c
+++ b/modules/net/srv/dap_chain_net_srv_client.c
@@ -146,10 +146,17 @@ static void s_srv_client_pkt_in(dap_stream_ch_chain_net_srv_t *a_ch_chain, uint8
             break;
         }
         if (l_srv_client->callbacks.sign) {
+            // Duplicate receipt for realloc can be applied
+            dap_chain_datum_tx_receipt_t *l_rec_cpy = DAP_DUP_SIZE(l_receipt, l_receipt->size);
             // Sign receipt     
-            l_receipt = l_srv_client->callbacks.sign(l_srv_client, l_receipt, l_srv_client->callbacks_arg);
-            dap_stream_ch_pkt_write_unsafe(a_ch_chain->ch, DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_SIGN_RESPONSE,
-                                           l_receipt, l_receipt->size);
+            l_rec_cpy = l_srv_client->callbacks.sign(l_srv_client, l_rec_cpy, l_srv_client->callbacks_arg);
+            if (l_rec_cpy) {
+                dap_stream_ch_pkt_write_unsafe(a_ch_chain->ch, DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_SIGN_RESPONSE,
+                                               l_rec_cpy, l_rec_cpy->size);
+                DAP_DELETE(l_rec_cpy);
+            } else {
+                log_it(L_ERROR, "Problem with receipt signing, callback.sign returned NULL");
+            }
         }
     } break;
     case DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_RESPONSE_SUCCESS: {
diff --git a/modules/net/srv/include/dap_chain_net_srv.h b/modules/net/srv/include/dap_chain_net_srv.h
index b7db8cfcb1..43d3795173 100755
--- a/modules/net/srv/include/dap_chain_net_srv.h
+++ b/modules/net/srv/include/dap_chain_net_srv.h
@@ -254,6 +254,7 @@ typedef struct dap_chain_net_srv
 
 int dap_chain_net_srv_init();
 void dap_chain_net_srv_deinit(void);
+bool dap_chain_net_srv_pay_verificator(dap_chain_tx_out_cond_t *a_cond, dap_chain_datum_tx_t *a_tx, bool a_owner);
 dap_chain_net_srv_t* dap_chain_net_srv_add(dap_chain_net_srv_uid_t a_uid,
                                            const char *a_config_section,
                                            dap_chain_net_srv_callback_data_t a_callback_requested,
-- 
GitLab