/* * Authors: * Dmitriy A. Gerasimov <naeper@demlabs.net> * DeM Labs Inc. https://demlabs.net This file is part of DAP (Deus Applications Prototypes) the open source project DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. DAP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with any DAP based project. If not, see <http://www.gnu.org/licenses/>. */ #include <stdlib.h> #include <stdio.h> #include <time.h> #include <stdlib.h> #include <stddef.h> #include <stdint.h> #include <string.h> #ifdef WIN32 #include <winsock2.h> #include <windows.h> #include <mswsock.h> #include <ws2tcpip.h> #include <io.h> #include <pthread.h> #else #include <sys/socket.h> #include <netinet/in.h> #endif #include "utlist.h" #include "dap_hash.h" #include "rand/dap_rand.h" #include "dap_chain_net.h" #include "dap_global_db.h" #include "dap_chain_node.h" #include "dap_chain_cell.h" #include "dap_chain_ledger.h" #define LOG_TAG "chain_node" /** * Register alias in base */ bool dap_chain_node_alias_register(dap_chain_net_t *a_net, const char *a_alias, dap_chain_node_addr_t *a_addr) { return dap_global_db_set_sync(a_net->pub.gdb_nodes_aliases, a_alias, a_addr, sizeof(dap_chain_node_addr_t), false) == 0; } /** * @brief dap_chain_node_alias_find * @param alias * @return */ dap_chain_node_addr_t *dap_chain_node_alias_find(dap_chain_net_t *a_net, const char *a_alias) { dap_return_val_if_fail(a_alias && a_net, NULL); size_t l_size = 0; dap_chain_node_addr_t *l_addr = (dap_chain_node_addr_t*)dap_global_db_get_sync(a_net->pub.gdb_nodes_aliases, a_alias, &l_size, NULL, NULL); return l_addr && l_size != sizeof(dap_chain_node_addr_t) ? log_it(L_WARNING, "Node record is corrupted for alias %s: %zu != %zu", a_alias, l_size, sizeof(dap_chain_node_addr_t)), DAP_DELETE(l_addr), NULL : l_addr; } /** * Delete alias from base */ bool dap_chain_node_alias_delete(dap_chain_net_t * a_net,const char *a_alias) { return dap_global_db_del_sync(a_net->pub.gdb_nodes_aliases, a_alias) == 0; } /** * Compare addresses of two dap_chain_node_info_t structures * * @return True if addresses are equal, otherwise false */ bool dap_chain_node_info_addr_match(dap_chain_node_info_t *node_info1, dap_chain_node_info_t *node_info2) { return node_info1 && node_info2 && !dap_strcmp(node_info1->ext_host, node_info2->ext_host) && node_info1->ext_port == node_info2->ext_port; } /** * @brief dap_chain_node_info_save * @param node_info * @return */ int dap_chain_node_info_save(dap_chain_net_t *a_net, dap_chain_node_info_t *a_node_info) { return !a_node_info || !a_node_info->address.uint64 ? log_it(L_ERROR,"Can't save node info, %s", a_node_info ? "null arg" : "zero address"), -1 : dap_global_db_set_sync( a_net->pub.gdb_nodes, dap_chain_node_addr_to_str_static(&a_node_info->address), a_node_info, dap_chain_node_info_get_size(a_node_info), false ); } int dap_chain_node_info_del(dap_chain_net_t *a_net, dap_chain_node_info_t *a_node_info) { return !a_node_info || !a_node_info->address.uint64 ? log_it(L_ERROR,"Can't delete node info, %s", a_node_info ? "null arg" : "zero address"), -1 : dap_global_db_del_sync( a_net->pub.gdb_nodes, dap_chain_node_addr_to_str_static(&a_node_info->address) ); } /** * Read node from base */ dap_chain_node_info_t* dap_chain_node_info_read(dap_chain_net_t *a_net, dap_chain_node_addr_t *a_address) { char *l_key = dap_chain_node_addr_to_str_static(a_address); size_t l_node_info_size = 0; dap_chain_node_info_t *l_node_info = (dap_chain_node_info_t*)dap_global_db_get_sync(a_net->pub.gdb_nodes, l_key, &l_node_info_size, NULL, NULL); if (!l_node_info) { log_it(L_NOTICE, "Node with address %s not found in base of %s network", l_key, a_net->pub.name); return NULL; } size_t l_node_info_size_calced = dap_chain_node_info_get_size(l_node_info); if (l_node_info_size_calced != l_node_info_size) { log_it(L_ERROR, "Bad node \"%s\" record size, %zu != %zu", l_key, l_node_info_size_calced, l_node_info_size); DAP_DELETE(l_node_info); return NULL; } return l_node_info; } bool dap_chain_node_mempool_need_process(dap_chain_t *a_chain, dap_chain_datum_t *a_datum) { for (uint16_t j = 0; j < a_chain->autoproc_datum_types_count; j++) if (a_datum->header.type_id == a_chain->autoproc_datum_types[j]) return true; return false; } /* Return true if processed datum should be deleted from mempool */ bool dap_chain_node_mempool_process(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, const char *a_datum_hash_str) { if (!a_chain->callback_add_datums) { log_it(L_ERROR, "Not found chain callback for datums processing"); return false; } dap_hash_fast_t l_datum_hash, l_real_hash; if (dap_chain_hash_fast_from_hex_str(a_datum_hash_str, &l_datum_hash)) { log_it(L_WARNING, "Can't get datum hash from hash string"); return false; } dap_hash_fast(a_datum->data, a_datum->header.data_size, &l_real_hash); if (!dap_hash_fast_compare(&l_datum_hash, &l_real_hash)) { log_it(L_WARNING, "Datum hash from mempool key and real datum hash are different"); return false; } int l_verify_datum = dap_chain_net_verify_datum_for_add(a_chain, a_datum, &l_datum_hash); if (l_verify_datum != 0 && l_verify_datum != DAP_CHAIN_CS_VERIFY_CODE_TX_NO_PREVIOUS && l_verify_datum != DAP_CHAIN_CS_VERIFY_CODE_TX_NO_EMISSION && l_verify_datum != DAP_CHAIN_CS_VERIFY_CODE_NO_DECREE) return true; if (!l_verify_datum #ifdef DAP_TPS_TEST || l_verify_datum == DAP_CHAIN_CS_VERIFY_CODE_TX_NO_PREVIOUS #endif ) { a_chain->callback_add_datums(a_chain, &a_datum, 1); } return false; } void dap_chain_node_mempool_process_all(dap_chain_t *a_chain, bool a_force) { dap_chain_net_t *l_net = dap_chain_net_by_id(a_chain->net_id); if (!a_force && !l_net->pub.mempool_autoproc) return; char *l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool_new(a_chain); size_t l_objs_size = 0; dap_global_db_obj_t *l_objs = dap_global_db_get_all_sync(l_gdb_group_mempool, &l_objs_size); if (l_objs_size) { for (size_t i = 0; i < l_objs_size; i++) { if (!l_objs[i].value_len) continue; dap_chain_datum_t *l_datum = (dap_chain_datum_t *)l_objs[i].value; if (dap_chain_node_mempool_need_process(a_chain, l_datum)) { if (l_datum->header.type_id == DAP_CHAIN_DATUM_TX && a_chain->callback_get_minimum_fee){ uint256_t l_tx_fee = {}; dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t *)l_datum->data; if (dap_chain_datum_tx_get_fee_value (l_tx, &l_tx_fee) || IS_ZERO_256(l_tx_fee)) { if (!dap_ledger_tx_poa_signed(l_net->pub.ledger, l_tx)) { log_it(L_WARNING, "Can't get fee value from tx %s", l_objs[i].key); continue; } else log_it(L_DEBUG, "Process service tx without fee"); } else { uint256_t l_min_fee = a_chain->callback_get_minimum_fee(a_chain); if (compare256(l_tx_fee, l_min_fee) < 0) { char *l_tx_fee_str = dap_chain_balance_to_coins(l_tx_fee); char *l_min_fee_str = dap_chain_balance_to_coins(l_min_fee); log_it(L_WARNING, "Fee %s is lower than minimum fee %s for tx %s", l_tx_fee_str, l_min_fee_str, l_objs[i].key); DAP_DELETE(l_tx_fee_str); DAP_DELETE(l_min_fee_str); continue; } } } if (dap_chain_node_mempool_process(a_chain, l_datum, l_objs[i].key)) { // Delete processed objects log_it(L_INFO, " ! Delete datum %s from mempool", l_objs[i].key); dap_global_db_del(l_gdb_group_mempool, l_objs[i].key, NULL, NULL); } else { log_it(L_INFO, " ! Datum %s remains in mempool", l_objs[i].key); } } } dap_global_db_objs_delete(l_objs, l_objs_size); } DAP_DELETE(l_gdb_group_mempool); } /** * @brief * get automatic mempool processing, when network config contains mempool_auto_types for specific datums * @return true * @return false */ bool dap_chain_node_mempool_autoproc_init() { if (!dap_config_get_item_bool_default(g_config, "mempool", "auto_proc", false)) return false; for (dap_chain_net_t *it = dap_chain_net_iter_start(); it; it = dap_chain_net_iter_next(it)) { switch (dap_chain_net_get_role(it).enums) { case NODE_ROLE_ROOT: case NODE_ROLE_MASTER: case NODE_ROLE_ROOT_MASTER: case NODE_ROLE_CELL_MASTER: it->pub.mempool_autoproc = true; break; default: it->pub.mempool_autoproc = false; continue; } } return true; }