From bbb2163bd97b6f74058f325623cc815bae7e8cd2 Mon Sep 17 00:00:00 2001
From: Roman Khlopkov <roman.khlopkov@demlabs.net>
Date: Mon, 25 May 2020 10:20:44 +0300
Subject: [PATCH] [+] Per chain mempool autoprocessing

---
 modules/chain/dap_chain.c                     | 27 ++++--
 modules/chain/dap_chain_ledger.c              |  2 +-
 modules/chain/include/dap_chain.h             |  6 +-
 modules/net/dap_chain_node.c                  | 35 +++++--
 modules/net/include/dap_chain_node.h          |  2 +-
 .../xchange/dap_chain_net_srv_xchange.c       | 95 +++++++++++++++----
 .../include/dap_chain_net_srv_xchange.h       |  1 +
 7 files changed, 125 insertions(+), 43 deletions(-)

diff --git a/modules/chain/dap_chain.c b/modules/chain/dap_chain.c
index 27542ad8db..a3d888bfa7 100644
--- a/modules/chain/dap_chain.c
+++ b/modules/chain/dap_chain.c
@@ -188,6 +188,20 @@ dap_chain_t * dap_chain_find_by_id(dap_chain_net_id_t a_chain_net_id,dap_chain_i
         return NULL;
 }
 
+dap_chain_type_t dap_chain_type_from_str(const char *a_type_str)
+{
+    if(!dap_strcmp(a_type_str, "token")) {
+        return CHAIN_TYPE_TOKEN;
+    }
+    if(!dap_strcmp(a_type_str, "emission")) {
+        return CHAIN_TYPE_EMISSION;
+    }
+    if(!dap_strcmp(a_type_str, "transaction")) {
+        return CHAIN_TYPE_TX;
+    }
+    return CHAIN_TYPE_UNKNOWN;
+}
+
 /**
  * @brief dap_chain_load_from_cfg
  * @param a_chain_net_name
@@ -277,16 +291,9 @@ dap_chain_t * dap_chain_load_from_cfg(dap_ledger_t* a_ledger, const char * a_cha
                 l_chain->datum_types = DAP_NEW_SIZE(dap_chain_type_t, l_datum_types_count * sizeof(dap_chain_type_t));
                 uint16_t l_count_recognized = 0;
                 for(uint16_t i = 0; i < l_datum_types_count; i++) {
-                    if(!dap_strcmp(l_datum_types[i], "token")) {
-                        l_chain->datum_types[l_count_recognized] = CHAIN_TYPE_TOKEN;
-                        l_count_recognized++;
-                    }
-                    else if(!dap_strcmp(l_datum_types[i], "emission")) {
-                        l_chain->datum_types[l_count_recognized] = CHAIN_TYPE_EMISSION;
-                        l_count_recognized++;
-                    }
-                    else if(!dap_strcmp(l_datum_types[i], "transaction")) {
-                        l_chain->datum_types[l_count_recognized] = CHAIN_TYPE_TX;
+                    dap_chain_type_t l_chain_type = dap_chain_type_from_str(l_datum_types[i]);
+                    if (l_chain_type != CHAIN_TYPE_UNKNOWN) {
+                        l_chain->datum_types[l_count_recognized] = l_chain_type;
                         l_count_recognized++;
                     }
                 }
diff --git a/modules/chain/dap_chain_ledger.c b/modules/chain/dap_chain_ledger.c
index ae594fca2e..b8b62b87a1 100644
--- a/modules/chain/dap_chain_ledger.c
+++ b/modules/chain/dap_chain_ledger.c
@@ -1340,7 +1340,7 @@ uint64_t dap_chain_ledger_calc_balance(dap_ledger_t *a_ledger, const dap_chain_a
         const char *a_token_ticker)
 {
     uint64_t l_ret = 0;
-    dap_ledger_wallet_balance_t *l_balance_item = NULL ,* l_balance_item_tmp = NULL;
+    dap_ledger_wallet_balance_t *l_balance_item = NULL;// ,* l_balance_item_tmp = NULL;
     char *l_addr = dap_chain_addr_to_str(a_addr);
     char *l_wallet_balance_key = dap_strjoin(" ", l_addr, a_token_ticker, (char*)NULL);
 
diff --git a/modules/chain/include/dap_chain.h b/modules/chain/include/dap_chain.h
index 8634ef4e11..4de3f01818 100644
--- a/modules/chain/include/dap_chain.h
+++ b/modules/chain/include/dap_chain.h
@@ -84,7 +84,8 @@ typedef  enum dap_chain_type
     CHAIN_TYPE_TOKEN,
     CHAIN_TYPE_EMISSION,
     CHAIN_TYPE_TX,
-    CHAIN_TYPE_LAST
+    CHAIN_TYPE_LAST,
+    CHAIN_TYPE_UNKNOWN
 } dap_chain_type_t;
 
 typedef struct dap_chain{
@@ -164,5 +165,4 @@ dap_chain_t * dap_chain_load_from_cfg(dap_ledger_t* a_ledger,const char * a_chai
 void dap_chain_delete(dap_chain_t * a_chain);
 void dap_chain_add_callback_notify(dap_chain_t * a_chain, dap_chain_callback_notify_t a_callback, void * a_arg);
 
-
-
+dap_chain_type_t dap_chain_type_from_str(const char *a_type_str);
diff --git a/modules/net/dap_chain_node.c b/modules/net/dap_chain_node.c
index 38cf9d1933..4e49bcd854 100644
--- a/modules/net/dap_chain_node.c
+++ b/modules/net/dap_chain_node.c
@@ -239,7 +239,7 @@ dap_chain_node_info_t* dap_chain_node_info_read( dap_chain_net_t * a_net,dap_cha
     return node_info;
 }*/
 
-int dap_chain_node_mempool_process(dap_chain_t *a_chain)
+int dap_chain_node_mempool_process(dap_chain_t *a_chain, char **a_datum_types, uint16_t a_datum_types_count)
 {
     char *l_gdb_group_mempool = NULL;
     if (!a_chain) {
@@ -251,10 +251,27 @@ int dap_chain_node_mempool_process(dap_chain_t *a_chain)
     if (l_objs_size) {
         for (size_t i = 0; i < l_objs_size; i++) {
             dap_chain_datum_t *l_datum = (dap_chain_datum_t *)l_objs[i].value;
-            if (l_datum->header.type_id != DAP_CHAIN_DATUM_TX &&
-                l_datum->header.type_id != DAP_CHAIN_DATUM_TOKEN_EMISSION &&
-                l_datum->header.type_id != DAP_CHAIN_DATUM_TOKEN_DECL) {
+            bool b_need_process = false;
+            for (uint16_t j = 0; j < a_datum_types_count; j++) {
+                if (l_datum->header.type_id == dap_chain_type_from_str(a_datum_types[j]) ||
+                    !strcmp(a_datum_types[j], "all")) {
+                    b_need_process = true;
+                    break;
+                }
+            }
+            if (!b_need_process)
                 continue;
+            if (l_datum->header.type_id == DAP_CHAIN_DATUM_TX) {
+                dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t *)l_datum->data;
+                dap_chain_tx_in_t *l_tx_in = (dap_chain_tx_in_t *)dap_chain_datum_tx_item_get(l_tx, NULL, TX_ITEM_TYPE_IN, NULL);
+                // Is it a base transaction?
+                if (dap_hash_fast_is_blank(&l_tx_in->header.tx_prev_hash)) {
+                    dap_chain_tx_token_t *l_tx_token = (dap_chain_tx_token_t *)dap_chain_datum_tx_item_get(l_tx, NULL, TX_ITEM_TYPE_TOKEN, NULL);
+                    if (l_tx_token && !dap_chain_ledger_token_emission_find(a_chain->ledger, l_tx_token->header.ticker,
+                                                                            &l_tx_token->header.token_emission_hash)) {
+                        continue;
+                    }
+                }
             }
             if (a_chain->callback_datums_pool_proc(a_chain, &l_datum, 1) != 1) {
                 continue;
@@ -292,10 +309,12 @@ void dap_chain_node_mempool_periodic(void *a_param)
         if (l_mempool_auto) {
             dap_chain_t *l_chain;
             DL_FOREACH(l_net_list[i]->pub.chains, l_chain) {
-                for (uint16_t i = 0; i < l_chain->datum_types_count; i++) {
-                    if (l_chain->datum_types[i] == CHAIN_TYPE_TX) {
-                        dap_chain_node_mempool_process(l_chain);
-                    }
+                // Read chain datum types
+                char** l_datum_types = NULL;
+                uint16_t l_datum_types_count = 0;
+                l_datum_types = dap_config_get_array_str(g_config, "chain", "mempool_auto_types", &l_datum_types_count);
+                if (l_datum_types_count) {
+                    dap_chain_node_mempool_process(l_chain, l_datum_types, l_datum_types_count);
                 }
             }
         }
diff --git a/modules/net/include/dap_chain_node.h b/modules/net/include/dap_chain_node.h
index 1e5f0b6d7f..aa5db7f462 100644
--- a/modules/net/include/dap_chain_node.h
+++ b/modules/net/include/dap_chain_node.h
@@ -141,6 +141,6 @@ inline static char* dap_chain_node_addr_to_hash_str(dap_chain_node_addr_t *addre
     return a_key;
 }
 
-int dap_chain_node_mempool_process(dap_chain_t *a_chain);
+int dap_chain_node_mempool_process(dap_chain_t *a_chain, char **a_datum_types, uint16_t a_datum_types_count);
 int dap_chain_node_mempool_init();
 void dap_chain_node_mempool_deinit();
diff --git a/modules/service/xchange/dap_chain_net_srv_xchange.c b/modules/service/xchange/dap_chain_net_srv_xchange.c
index 3bf646077c..6214ec8693 100644
--- a/modules/service/xchange/dap_chain_net_srv_xchange.c
+++ b/modules/service/xchange/dap_chain_net_srv_xchange.c
@@ -121,7 +121,20 @@ static bool dap_chain_net_srv_xchange_verificator(dap_chain_tx_out_cond_t *a_con
     return true;
 }
 
-static dap_chain_datum_tx_t *s_xchange_create_tx_request(dap_chain_net_srv_xchange_price_t *a_price)
+static dap_chain_datum_tx_receipt_t *s_xchage_receipt_create(dap_chain_net_srv_xchange_price_t *a_price)
+{
+    uint32_t l_ext_size = sizeof(uint64_t) + DAP_CHAIN_TICKER_SIZE_MAX;
+    uint8_t *l_ext = DAP_NEW_SIZE(uint8_t, l_ext_size);
+    dap_lendian_put64(l_ext, a_price->datoshi_sell);
+    strcpy((char *)&l_ext[sizeof(uint64_t)], a_price->token_sell);
+    dap_chain_net_srv_price_unit_uid_t l_unit = { .uint32 = SERV_UNIT_UNDEFINED};
+    dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_XCHANGE_ID };
+    dap_chain_datum_tx_receipt_t *l_receipt =  dap_chain_datum_tx_receipt_create(l_uid, l_unit, 0, a_price->datoshi_buy,
+                                                                                 l_ext, l_ext_size);
+    return l_receipt;
+}
+
+static dap_chain_datum_tx_t *s_xchange_tx_create_request(dap_chain_net_srv_xchange_price_t *a_price)
 {
     if (!a_price || !a_price->net_sell || !a_price->net_buy || !*a_price->token_sell || !*a_price->token_buy || !a_price->wallet) {
         return NULL;
@@ -141,7 +154,8 @@ static dap_chain_datum_tx_t *s_xchange_create_tx_request(dap_chain_net_srv_xchan
         dap_chain_datum_tx_delete(l_tx);
         log_it(L_WARNING, "Nothing to change (not enough funds)");
         return NULL;
-    }  
+    }
+
     // add 'in' items to sell
     uint64_t l_value_to_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out);
     dap_list_free_full(l_list_used_out, free);
@@ -185,7 +199,7 @@ static dap_chain_datum_tx_t *s_xchange_create_tx_request(dap_chain_net_srv_xchan
     return l_tx;
 }
 
-static dap_chain_datum_tx_t *s_xchange_create_tx_exchange(dap_chain_net_srv_xchange_price_t *a_price, dap_chain_hash_fast_t *a_tx_cond_hash)
+static dap_chain_datum_tx_t *s_xchange_tx_create_exchange(dap_chain_net_srv_xchange_price_t *a_price, dap_chain_hash_fast_t *a_tx_cond_hash)
 {
     if (!a_price || !a_price->net_sell || !a_price->net_buy || !*a_price->token_sell || !*a_price->token_buy || !a_price->wallet) {
         return NULL;
@@ -208,14 +222,7 @@ static dap_chain_datum_tx_t *s_xchange_create_tx_exchange(dap_chain_net_srv_xcha
     }
 
     // create and add reciept
-    uint32_t l_ext_size = sizeof(uint64_t) + DAP_CHAIN_TICKER_SIZE_MAX;
-    uint8_t *l_ext = DAP_NEW_SIZE(uint8_t, l_ext_size);
-    dap_lendian_put64(l_ext, a_price->datoshi_sell);
-    strcpy((char *)&l_ext[sizeof(uint64_t)], a_price->token_sell);
-    dap_chain_net_srv_price_unit_uid_t l_unit = { .uint32 =  SERV_UNIT_UNDEFINED};
-    dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_XCHANGE_ID };
-    dap_chain_datum_tx_receipt_t *l_receipt =  dap_chain_datum_tx_receipt_create(l_uid, l_unit, 0, a_price->datoshi_buy,
-                                                                                 l_ext, l_ext_size);
+    dap_chain_datum_tx_receipt_t *l_receipt = s_xchage_receipt_create(a_price);
     dap_chain_datum_tx_add_item(&l_tx, (byte_t *)l_receipt);
     DAP_DELETE(l_receipt);
     // add 'in' items to sell
@@ -237,7 +244,6 @@ static dap_chain_datum_tx_t *s_xchange_create_tx_exchange(dap_chain_net_srv_xcha
     {
         // transfer selling coins
         const dap_chain_addr_t *l_buyer_addr = (dap_chain_addr_t *)l_tx_out_cond->params;
-        uint64_t l_buying_value = l_tx_out_cond->header.value;
         dap_chain_tx_out_t *l_tx_out = dap_chain_datum_tx_item_out_create(l_buyer_addr, a_price->datoshi_sell, a_price->token_sell);
         if (l_tx_out) {
             dap_chain_datum_tx_add_item(&l_tx, (const uint8_t *)l_tx_out);
@@ -267,6 +273,7 @@ static dap_chain_datum_tx_t *s_xchange_create_tx_exchange(dap_chain_net_srv_xcha
             return NULL;
         }
         //transfer unbuying coins (partial exchange)
+        uint64_t l_buying_value = l_tx_out_cond->header.value;
         l_value_back = l_buying_value - a_price->datoshi_buy;
         if (l_value_back) {
             //if (dap_chain_datum_tx_add_out_item(&l_tx, l_buyer_addr, l_value_back) != 1) {
@@ -316,6 +323,50 @@ static bool s_xchange_tx_put(dap_chain_datum_tx_t *a_tx, dap_chain_net_t *a_net)
     return true;
 }
 
+static bool s_xchage_tx_invalidate(dap_chain_net_srv_xchange_price_t *a_price)
+{
+    // create empty transaction
+    dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create();
+
+    dap_ledger_t *l_ledger = dap_chain_ledger_by_net_name(a_price->net_sell->pub.name);
+    const dap_chain_addr_t *l_seller_addr = (const dap_chain_addr_t *) dap_chain_wallet_get_addr(a_price->wallet, a_price->net_sell->pub.id);
+    dap_enc_key_t *l_seller_key = dap_chain_wallet_get_key(a_price->wallet, 0);
+
+    // create and add reciept
+    dap_chain_datum_tx_receipt_t *l_receipt = s_xchage_receipt_create(a_price);
+    dap_chain_datum_tx_add_item(&l_tx, (byte_t *)l_receipt);
+    DAP_DELETE(l_receipt);
+
+    // add 'in' item to buy from conditional transaction
+    int l_prev_cond_idx = 0;
+    dap_chain_datum_tx_t *l_cond_tx = dap_chain_ledger_tx_find_by_hash(l_ledger, &a_price->tx_hash);
+    dap_chain_tx_out_cond_t *l_tx_out_cond  = (dap_chain_tx_out_cond_t *)dap_chain_datum_tx_item_get(
+                                               l_cond_tx, &l_prev_cond_idx, TX_ITEM_TYPE_OUT_COND, NULL);
+    dap_chain_datum_tx_add_in_cond_item(&l_tx, &a_price->tx_hash, l_prev_cond_idx, 0);
+
+    // add 'out' item
+#ifndef NDEBUG
+    const dap_chain_addr_t *l_buyer_addr = (dap_chain_addr_t *)l_tx_out_cond->params;
+    assert(l_buyer_addr == l_seller_addr);
+#endif
+    if (dap_chain_datum_tx_add_out_item(&l_tx, l_seller_addr, l_tx_out_cond->header.value)) {
+        dap_chain_datum_tx_delete(l_tx);
+        log_it(L_ERROR, "Cant add returning coins output");
+        return false;
+    }
+
+    // add 'sign' items
+    if(dap_chain_datum_tx_add_sign_item(&l_tx, l_seller_key) != 1) {
+        dap_chain_datum_tx_delete(l_tx);
+        log_it( L_ERROR, "Can't add sign output");
+        return false;
+    }
+    if (!s_xchange_tx_put(l_tx, a_price->net_sell)) {
+        return false;
+    }
+    return true;
+}
+
 static int s_cli_srv_xchange_price(int a_argc, char **a_argv, int a_arg_index, char **a_str_reply)
 {
     enum {
@@ -445,7 +496,7 @@ static int s_cli_srv_xchange_price(int a_argc, char **a_argv, int a_arg_index, c
             l_price->datoshi_sell = l_datoshi_sell;
             l_price->datoshi_buy = l_datoshi_buy;
             // Create conditional transaction
-            dap_chain_datum_tx_t *l_tx = s_xchange_create_tx_request(l_price);
+            dap_chain_datum_tx_t *l_tx = s_xchange_tx_create_request(l_price);
             if (!l_tx) {
                 DAP_DELETE(l_price);
                 break;
@@ -460,7 +511,7 @@ static int s_cli_srv_xchange_price(int a_argc, char **a_argv, int a_arg_index, c
             dap_chain_node_addr_t *l_node_addr = dap_chain_net_get_cur_addr(l_price->net_sell);
             dap_chain_net_srv_price_unit_uid_t l_unit = { .uint32 =  SERV_UNIT_UNDEFINED};
             dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_XCHANGE_ID };
-            //TODO add l_net_buy to the order
+            //TODO add l_net_sell to the order
             char *l_order_hash_str = dap_chain_net_srv_order_create(l_net_buy, SERV_DIR_SELL, l_uid, *l_node_addr,
                                                                     l_tx_hash, l_datoshi_sell, l_unit, l_price->token_sell, 0,
                                                                     l_ext, l_ext_size, NULL, 0);
@@ -473,6 +524,7 @@ static int s_cli_srv_xchange_price(int a_argc, char **a_argv, int a_arg_index, c
                     DAP_DELETE(l_order_hash_str);
                     break;
                 }
+                memcpy(&l_price->tx_hash, &l_tx_hash, sizeof(dap_chain_hash_fast_t));
                 dap_chain_node_cli_set_reply_text(a_str_reply, "Successfully created order %s", l_order_hash_str);
                 DAP_DELETE(l_order_hash_str);
                 // Add active price to pricelist
@@ -488,15 +540,18 @@ static int s_cli_srv_xchange_price(int a_argc, char **a_argv, int a_arg_index, c
             HASH_FIND_STR(s_srv_xchange->pricelist, l_strkey, l_price);
             if (l_price) {
                 if (l_cmd_num == CMD_REMOVE) {
+                    if (!s_xchage_tx_invalidate(l_price)) {
+                        char *l_tx_hash_str = dap_chain_hash_fast_to_str_new(&l_price->tx_hash);
+                        dap_chain_node_cli_set_reply_text(a_str_reply, "Can't invalidate transaction %s", l_tx_hash_str);
+                        DAP_DELETE(l_tx_hash_str);
+                        break;
+                    }
+                    //TODO delete order (l_price->order);
                     HASH_DEL(s_srv_xchange->pricelist, l_price);
                     DAP_DELETE(l_price->key_ptr);
-
                     dap_chain_wallet_close(l_price->wallet);
-
-                    //TODO invalidate transaction
-
-                    //TODO delete order (l_price->order);
                     DAP_DELETE(l_price);
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "Price successfully removed");
                 } else {    // CMD_UPDATE
                     const char *l_val_sell_str = NULL, *l_val_buy_str = NULL, *l_wallet_str = NULL;
                     uint64_t l_datoshi_sell = 0, l_datoshi_buy = 0;
@@ -634,7 +689,7 @@ static int s_cli_srv_xchange(int a_argc, char **a_argv, void *a_arg_func, char *
                 l_price->datoshi_sell = dap_lendian_get64(l_order->ext);
                 l_price->wallet = l_wallet;
                 // Create conditional transaction
-                dap_chain_datum_tx_t *l_tx = s_xchange_create_tx_exchange(l_price, &l_order->tx_cond_hash);
+                dap_chain_datum_tx_t *l_tx = s_xchange_tx_create_exchange(l_price, &l_order->tx_cond_hash);
                 if (l_tx) {
                     s_xchange_tx_put(l_tx, l_net);
                 }
diff --git a/modules/service/xchange/include/dap_chain_net_srv_xchange.h b/modules/service/xchange/include/dap_chain_net_srv_xchange.h
index 9dc7335842..4592b4491d 100644
--- a/modules/service/xchange/include/dap_chain_net_srv_xchange.h
+++ b/modules/service/xchange/include/dap_chain_net_srv_xchange.h
@@ -37,6 +37,7 @@ typedef struct dap_chain_net_srv_xchange_price {
     dap_chain_net_t *net_buy;
     char token_buy[DAP_CHAIN_TICKER_SIZE_MAX];
     uint64_t datoshi_buy;
+    dap_chain_hash_fast_t tx_hash;
     dap_chain_hash_fast_t order_hash;
     char *key_ptr;
     UT_hash_handle hh;
-- 
GitLab