From fa76d09a9d94c7750a26f3f62c1c6fd2a811432d Mon Sep 17 00:00:00 2001
From: Roman Khlopkov <roman.khlopkov@demlabs.net>
Date: Sat, 30 May 2020 03:32:07 +0300
Subject: [PATCH] [+] Cashback in srv_pay tx

---
 modules/chain/dap_chain_ledger.c              | 29 ++++++-----
 modules/chain/include/dap_chain_ledger.h      |  3 +-
 .../dap_stream_ch_chain_net_srv.c             | 14 +++---
 modules/mempool/dap_chain_mempool.c           | 48 +++++++++++++++++--
 4 files changed, 69 insertions(+), 25 deletions(-)

diff --git a/modules/chain/dap_chain_ledger.c b/modules/chain/dap_chain_ledger.c
index af1201fd20..6c1d7bf8be 100644
--- a/modules/chain/dap_chain_ledger.c
+++ b/modules/chain/dap_chain_ledger.c
@@ -1672,15 +1672,17 @@ const dap_chain_datum_tx_t* dap_chain_ledger_tx_find_by_pkey(dap_ledger_t *a_led
  * a_addr[in] wallet address, whose owner can use the service
  */
 dap_chain_datum_tx_t* dap_chain_ledger_tx_cache_find_out_cond(dap_ledger_t *a_ledger,
-        dap_chain_addr_t *a_addr, dap_chain_hash_fast_t *a_tx_first_hash)
+        dap_chain_hash_fast_t *a_tx_first_hash, dap_chain_tx_out_cond_t **a_out_cond, int *a_out_cond_idx)
 {
-    if(!a_addr || !a_tx_first_hash)
+    if (!a_tx_first_hash)
         return NULL;
     dap_ledger_private_t *l_ledger_priv = PVT(a_ledger);
     dap_chain_datum_tx_t *l_cur_tx = NULL;
     bool is_null_hash = dap_hash_fast_is_blank(a_tx_first_hash);
     bool is_search_enable = is_null_hash;
     dap_chain_ledger_tx_item_t *l_iter_current, *l_item_tmp;
+    dap_chain_tx_out_cond_t *l_tx_out_cond;
+    int l_tx_out_cond_idx;
     pthread_rwlock_wrlock(&l_ledger_priv->ledger_rwlock);
     HASH_ITER(hh, l_ledger_priv->ledger_items, l_iter_current, l_item_tmp)
     {
@@ -1692,10 +1694,8 @@ dap_chain_datum_tx_t* dap_chain_ledger_tx_cache_find_out_cond(dap_ledger_t *a_le
                 is_search_enable = true;
             continue;
         }
-        // Get sign item from transaction
-        int l_tx_out_cond_size = 0;
-        dap_chain_tx_out_cond_t *l_tx_out_cond = (dap_chain_tx_out_cond_t*) dap_chain_datum_tx_item_get(
-                l_tx_tmp, NULL, TX_ITEM_TYPE_OUT_COND, &l_tx_out_cond_size);
+        // Get out_cond item from transaction
+        l_tx_out_cond = dap_chain_datum_tx_out_cond_get(l_tx_tmp, &l_tx_out_cond_idx);
 
         if(l_tx_out_cond) {
             l_cur_tx = l_tx_tmp;
@@ -1704,6 +1704,12 @@ dap_chain_datum_tx_t* dap_chain_ledger_tx_cache_find_out_cond(dap_ledger_t *a_le
         }
     }
     pthread_rwlock_unlock(&l_ledger_priv->ledger_rwlock);
+    if (a_out_cond) {
+        *a_out_cond = l_tx_out_cond;
+    }
+    if (a_out_cond_idx) {
+        *a_out_cond_idx = l_tx_out_cond_idx;
+    }
     return l_cur_tx;
 }
 
@@ -1727,22 +1733,21 @@ uint64_t dap_chain_ledger_tx_cache_get_out_cond_value(dap_ledger_t *a_ledger, da
     //memcpy(&l_tx_first_hash, 0, sizeof(dap_chain_hash_fast_t));
     /* size_t l_pub_key_size = a_key_from->pub_key_data_size;
      uint8_t *l_pub_key = dap_enc_key_serealize_pub_key(a_key_from, &l_pub_key_size);*/
-
+    dap_chain_tx_out_cond_t *l_tx_out_cond;
     // Find all transactions
     do {
-        l_tx_tmp = dap_chain_ledger_tx_cache_find_out_cond(a_ledger, a_addr, &l_tx_first_hash);
+
+        l_tx_tmp = dap_chain_ledger_tx_cache_find_out_cond(a_ledger, &l_tx_first_hash, &l_tx_out_cond, NULL);
 
         // Get out_cond item from transaction
         if(l_tx_tmp) {
-           dap_chain_tx_out_cond_t *l_tx_out_cond  =(dap_chain_tx_out_cond_t *)dap_chain_datum_tx_item_get(
-                     l_tx_tmp, NULL, TX_ITEM_TYPE_OUT_COND, NULL);
-
+            UNUSED(a_addr);
             // TODO check relations a_addr with cond_data and public key
 
             if(l_tx_out_cond) {
                 l_ret_value += l_tx_out_cond->header.value;
                 if(tx_out_cond)
-                    *tx_out_cond = (dap_chain_tx_out_cond_t*) l_tx_out_cond;
+                    *tx_out_cond = l_tx_out_cond;
             }
         }
     } while(l_tx_tmp);
diff --git a/modules/chain/include/dap_chain_ledger.h b/modules/chain/include/dap_chain_ledger.h
index 7639ead95a..1265e7f55f 100644
--- a/modules/chain/include/dap_chain_ledger.h
+++ b/modules/chain/include/dap_chain_ledger.h
@@ -175,8 +175,7 @@ const dap_chain_datum_tx_t* dap_chain_ledger_tx_find_by_pkey(dap_ledger_t *a_led
         char *a_public_key, size_t a_public_key_size, dap_chain_hash_fast_t *a_tx_first_hash);
 
 // Get the transaction in the cache with the out_cond item
-dap_chain_datum_tx_t* dap_chain_ledger_tx_cache_find_out_cond(dap_ledger_t *a_ledger,
-        dap_chain_addr_t *a_addr, dap_chain_hash_fast_t *a_tx_first_hash);
+dap_chain_datum_tx_t* dap_chain_ledger_tx_cache_find_out_cond(dap_ledger_t *a_ledger, dap_chain_hash_fast_t *a_tx_first_hash, dap_chain_tx_out_cond_t **a_out_cond, int *a_out_cond_used);
 
 // Get the value from all transactions in the cache with out_cond item
 uint64_t dap_chain_ledger_tx_cache_get_out_cond_value(dap_ledger_t *a_ledger, dap_chain_addr_t *a_addr,
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 bccec32e88..0ffa340dc7 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
@@ -432,27 +432,27 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch , void* a_arg)
                         char * l_tx_in_hash_str = dap_chain_hash_fast_to_str_new(l_tx_in_hash);
                         log_it(L_NOTICE, "Formed tx %s for input with active receipt", l_tx_in_hash_str);
 
-
-                        // We could put transaction directly to chains
+                        /* We could put transaction directly to chains
                         if ( dap_chain_net_get_role( l_usage->net  ).enums == NODE_ROLE_MASTER ||
                               dap_chain_net_get_role( l_usage->net  ).enums == NODE_ROLE_CELL_MASTER ||
                              dap_chain_net_get_role( l_usage->net  ).enums == NODE_ROLE_ROOT ||
                              dap_chain_net_get_role( l_usage->net  ).enums == NODE_ROLE_ROOT_MASTER ){
                             dap_chain_net_proc_mempool( l_usage->net);
-                        }
+                        }*/
                         DAP_DELETE(l_tx_in_hash_str);
                     }else
                         log_it(L_ERROR, "Can't create input tx cond transaction!");
-                    if (l_tx_in_hash)
-                        DAP_DELETE(l_tx_in_hash);
 
-                    size_t l_success_size = sizeof (dap_stream_ch_chain_net_srv_pkt_success_hdr_t );
+                    size_t l_success_size = sizeof(dap_stream_ch_chain_net_srv_pkt_success_hdr_t) + sizeof(dap_chain_hash_fast_t);
                     dap_stream_ch_chain_net_srv_pkt_success_t *l_success = DAP_NEW_Z_SIZE(dap_stream_ch_chain_net_srv_pkt_success_t,
                                                                                           l_success_size);
                     l_success->hdr.usage_id = l_usage->id;
                     l_success->hdr.net_id.uint64 = l_usage->net->pub.id.uint64;
                     l_success->hdr.srv_uid.uint64 = l_usage->service->uid.uint64;
-
+                    if (l_tx_in_hash) {
+                        memcpy(l_success->custom_data, l_tx_in_hash, sizeof(dap_chain_hash_fast_t));
+                        DAP_DELETE(l_tx_in_hash);
+                    }
                     dap_stream_ch_pkt_write( a_ch, DAP_STREAM_CH_CHAIN_NET_SRV_PKT_TYPE_RESPONSE_SUCCESS ,
                                                  l_success, l_success_size);
                     DAP_DELETE(l_success);
diff --git a/modules/mempool/dap_chain_mempool.c b/modules/mempool/dap_chain_mempool.c
index d103711c10..343fe26bbe 100644
--- a/modules/mempool/dap_chain_mempool.c
+++ b/modules/mempool/dap_chain_mempool.c
@@ -361,23 +361,64 @@ dap_chain_hash_fast_t* dap_chain_mempool_tx_create_cond_input(dap_chain_net_t *
     uint16_t pos=0;
     dap_chain_datum_tx_add_item(&l_tx, (byte_t*) l_receipt);
     pos++;
+    uint64_t l_value_send = l_receipt->receipt_info.value_datoshi;
 
     // add 'in_cond' items
-    dap_chain_datum_tx_t *l_cond_tx = dap_chain_ledger_tx_find_by_hash(l_ledger, a_tx_prev_hash);
+    dap_chain_datum_tx_t *l_tx_cond = dap_chain_ledger_tx_find_by_hash(l_ledger, a_tx_prev_hash);
     int l_prev_cond_idx;
-    dap_chain_datum_tx_out_cond_get(l_cond_tx, &l_prev_cond_idx);
+    dap_chain_tx_out_cond_t *l_out_cond = dap_chain_datum_tx_out_cond_get(l_tx_cond, &l_prev_cond_idx);
+    if (dap_chain_ledger_tx_hash_is_used_out_item(l_ledger, a_tx_prev_hash, l_prev_cond_idx)) {
+        dap_chain_datum_tx_t *l_tx_tmp;
+        dap_chain_hash_fast_t l_tx_cur_hash = { 0 }; // start hash
+        dap_chain_tx_out_cond_t *l_tmp_cond;
+        uint64_t l_value_cond = 0;
+        int l_tmp_cond_idx;
+        // Find all transactions
+        while (l_value_cond < l_value_send) {
+            l_tx_tmp = dap_chain_ledger_tx_cache_find_out_cond(l_ledger, &l_tx_cur_hash, &l_tmp_cond, &l_tmp_cond_idx);
+            if (!l_tx_tmp) {
+                break;
+            }
+            if (dap_chain_ledger_tx_hash_is_used_out_item(l_ledger, &l_tx_cur_hash, l_tmp_cond_idx))
+                continue;
+            if (l_tmp_cond->header.subtype != l_out_cond->header.subtype)
+                continue;
+            if (l_tmp_cond->subtype.srv_pay.srv_uid.uint64 != l_out_cond->subtype.srv_pay.srv_uid.uint64)
+                continue;
+            if (l_tmp_cond->subtype.srv_pay.unit.uint32 != l_out_cond->subtype.srv_pay.unit.uint32)
+                continue;
+            if (l_tmp_cond->subtype.srv_pay.unit_price_max_datoshi != l_out_cond->subtype.srv_pay.unit_price_max_datoshi)
+                continue;
+            if (memcmp(&l_tmp_cond->subtype.srv_pay.pkey_hash, &l_out_cond->subtype.srv_pay.pkey_hash, sizeof(dap_chain_hash_fast_t)))
+                continue;
+            l_value_cond = l_tmp_cond->header.value;
+        }
+        if (l_value_cond < l_value_send) {
+            log_it(L_WARNING, "Requested conditional transaction is already used out");
+            return NULL;
+        }
+    }
     if (dap_chain_datum_tx_add_in_cond_item(&l_tx, a_tx_prev_hash, l_prev_cond_idx, pos-1) != 0 ){
         dap_chain_datum_tx_delete(l_tx);
         log_it( L_ERROR, "Cant add tx cond input");
         return NULL;
     }
 
-    if(dap_chain_datum_tx_add_out_item(&l_tx, a_addr_to, l_receipt->receipt_info.value_datoshi) != 1) {
+    // add 'out' item
+    if (dap_chain_datum_tx_add_out_item(&l_tx, a_addr_to, l_value_send) != 1) {
         dap_chain_datum_tx_delete(l_tx);
         log_it( L_ERROR, "Cant add tx output");
         return NULL;
     }
 
+    //add 'out_cond' item
+    size_t l_size = dap_chain_datum_item_tx_get_size((uint8_t *)l_out_cond);
+    dap_chain_tx_out_cond_t *l_out_cond_new = DAP_NEW_Z_SIZE(dap_chain_tx_out_cond_t, l_size);
+    memcpy(l_out_cond_new, l_out_cond, l_size);
+    l_out_cond_new->header.value -= l_value_send;
+    dap_chain_datum_tx_add_item(&l_tx, (const uint8_t *)l_out_cond_new);
+    DAP_DELETE(l_out_cond_new);
+
     // add 'sign' items
     if (l_key_tx_sign){
         if(dap_chain_datum_tx_add_sign_item(&l_tx, l_key_tx_sign) != 1) {
@@ -874,7 +915,6 @@ void chain_mempool_proc(struct dap_http_simple *cl_st, void * arg)
             {
                 dap_datum_mempool_free(datum_mempool);
                 char *a_key = calc_datum_hash(request_str, (size_t) request_size);
-                char *a_value;
                 switch (action)
                 {
                 case DAP_DATUM_MEMPOOL_ADD: // add datum in base
-- 
GitLab