/* * Authors: * Dmitriy A. Gearasimov <gerasimov.dmitriy@demlabs.net> * Alexander Lysikov <alexander.lysikov@demlabs.net> * DeM Labs Inc. https://demlabs.net * Kelvin Project https://github.com/kelvinblockchain * Copyright (c) 2017-2018 * All rights reserved. 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 <stdio.h> #include <stdlib.h> #include <stddef.h> #include <stdint.h> #include <string.h> #include <errno.h> #include <pthread.h> #ifdef DAP_OS_UNIX #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netdb.h> #endif #ifdef WIN32 #include <winsock2.h> #include <windows.h> #include <mswsock.h> #include <ws2tcpip.h> #include <io.h> #endif #include "uthash.h" #include "utlist.h" #include "dap_common.h" #include "dap_string.h" #include "dap_strfuncs.h" #include "dap_file_utils.h" #include "dap_enc_base58.h" #include "dap_config.h" #include "dap_hash.h" #include "dap_cert.h" #include "dap_cert_file.h" #include "dap_timerfd.h" #include "dap_enc_http.h" #include "dap_chain_common.h" #include "dap_chain_net.h" #include "dap_chain_pvt.h" #include "dap_chain_node_client.h" #include "dap_chain_node_cli.h" #include "dap_chain_node_cli_cmd.h" #include "dap_chain_ledger.h" #include "dap_chain_cs_none.h" #include "dap_chain_global_db.h" #include "dap_chain_global_db_remote.h" #include "dap_stream_ch_chain_net_pkt.h" #include "dap_stream_ch_chain_net.h" #include "dap_stream_ch_chain.h" #include "dap_stream_ch_chain_pkt.h" #include "dap_stream_ch.h" #include "dap_stream_ch_pkt.h" #include "dap_dns_server.h" #include "dap_module.h" #include <stdio.h> #include <sys/types.h> #include <dirent.h> #define _XOPEN_SOURCE /* See feature_test_macros(7) */ #ifndef __USE_XOPEN #define __USE_XOPEN #endif #define _GNU_SOURCE #include <time.h> #define LOG_TAG "chain_net" #define F_DAP_CHAIN_NET_SYNC_FROM_ZERO ( 1 << 8 ) #define F_DAP_CHAIN_NET_SHUTDOWN ( 1 << 9 ) #define F_DAP_CHAIN_NET_GO_SYNC ( 1 << 10 ) // maximum number of connections static size_t s_max_links_count = 5;// by default 5 // number of required connections static size_t s_required_links_count = 3;// by default 3 static pthread_t s_net_check_pid; /** * @struct dap_chain_net_pvt * @details Private part of chain_net dap object */ typedef struct dap_chain_net_pvt{ pthread_t proc_tid; #ifndef _WIN32 pthread_cond_t state_proc_cond; #else HANDLE state_proc_cond; #endif dap_events_socket_t * event_state_proc; pthread_mutex_t state_mutex_cond; dap_chain_node_role_t node_role; uint32_t flags; time_t last_sync; dap_chain_node_addr_t * node_addr; dap_chain_node_info_t * node_info; // Current node's info dap_list_t *links; // Links list dap_list_t *links_info; // Links info list bool load_mode; char ** seed_aliases; uint16_t bootstrap_nodes_count; struct in_addr *bootstrap_nodes_addrs; uint16_t *bootstrap_nodes_ports; uint16_t gdb_sync_groups_count; uint16_t gdb_sync_nodes_addrs_count; char **gdb_sync_groups; dap_chain_node_addr_t *gdb_sync_nodes_addrs; uint16_t seed_aliases_count; dap_chain_net_state_t state; dap_chain_net_state_t state_target; uint16_t acl_idx; } dap_chain_net_pvt_t; typedef struct dap_chain_net_item{ char name [DAP_CHAIN_NET_NAME_MAX]; dap_chain_net_id_t net_id; dap_chain_net_t * chain_net; UT_hash_handle hh; } dap_chain_net_item_t; #define PVT(a) ( (dap_chain_net_pvt_t *) (void*) a->pvt ) #define PVT_S(a) ( (dap_chain_net_pvt_t *) (void*) a.pvt ) static dap_chain_net_item_t * s_net_items = NULL; static dap_chain_net_item_t * s_net_items_ids = NULL; static const char * c_net_states[]={ [NET_STATE_OFFLINE] = "NET_STATE_OFFLINE", [NET_STATE_LINKS_PREPARE ] = "NET_STATE_LINKS_PREPARE", [NET_STATE_LINKS_CONNECTING] = "NET_STATE_LINKS_CONNECTING", [NET_STATE_LINKS_ESTABLISHED]= "NET_STATE_LINKS_ESTABLISHED", [NET_STATE_SYNC_GDB]= "NET_STATE_SYNC_GDB", [NET_STATE_SYNC_CHAINS]= "NET_STATE_SYNC_CHAINS", [NET_STATE_ADDR_REQUEST]= "NET_STATE_ADDR_REQUEST", [NET_STATE_ONLINE]= "NET_STATE_ONLINE" }; static dap_chain_net_t * s_net_new(const char * a_id, const char * a_name , const char * a_node_role); inline static const char * s_net_state_to_str(dap_chain_net_state_t l_state); // Node link callbacks static void s_node_link_callback_connected(dap_chain_node_client_t * a_node_client, void * a_arg); static void s_node_link_callback_disconnected(dap_chain_node_client_t * a_node_client, void * a_arg); static void s_node_link_callback_stage(dap_chain_node_client_t * a_node_client,dap_client_stage_t a_stage, void * a_arg); static void s_node_link_callback_error(dap_chain_node_client_t * a_node_client, int a_error, void * a_arg); static int s_net_states_proc(dap_chain_net_t * a_net); static void * s_net_check_thread ( void * a_net); static void s_net_check_thread_start( dap_chain_net_t * a_net ); static void s_net_proc_kill( dap_chain_net_t * a_net ); int s_net_load(const char * a_net_name, uint16_t a_acl_idx); static void s_gbd_history_callback_notify (void * a_arg,const char a_op_code, const char * a_prefix, const char * a_group, const char * a_key, const void * a_value, const size_t a_value_len); static void s_chain_callback_notify(void * a_arg, dap_chain_t *a_chain, dap_chain_cell_id_t a_id, void *a_atom, size_t a_atom_size); static int s_cli_net(int argc, char ** argv, void *arg_func, char **str_reply); static bool s_seed_mode = false; static uint8_t *dap_chain_net_set_acl(dap_chain_hash_fast_t *a_pkey_hash); static dap_global_db_obj_callback_notify_t s_srv_callback_notify = NULL; char *dap_chain_net_get_gdb_group_acl(dap_chain_net_t *a_net) { if (a_net) { const char l_path[] = "network/"; char l_cfg_path[strlen(a_net->pub.name) + strlen(l_path) + 1]; strcpy(l_cfg_path, l_path); strcat(l_cfg_path, a_net->pub.name); dap_config_t *l_cfg = dap_config_open(l_cfg_path); const char *l_auth_gdb = dap_config_get_item_str(l_cfg, "auth", "acl_accept_ca_gdb"); if (l_auth_gdb) { return dap_strdup_printf("%s.%s", a_net->pub.gdb_groups_prefix, l_auth_gdb); } } return NULL; } /** * @brief s_net_state_to_str * @param l_state * @return */ inline static const char * s_net_state_to_str(dap_chain_net_state_t l_state) { return c_net_states[l_state]; } /** * @brief dap_chain_net_state_go_to * @param a_net * @param a_new_state */ int dap_chain_net_state_go_to(dap_chain_net_t * a_net, dap_chain_net_state_t a_new_state) { if (PVT(a_net)->state_target == a_new_state){ log_it(L_WARNING,"Already going to state %s",s_net_state_to_str(a_new_state)); } PVT(a_net)->state_target = a_new_state; pthread_mutex_lock( &PVT(a_net)->state_mutex_cond); // Preventing call of state_go_to before wait cond will be armed pthread_mutex_unlock( &PVT(a_net)->state_mutex_cond); // set flag for sync PVT(a_net)->flags |= F_DAP_CHAIN_NET_GO_SYNC; #ifndef _WIN32 pthread_cond_signal( &PVT(a_net)->state_proc_cond ); #else SetEvent( PVT(a_net)->state_proc_cond ); #endif return 0; } void dap_chain_net_set_srv_callback_notify(dap_global_db_obj_callback_notify_t a_callback) { s_srv_callback_notify = a_callback; } void dap_chain_net_sync_gdb_broadcast(void *a_arg, const char a_op_code, const char *a_prefix, const char *a_group, const char *a_key, const void *a_value, const size_t a_value_len) { UNUSED(a_prefix); UNUSED(a_value_len); dap_chain_net_t *l_net = (dap_chain_net_t *)a_arg; if (PVT(l_net)->state == NET_STATE_ONLINE) { char *l_group; if (a_op_code == 'd') { l_group = dap_strdup_printf("%s.del", a_group); } else { l_group = (char *)a_group; } dap_store_obj_t *l_obj = (dap_store_obj_t *)dap_chain_global_db_obj_get(a_key, l_group); if (a_op_code == 'd') { DAP_DELETE(l_group); } if (!l_obj) { log_it(L_DEBUG, "Notified GDB event does not exist"); return; } l_obj->type = (uint8_t)a_op_code; DAP_DELETE(l_obj->group); l_obj->group = dap_strdup(a_group); dap_list_t *l_list_out = dap_store_packet_multiple(l_obj, l_obj->timestamp, 1); // Expect only one element in list dap_store_obj_pkt_t *l_data_out = (dap_store_obj_pkt_t *)l_list_out->data; dap_store_obj_free(l_obj, 1); dap_chain_t *l_chain = dap_chain_net_get_chain_by_name(l_net, "gdb"); dap_chain_id_t l_chain_id = l_chain ? l_chain->id : (dap_chain_id_t) {}; for (dap_list_t *l_tmp = PVT(l_net)->links; l_tmp; l_tmp = dap_list_next(l_tmp)) { dap_chain_node_client_t *l_node_client = (dap_chain_node_client_t *)l_tmp->data; dap_stream_ch_t *l_ch_chain = dap_client_get_stream_ch_unsafe(l_node_client->client, dap_stream_ch_chain_get_id()); if (!l_ch_chain) { continue; } dap_stream_ch_chain_pkt_write_mt( dap_client_get_stream_worker(l_node_client->client), l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_GLOBAL_DB, l_net->pub.id, l_chain_id, l_net->pub.cell_id, l_data_out, sizeof(dap_store_obj_pkt_t) + l_data_out->data_size); } dap_list_free_full(l_list_out, free); } } /** * @brief s_gbd_history_callback_notify * @param a_arg * @param a_op_code * @param a_prefix * @param a_group * @param a_key * @param a_value * @param a_value_len */ static void s_gbd_history_callback_notify (void * a_arg, const char a_op_code, const char * a_prefix, const char * a_group, const char * a_key, const void * a_value, const size_t a_value_len) { if (!a_arg) { return; } dap_chain_node_mempool_autoproc_notify(a_arg, a_op_code, a_prefix, a_group, a_key, a_value, a_value_len); dap_chain_net_sync_gdb_broadcast(a_arg, a_op_code, a_prefix, a_group, a_key, a_value, a_value_len); if (s_srv_callback_notify) { s_srv_callback_notify(a_arg, a_op_code, a_prefix, a_group, a_key, a_value, a_value_len); } } /** * @brief s_chain_callback_notify * @param a_arg * @param a_chain * @param a_id */ static void s_chain_callback_notify(void * a_arg, dap_chain_t *a_chain, dap_chain_cell_id_t a_id, void* a_atom, size_t a_atom_size) { if (!a_arg) return; dap_chain_net_t *l_net = (dap_chain_net_t *)a_arg; if (PVT(l_net)->state == NET_STATE_ONLINE) { for (dap_list_t *l_tmp = PVT(l_net)->links; l_tmp; l_tmp = dap_list_next(l_tmp)) { dap_chain_node_client_t *l_node_client = (dap_chain_node_client_t *)l_tmp->data; uint8_t l_ch_id = dap_stream_ch_chain_get_id(); // Channel id for global_db and chains sync dap_stream_ch_t *l_ch_chain = dap_client_get_stream_ch_unsafe(l_node_client->client, l_ch_id); if (!l_ch_chain) { log_it(L_DEBUG,"Can't get stream_ch for id='%c' ", l_ch_id); continue; } dap_stream_ch_chain_pkt_write_mt( dap_client_get_stream_worker( l_node_client->client), l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_CHAIN, l_net->pub.id, a_chain->id, a_id, a_atom, a_atom_size); } } } static void s_fill_links_from_root_aliases(dap_chain_net_t * a_net) { dap_chain_net_pvt_t *l_pvt_net = PVT(a_net); uint64_t l_own_addr = dap_chain_net_get_cur_addr_int(a_net); for (int i = 0; i < MIN(s_max_links_count, l_pvt_net->seed_aliases_count); i++) { if (dap_list_length(l_pvt_net->links_info) >= s_max_links_count) { break; } dap_chain_node_addr_t *l_link_addr = dap_chain_node_alias_find(a_net, l_pvt_net->seed_aliases[i]); if (!l_link_addr){ log_it(L_WARNING, "Not found alias %s", l_pvt_net->seed_aliases[i] ); continue; } if (l_link_addr->uint64 == l_own_addr) { continue; // Do not link with self } dap_chain_node_info_t *l_link_node_info = dap_chain_node_info_read(a_net, l_link_addr); if(l_link_node_info) { l_pvt_net->links_info = dap_list_append(l_pvt_net->links_info, l_link_node_info); } else { log_it(L_WARNING, "Not found link "NODE_ADDR_FP_STR" in the node list", NODE_ADDR_FPS_ARGS(l_link_addr)); } } } /** * @brief s_node_link_callback_connected * @param a_node_client * @param a_arg */ static void s_node_link_callback_connected(dap_chain_node_client_t * a_node_client, void * a_arg) { dap_chain_net_t * l_net = (dap_chain_net_t *) a_arg; } /** * @brief s_node_link_callback_disconnected * @param a_node_client * @param a_arg */ static void s_node_link_callback_disconnected(dap_chain_node_client_t * a_node_client, void * a_arg) { dap_chain_net_t * l_net = (dap_chain_net_t *) a_arg; } /** * @brief s_node_link_callback_stage * @param a_node_client * @param a_stage * @param a_arg */ static void s_node_link_callback_stage(dap_chain_node_client_t * a_node_client,dap_client_stage_t a_stage, void * a_arg) { dap_chain_net_t * l_net = (dap_chain_net_t *) a_arg; } /** * @brief s_node_link_callback_error * @param a_node_client * @param a_error * @param a_arg */ static void s_node_link_callback_error(dap_chain_node_client_t * a_node_client, int a_error, void * a_arg) { dap_chain_net_t * l_net = (dap_chain_net_t *) a_arg; } /** * @brief s_net_states_proc * @param l_net */ static int s_net_states_proc(dap_chain_net_t *a_net) { dap_chain_net_pvt_t *l_pvt_net = PVT(a_net); int ret = 0; switch (l_pvt_net->state) { case NET_STATE_OFFLINE: { // delete all links dap_list_t *l_tmp = l_pvt_net->links; while (l_tmp) { dap_list_t *l_next =l_tmp->next; dap_chain_node_client_close(l_tmp->data); DAP_DELETE(l_tmp); l_tmp = l_next; } l_pvt_net->links = NULL; dap_list_free_full(l_pvt_net->links_info, free); l_pvt_net->links_info = NULL; if ( l_pvt_net->state_target != NET_STATE_OFFLINE ){ l_pvt_net->state = NET_STATE_LINKS_PREPARE; break; } // disable SYNC_GDB l_pvt_net->flags &= ~F_DAP_CHAIN_NET_GO_SYNC; l_pvt_net->last_sync = 0; } break; case NET_STATE_LINKS_PREPARE: { log_it(L_NOTICE,"%s.state: NET_STATE_LINKS_PREPARE", a_net->pub.name); uint64_t l_own_addr = dap_chain_net_get_cur_addr_int(a_net); if (l_pvt_net->node_info) { for (size_t i = 0; i < l_pvt_net->node_info->hdr.links_number; i++) { dap_chain_node_info_t *l_link_node_info = dap_chain_node_info_read(a_net, &l_pvt_net->node_info->links[i]); if (!l_link_node_info || l_link_node_info->hdr.address.uint64 == l_own_addr) { continue; // Do not link with self } l_pvt_net->links_info = dap_list_append(l_pvt_net->links_info, l_link_node_info); if (dap_list_length(l_pvt_net->links_info) >= s_max_links_count) { break; } } } else { log_it(L_WARNING,"No nodeinfo in global_db to prepare links for connecting, try to add links from root servers"); } switch (l_pvt_net->node_role.enums) { case NODE_ROLE_ROOT: case NODE_ROLE_ROOT_MASTER: case NODE_ROLE_ARCHIVE: case NODE_ROLE_CELL_MASTER: { if (l_pvt_net->seed_aliases_count) { // Add other root nodes as synchronization links s_fill_links_from_root_aliases(a_net); break; } } case NODE_ROLE_FULL: case NODE_ROLE_MASTER: case NODE_ROLE_LIGHT: default: { // Get DNS request result from root nodes as synchronization links int l_max_tries = 5; int l_tries = 0; while (dap_list_length(l_pvt_net->links_info) < s_max_links_count && l_tries < l_max_tries) { struct in_addr l_addr = {}; uint16_t i, l_port; if (l_pvt_net->seed_aliases_count) { i = rand() % l_pvt_net->seed_aliases_count; dap_chain_node_addr_t *l_remote_addr = dap_chain_node_alias_find(a_net, l_pvt_net->seed_aliases[i]); dap_chain_node_info_t *l_remote_node_info = dap_chain_node_info_read(a_net, l_remote_addr); l_addr.s_addr = l_remote_node_info ? l_remote_node_info->hdr.ext_addr_v4.s_addr : 0; DAP_DELETE(l_remote_node_info); l_port = DNS_LISTEN_PORT; } else if (l_pvt_net->bootstrap_nodes_count) { i = rand() % l_pvt_net->bootstrap_nodes_count; l_addr = l_pvt_net->bootstrap_nodes_addrs[i]; l_port = l_pvt_net->bootstrap_nodes_ports[i]; } else { log_it(L_ERROR, "No root servers present in configuration file. Can't establish DNS requests"); if (!dap_list_length(l_pvt_net->links_info)) { // No links can be prepared, go offline l_pvt_net->state_target = NET_STATE_OFFLINE; } } if (l_addr.s_addr) { dap_chain_node_info_t *l_link_node_info = DAP_NEW_Z(dap_chain_node_info_t); #ifdef DAP_OS_UNIX struct in_addr _in_addr = { .s_addr = l_addr.s_addr }; #else struct in_addr _in_addr = { { .S_addr = l_addr.S_un.S_addr } }; #endif log_it(L_INFO, "dns get addrs %s : %d, net %s", inet_ntoa(_in_addr), l_port, a_net->pub.name); int l_res = dap_dns_client_get_addr(l_addr, l_port, a_net->pub.name, l_link_node_info); if (!l_res && l_link_node_info->hdr.address.uint64 != l_own_addr) { l_pvt_net->links_info = dap_list_append(l_pvt_net->links_info, l_link_node_info); l_tries = 0; } } if (l_pvt_net->state_target == NET_STATE_OFFLINE) { break; } l_tries++; } s_fill_links_from_root_aliases(a_net); } break; } if (l_pvt_net->state_target != NET_STATE_OFFLINE) { if (dap_list_length(l_pvt_net->links_info)) { l_pvt_net->state = NET_STATE_LINKS_CONNECTING; log_it(L_DEBUG, "Prepared %u links, start to establish them", dap_list_length(l_pvt_net->links_info)); }else{ // If no links prepared go to offline log_it(L_WARNING, "Not foun any links, return back to offline"); l_pvt_net->state = NET_STATE_OFFLINE; l_pvt_net->state_target = NET_STATE_OFFLINE; } } else { l_pvt_net->state = NET_STATE_OFFLINE; } } break; case NET_STATE_LINKS_CONNECTING: { log_it(L_DEBUG, "%s.state: NET_STATE_LINKS_CONNECTING",a_net->pub.name); for (dap_list_t *l_tmp = l_pvt_net->links_info; l_tmp; l_tmp = dap_list_next(l_tmp)) { dap_chain_node_info_t *l_link_info = (dap_chain_node_info_t *)l_tmp->data; dap_chain_node_client_t *l_node_client = dap_chain_node_client_create_n_connect(l_link_info,"CN",s_node_link_callback_connected, s_node_link_callback_disconnected,s_node_link_callback_stage, s_node_link_callback_error,NULL); if (l_node_client) { // wait connected int res = dap_chain_node_client_wait(l_node_client, NODE_CLIENT_STATE_CONNECTED, 20000 ); if (res == 0 ) { log_it(L_DEBUG, "Established connection with "NODE_ADDR_FP_STR, NODE_ADDR_FP_ARGS_S(l_link_info->hdr.address)); l_pvt_net->links = dap_list_append(l_pvt_net->links, l_node_client); } else { log_it(L_DEBUG, "Can't establish link with "NODE_ADDR_FP_STR, NODE_ADDR_FP_ARGS_S(l_link_info->hdr.address)); dap_chain_node_client_close(l_node_client); l_node_client = NULL; } } if (dap_list_length(l_pvt_net->links) >= s_required_links_count) { break; } } if (l_pvt_net->links) { // We have at least one working link l_pvt_net->state = NET_STATE_SYNC_GDB; } else { // Try to find another links struct timespec l_sleep = {3, 0}; nanosleep(&l_sleep, NULL); l_pvt_net->state = NET_STATE_OFFLINE; } } break; case NET_STATE_SYNC_GDB:{ for (dap_list_t *l_tmp = l_pvt_net->links; l_tmp; ) { dap_chain_node_client_t *l_node_client = (dap_chain_node_client_t *)l_tmp->data; dap_stream_worker_t *l_worker = dap_client_get_stream_worker(l_node_client->client); dap_stream_ch_t *l_ch_chain = dap_client_get_stream_ch_unsafe(l_node_client->client, dap_stream_ch_chain_get_id()); if ( !l_ch_chain) { // Channel or stream or client itself closed l_tmp = dap_list_next(l_tmp); dap_chain_node_client_close(l_node_client); l_pvt_net->links = dap_list_remove(l_pvt_net->links, l_node_client); continue; } dap_stream_ch_chain_sync_request_t l_sync_gdb = {}; // Get last timestamp in log if wasn't SYNC_FROM_ZERO flag if (! (l_pvt_net->flags & F_DAP_CHAIN_NET_SYNC_FROM_ZERO) ) l_sync_gdb.id_start = (uint64_t) dap_db_get_last_id_remote(l_node_client->remote_node_addr.uint64); l_sync_gdb.node_addr.uint64 = dap_chain_net_get_cur_addr_int(a_net); log_it(L_DEBUG, "Prepared request to gdb sync from %llu to %llu", l_sync_gdb.id_start, l_sync_gdb.id_end?l_sync_gdb.id_end:-1 ); // find dap_chain_id_t dap_chain_t *l_chain = dap_chain_net_get_chain_by_name(a_net, "gdb"); dap_chain_id_t l_chain_id = l_chain ? l_chain->id : (dap_chain_id_t ) {0}; dap_chain_node_client_reset(l_node_client); size_t l_res = dap_stream_ch_chain_pkt_write_mt(l_worker, l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNC_GLOBAL_DB, a_net->pub.id, l_chain_id, a_net->pub.cell_id, &l_sync_gdb, sizeof(l_sync_gdb)); if (l_res == 0) { log_it(L_WARNING, "Can't send GDB sync request"); continue; } // wait for finishing of request int timeout_ms = 300000; // 5 min = 300 sec = 300 000 ms // TODO add progress info to console l_res = dap_chain_node_client_wait(l_node_client, NODE_CLIENT_STATE_SYNCED, timeout_ms); switch (l_res) { case -1: log_it(L_WARNING, "Timeout with link sync gdb"); break; case 0: log_it(L_INFO, "Node sync gdb completed"); break; default: log_it(L_INFO, "Node sync gdb error %d",l_res); } dap_chain_node_client_reset(l_node_client); l_res = dap_stream_ch_chain_pkt_write_mt(l_worker, l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNC_GLOBAL_DB_RVRS, a_net->pub.id, l_chain_id, a_net->pub.cell_id, &l_sync_gdb, sizeof(l_sync_gdb)); l_res = dap_chain_node_client_wait(l_node_client, NODE_CLIENT_STATE_SYNCED, timeout_ms); switch (l_res) { case -1: log_it(L_WARNING, "Timeout with reverse link gdb sync"); break; case 0: log_it(L_INFO, "Node reverse gdb sync completed"); break; default: log_it(L_INFO, "Node reverse gdb sync error %d",l_res); } l_tmp = dap_list_next(l_tmp); } if (!l_pvt_net->links) { l_pvt_net->state = NET_STATE_LINKS_PREPARE; } else if (l_pvt_net->state_target >= NET_STATE_SYNC_CHAINS) { l_pvt_net->state = NET_STATE_SYNC_CHAINS; } else { // Synchronization done, go offline log_it(L_INFO, "Synchronization done, go offline"); l_pvt_net->flags ^= F_DAP_CHAIN_NET_GO_SYNC; l_pvt_net->flags ^= F_DAP_CHAIN_NET_SYNC_FROM_ZERO; l_pvt_net->state = l_pvt_net->state_target = NET_STATE_OFFLINE; } } break; case NET_STATE_SYNC_CHAINS: { bool l_need_flush = false; for (dap_list_t *l_tmp = l_pvt_net->links; l_tmp; l_tmp = dap_list_next(l_tmp)) { dap_chain_node_client_t *l_node_client = (dap_chain_node_client_t *)l_tmp->data; dap_stream_ch_t *l_ch_chain = dap_client_get_stream_ch_unsafe(l_node_client->client, dap_stream_ch_chain_get_id()); if (!l_ch_chain) { // Channel or stream or client itself closed l_tmp = dap_list_next(l_tmp); dap_chain_node_client_close(l_node_client); l_pvt_net->links = dap_list_remove(l_pvt_net->links, l_node_client); continue; } dap_stream_worker_t *l_worker = dap_client_get_stream_worker(l_node_client->client); dap_chain_t * l_chain = NULL; int l_res = 0; DL_FOREACH (a_net->pub.chains, l_chain) { dap_chain_node_client_reset(l_node_client); dap_stream_ch_chain_sync_request_t l_request = {0}; // TODO: Uncomment next block when finish with partial updates /* if (! (l_pvt_net->flags & F_DAP_CHAIN_NET_SYNC_FROM_ZERO) ) dap_chain_get_atom_last_hash(l_chain,&l_request.hash_from); */ if ( !dap_hash_fast_is_blank(&l_request.hash_from) ){ if(dap_log_level_get() <= L_DEBUG){ char l_hash_str[128]={[0]='\0'}; dap_chain_hash_fast_to_str(&l_request.hash_from,l_hash_str,sizeof (l_hash_str)-1); log_it(L_DEBUG,"Send sync chain request to"NODE_ADDR_FP_STR" for %s:%s from %s to infinity", NODE_ADDR_FP_ARGS_S(l_node_client->remote_node_addr) ,a_net->pub.name, l_chain->name, l_hash_str); } }else log_it(L_DEBUG,"Send sync chain request for all the chains for addr "NODE_ADDR_FP_STR, NODE_ADDR_FP_ARGS_S(l_node_client->remote_node_addr)); dap_stream_ch_chain_pkt_write_mt(l_worker, l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNC_CHAINS, a_net->pub.id, l_chain->id, a_net->pub.cell_id, &l_request, sizeof(l_request)); // wait for finishing of request int timeout_ms = 300000; // 5 min = 300 sec = 300 000 ms // TODO add progress info to console l_res = dap_chain_node_client_wait(l_node_client, NODE_CLIENT_STATE_SYNCED, timeout_ms); switch (l_res) { case -1: //log_it(L_WARNING, "Timeout with sync of chain '%s' ", l_chain->name); break; case 0: l_need_flush = true; log_it(L_INFO, "Sync of chain '%s' completed ", l_chain->name); break; default: log_it(L_ERROR, "Sync of chain '%s' error %d", l_chain->name,l_res); } dap_chain_node_client_reset(l_node_client); l_request.node_addr.uint64 = dap_chain_net_get_cur_addr_int(a_net); dap_stream_ch_chain_pkt_write_mt(l_worker, l_ch_chain, DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNC_CHAINS_RVRS, a_net->pub.id, l_chain->id, a_net->pub.cell_id, &l_request, sizeof(l_request)); l_res = dap_chain_node_client_wait(l_node_client, NODE_CLIENT_STATE_SYNCED, timeout_ms); switch (l_res) { case -1: //log_it(L_WARNING, "Timeout with reverse sync of chain '%s' ", l_chain->name); break; case 0: l_need_flush = true; log_it(L_INFO, "Reverse sync of chain '%s' completed ", l_chain->name); // set time of last sync { struct timespec l_to; clock_gettime(CLOCK_MONOTONIC, &l_to); l_pvt_net->last_sync = l_to.tv_sec; } break; default: log_it(L_ERROR, "Reverse sync of chain '%s' error %d", l_chain->name,l_res); } } l_tmp = dap_list_next(l_tmp); } if (l_need_flush) { // flush global_db dap_chain_global_db_flush(); } if (!l_pvt_net->links ) { log_it( L_INFO,"Return back to state LINKS_PREPARE "); l_pvt_net->state = NET_STATE_LINKS_PREPARE; } else { if (l_pvt_net->state_target == NET_STATE_ONLINE) { l_pvt_net->flags ^= F_DAP_CHAIN_NET_GO_SYNC; l_pvt_net->flags ^= F_DAP_CHAIN_NET_SYNC_FROM_ZERO; l_pvt_net->state = NET_STATE_ONLINE; log_it(L_INFO, "Synchronization done, status online"); } else { // Synchronization done, go offline l_pvt_net->state = l_pvt_net->state_target = NET_STATE_OFFLINE; log_it(L_INFO, "Synchronization done, go offline"); } } } break; case NET_STATE_ONLINE: { if (l_pvt_net->flags & F_DAP_CHAIN_NET_GO_SYNC) { switch ( l_pvt_net->state_target) { // disconnect case NET_STATE_OFFLINE: l_pvt_net->state = NET_STATE_OFFLINE; log_it(L_NOTICE, "Going to disconnet"); break; case NET_STATE_ONLINE: case NET_STATE_SYNC_GDB: case NET_STATE_SYNC_CHAINS: l_pvt_net->state = NET_STATE_SYNC_GDB; break; default: break; } } } break; default: log_it (L_DEBUG, "Unprocessed state"); } return ret; } /** * @brief s_net_proc_thread * @details Brings up and check the Dap Chain Network * @param a_cfg Network1 configuration * @return */ static void *s_net_check_thread ( void *a_net ) { dap_chain_net_t *l_net = (dap_chain_net_t *)a_net; dap_chain_net_pvt_t *l_net_pvt = (dap_chain_net_pvt_t *)(void *)l_net->pvt; assert(l_net); assert(l_net_pvt); log_it(L_NOTICE, "Running chain net context thread for network \"%s\"", l_net->pub.name); // set callback to update data //dap_chain_global_db_set_callback_for_update_base(s_net_proc_thread_callback_update_db); while(1){ if ( l_net_pvt->flags & F_DAP_CHAIN_NET_SHUTDOWN ) { log_it(L_NOTICE,"Shutdown chain net context thread for network \"%s\" (flags 0x%08X)",l_net->pub.name , l_net_pvt->flags); return NULL; } //log_it(L_DEBUG, "Check net states"); // check or start sync s_net_states_proc( l_net ); if (l_net_pvt->flags & F_DAP_CHAIN_NET_GO_SYNC) { // check or start sync s_net_states_proc( l_net ); continue; } struct timespec l_to; // checking whether new sync is needed time_t l_sync_timeout = 180; // 1800 sec = 30 min clock_gettime(CLOCK_MONOTONIC, &l_to); // start sync every l_sync_timeout sec if(l_to.tv_sec >= l_net_pvt->last_sync + l_sync_timeout) { l_net_pvt->flags |= F_DAP_CHAIN_NET_GO_SYNC; s_net_states_proc( l_net ); } //log_it(L_DEBUG, "Sleep on 10 seconds..."); sleep(10); } return NULL; } /** * @brief net_proc_start * @param a_cfg */ static void s_net_check_thread_start( dap_chain_net_t * a_net ) { pthread_create(&s_net_check_pid,NULL, s_net_check_thread, a_net); } dap_chain_node_role_t dap_chain_net_get_role(dap_chain_net_t * a_net) { return PVT(a_net)->node_role; } /** * @brief dap_chain_net_new * @param a_id * @param a_name * @param a_node_role * @param a_node_name * @return */ static dap_chain_net_t *s_net_new(const char * a_id, const char * a_name , const char * a_node_role) { dap_chain_net_t *ret = DAP_NEW_Z_SIZE( dap_chain_net_t, sizeof(ret->pub) + sizeof(dap_chain_net_pvt_t) ); ret->pub.name = strdup( a_name ); #ifndef _WIN32 pthread_condattr_t l_attr; pthread_condattr_init( &l_attr ); pthread_condattr_setclock( &l_attr, CLOCK_MONOTONIC ); pthread_cond_init( &PVT(ret)->state_proc_cond, &l_attr ); #else PVT(ret)->state_proc_cond = CreateEventA( NULL, FALSE, FALSE, NULL ); #endif if ( sscanf(a_id,"0x%016lx", &ret->pub.id.uint64 ) == 1 ){ if (strcmp (a_node_role, "root_master")==0){ PVT(ret)->node_role.enums = NODE_ROLE_ROOT_MASTER; log_it (L_NOTICE, "Node role \"root master\" selected"); } else if (strcmp( a_node_role,"root") == 0){ PVT(ret)->node_role.enums = NODE_ROLE_ROOT; log_it (L_NOTICE, "Node role \"root\" selected"); } else if (strcmp( a_node_role,"archive") == 0){ PVT(ret)->node_role.enums = NODE_ROLE_ARCHIVE; log_it (L_NOTICE, "Node role \"archive\" selected"); } else if (strcmp( a_node_role,"cell_master") == 0){ PVT(ret)->node_role.enums = NODE_ROLE_CELL_MASTER; log_it (L_NOTICE, "Node role \"cell master\" selected"); }else if (strcmp( a_node_role,"master") == 0){ PVT(ret)->node_role.enums = NODE_ROLE_MASTER; log_it (L_NOTICE, "Node role \"master\" selected"); }else if (strcmp( a_node_role,"full") == 0){ PVT(ret)->node_role.enums = NODE_ROLE_FULL; log_it (L_NOTICE, "Node role \"full\" selected"); }else if (strcmp( a_node_role,"light") == 0){ PVT(ret)->node_role.enums = NODE_ROLE_LIGHT; log_it (L_NOTICE, "Node role \"light\" selected"); }else{ log_it(L_ERROR,"Unknown node role \"%s\"",a_node_role); DAP_DELETE(ret); return NULL; } } else { log_it (L_ERROR, "Wrong id format (\"%s\"). Must be like \"0x0123456789ABCDE\"" , a_id ); DAP_DELETE(ret); return NULL; } return ret; } /** * @brief dap_chain_net_delete * @param a_net */ void dap_chain_net_delete( dap_chain_net_t * a_net ) { if(PVT(a_net)->seed_aliases) { DAP_DELETE(PVT(a_net)->seed_aliases); PVT(a_net)->seed_aliases = NULL; } DAP_DELETE( PVT(a_net) ); } /** * @brief dap_chain_net_init * @return */ int dap_chain_net_init() { dap_chain_node_cli_cmd_item_create ("net", s_cli_net, NULL, "Network commands", "net -net <chain net name> [-mode update|all] go < online | offline >\n" "\tFind and establish links and stay online. \n" "\tMode \"update\" is by default when only new chains and gdb are updated. Mode \"all\" updates everything from zero\n" "net -net <chain net name> get status\n" "\tLook at current status\n" "net -net <chain net name> stats tx [-from <From time>] [-to <To time>] [-prev_sec <Seconds>] \n" "\tTransactions statistics. Time format is <Year>-<Month>-<Day>_<Hours>:<Minutes>:<Seconds> or just <Seconds> \n" "net -net <chain net name> [-mode update|all] sync < all | gdb | chains >\n" "\tSyncronyze gdb, chains or everything\n" "\tMode \"update\" is by default when only new chains and gdb are updated. Mode \"all\" updates everything from zero\n" "net -net <chain net name> link < list | add | del | info | establish >\n" "\tList, add, del, dump or establish links\n" "net -net <chain net name> ca add {-cert <cert name> | -hash <cert hash>}\n" "\tAdd certificate to list of authority cetificates in GDB group\n" "net -net <chain net name> ca list\n" "\tPrint list of authority cetificates from GDB group\n" "net -net <chain net name> ca del -hash <cert hash> [-H hex|base58(default)]\n" "\tDelete certificate from list of authority cetificates in GDB group by it's hash\n" "net -net <chain net name> ledger reload\n" "\tPurge the cache of chain net ledger and recalculate it from chain file\n" ); s_seed_mode = dap_config_get_item_bool_default(g_config,"general","seed_mode",false); dap_chain_global_db_add_history_group_prefix("global", GROUP_LOCAL_HISTORY); dap_chain_global_db_add_history_callback_notify("global", s_gbd_history_callback_notify, NULL ); // maximum number of connections to other nodes s_max_links_count = dap_config_get_item_int32_default(g_config, "general", "max_links", s_max_links_count); // required number of connections to other nodes s_required_links_count = dap_config_get_item_int32_default(g_config, "general", "require_links", s_required_links_count); dap_chain_net_load_all(); dap_enc_http_set_acl_callback(dap_chain_net_set_acl); log_it(L_NOTICE,"Chain networks initialized"); return 0; } void dap_chain_net_load_all() { char * l_net_dir_str = dap_strdup_printf("%s/network", dap_config_path()); DIR * l_net_dir = opendir( l_net_dir_str); if ( l_net_dir ){ struct dirent * l_dir_entry; uint16_t l_acl_idx = 0; while ( (l_dir_entry = readdir(l_net_dir) )!= NULL ){ if (l_dir_entry->d_name[0]=='\0' || l_dir_entry->d_name[0]=='.') continue; // don't search in directories char * l_full_path = dap_strdup_printf("%s/%s", l_net_dir_str, l_dir_entry->d_name); if(dap_dir_test(l_full_path)) { DAP_DELETE(l_full_path); continue; } DAP_DELETE(l_full_path); // search only ".cfg" files if(strlen(l_dir_entry->d_name) > 4) { // It has non zero name excluding file extension if(strncmp(l_dir_entry->d_name + strlen(l_dir_entry->d_name) - 4, ".cfg", 4) != 0) { // its not .cfg file continue; } } log_it(L_DEBUG,"Network config %s try to load", l_dir_entry->d_name); //char* l_dot_pos = rindex(l_dir_entry->d_name,'.'); char* l_dot_pos = strchr(l_dir_entry->d_name,'.'); if ( l_dot_pos ) *l_dot_pos = '\0'; s_net_load(l_dir_entry->d_name, l_acl_idx++); } closedir(l_net_dir); }else{ int l_errno = errno; char l_errbuf[128]; l_errbuf[0] = 0; strerror_r(l_errno,l_errbuf,sizeof (l_errbuf)); log_it(L_WARNING,"Can't open entries on path %s: \"%s\" (code %d)", l_net_dir_str, l_errbuf, l_errno); } DAP_DELETE (l_net_dir_str); } void s_set_reply_text_node_status(char **a_str_reply, dap_chain_net_t * a_net){ char* l_node_address_text_block = NULL; dap_chain_node_addr_t l_cur_node_addr = { 0 }; l_cur_node_addr.uint64 = dap_chain_net_get_cur_addr_int(a_net); if(!l_cur_node_addr.uint64) l_node_address_text_block = dap_strdup_printf(", cur node address not defined"); else l_node_address_text_block = dap_strdup_printf(", cur node address " NODE_ADDR_FP_STR,NODE_ADDR_FP_ARGS_S(l_cur_node_addr)); char* l_sync_current_link_text_block = NULL; if (PVT(a_net)->state != NET_STATE_OFFLINE) l_sync_current_link_text_block = dap_strdup_printf(", active links %u from %u", dap_list_length(PVT(a_net)->links), dap_list_length(PVT(a_net)->links_info)); dap_chain_node_cli_set_reply_text(a_str_reply, "Network \"%s\" has state %s (target state %s)%s%s", a_net->pub.name, c_net_states[PVT(a_net)->state], c_net_states[PVT(a_net)->state_target], (l_sync_current_link_text_block)? l_sync_current_link_text_block: "", l_node_address_text_block ); DAP_DELETE(l_sync_current_link_text_block); DAP_DELETE(l_node_address_text_block); } /** * @brief s_cli_net * @param argc * @param argv * @param arg_func * @param str_reply * @return */ static int s_cli_net( int argc, char **argv, void *arg_func, char **a_str_reply) { UNUSED(arg_func); int arg_index = 1; dap_chain_net_t * l_net = NULL; const char * l_hash_out_type = NULL; dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-H", &l_hash_out_type); if(!l_hash_out_type) l_hash_out_type = "hex"; if(dap_strcmp(l_hash_out_type,"hex") && dap_strcmp(l_hash_out_type,"base58")) { dap_chain_node_cli_set_reply_text(a_str_reply, "invalid parameter -H, valid values: -H <hex | base58>"); return -1; } // command 'net list' if(dap_chain_node_cli_find_option_val(argv, arg_index, argc, "list", NULL) == arg_index) { dap_string_t *l_string_ret = dap_string_new("list of nets: "); // show list of nets dap_chain_net_item_t * l_net_item, *l_net_item_tmp; int l_net_i = 0; HASH_ITER(hh, s_net_items, l_net_item, l_net_item_tmp) { if(l_net_i > 0) dap_string_append(l_string_ret, ", "); dap_string_append_printf(l_string_ret, "%s", l_net_item->name); l_net_i++; } if(!l_net_i) dap_string_append(l_string_ret, "-\n"); else dap_string_append(l_string_ret, "\n"); dap_chain_node_cli_set_reply_text(a_str_reply, l_string_ret->str); dap_string_free(l_string_ret, true); return 0; } int ret = dap_chain_node_cli_cmd_values_parse_net_chain( &arg_index, argc, argv, a_str_reply, NULL, &l_net ); if ( l_net ) { const char *l_sync_str = NULL; const char *l_links_str = NULL; const char *l_go_str = NULL; const char *l_get_str = NULL; const char *l_stats_str = NULL; const char *l_ca_str = NULL; const char *l_ledger_str = NULL; dap_chain_node_cli_find_option_val(argv, arg_index, argc, "sync", &l_sync_str); dap_chain_node_cli_find_option_val(argv, arg_index, argc, "link", &l_links_str); dap_chain_node_cli_find_option_val(argv, arg_index, argc, "go", &l_go_str); dap_chain_node_cli_find_option_val(argv, arg_index, argc, "get", &l_get_str); dap_chain_node_cli_find_option_val(argv, arg_index, argc, "stats", &l_stats_str); dap_chain_node_cli_find_option_val(argv, arg_index, argc, "ca", &l_ca_str); dap_chain_node_cli_find_option_val(argv, arg_index, argc, "ledger", &l_ledger_str); const char * l_sync_mode_str = "updates"; dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-mode", &l_sync_mode_str); if ( !dap_strcmp(l_sync_mode_str,"all") ) dap_chain_net_get_flag_sync_from_zero(l_net); if ( l_stats_str ){ if ( strcmp(l_stats_str,"tx") == 0 ) { const char *l_to_str = NULL; struct tm l_to_tm = {0}; const char *l_from_str = NULL; struct tm l_from_tm = {0}; const char *l_prev_sec_str = NULL; //time_t l_prev_sec_ts; const char c_time_fmt[]="%Y-%m-%d_%H:%M:%S"; // Read from/to time dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-from", &l_from_str); dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-to", &l_to_str); dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-prev_sec", &l_prev_sec_str); if (l_from_str ) { strptime( (char *)l_from_str, c_time_fmt, &l_from_tm ); } if (l_to_str) { strptime( (char *)l_to_str, c_time_fmt, &l_to_tm ); } if ( l_to_str == NULL ){ // If not set '-to' - we set up current time time_t l_ts_now = time(NULL); localtime_r(&l_ts_now, &l_to_tm); } if ( l_prev_sec_str ){ time_t l_ts_now = time(NULL); l_ts_now -= strtol( l_prev_sec_str, NULL,10 ); localtime_r(&l_ts_now, &l_from_tm ); }/*else if ( l_from_str == NULL ){ // If not set '-from' we set up current time minus 10 seconds time_t l_ts_now = time(NULL); l_ts_now -= 10; localtime_r(&l_ts_now, &l_from_tm ); }*/ // Form timestamps from/to time_t l_from_ts = mktime(&l_from_tm); time_t l_to_ts = mktime(&l_to_tm); // Produce strings char l_from_str_new[50]; char l_to_str_new[50]; strftime(l_from_str_new, sizeof(l_from_str_new), c_time_fmt,&l_from_tm ); strftime(l_to_str_new, sizeof(l_to_str_new), c_time_fmt,&l_to_tm ); dap_string_t * l_ret_str = dap_string_new("Transactions statistics:\n"); dap_string_append_printf( l_ret_str, "\tFrom: %s\tTo: %s\n", l_from_str_new, l_to_str_new); log_it(L_INFO, "Calc TPS from %s to %s", l_from_str_new, l_to_str_new); uint64_t l_tx_count = dap_chain_ledger_count_from_to ( l_net->pub.ledger, l_from_ts, l_to_ts); long double l_tps = l_to_ts == l_from_ts ? 0 : (long double) l_tx_count / (long double) ( l_to_ts - l_from_ts ); dap_string_append_printf( l_ret_str, "\tSpeed: %.3Lf TPS\n", l_tps ); dap_string_append_printf( l_ret_str, "\tTotal: %llu\n", l_tx_count ); dap_chain_node_cli_set_reply_text( a_str_reply, l_ret_str->str ); dap_string_free( l_ret_str, false ); } } else if ( l_go_str){ if ( strcmp(l_go_str,"online") == 0 ) { dap_chain_net_state_go_to(l_net, NET_STATE_ONLINE); dap_chain_node_cli_set_reply_text(a_str_reply, "Network \"%s\" go from state %s to %s", l_net->pub.name,c_net_states[PVT(l_net)->state], c_net_states[PVT(l_net)->state_target]); } else if ( strcmp(l_go_str,"offline") == 0 ) { dap_chain_net_state_go_to(l_net, NET_STATE_OFFLINE); dap_chain_node_cli_set_reply_text(a_str_reply, "Network \"%s\" go from state %s to %s", l_net->pub.name,c_net_states[PVT(l_net)->state], c_net_states[PVT(l_net)->state_target]); } else if(strcmp(l_go_str, "sync") == 0) { dap_chain_net_state_go_to(l_net, NET_STATE_SYNC_GDB); dap_chain_node_cli_set_reply_text(a_str_reply, "Network \"%s\" go from state %s to %s", l_net->pub.name, c_net_states[PVT(l_net)->state], c_net_states[PVT(l_net)->state_target]); } } else if ( l_get_str){ if ( strcmp(l_get_str,"status") == 0 ) { s_set_reply_text_node_status(a_str_reply, l_net); ret = 0; } } else if ( l_links_str ){ if ( strcmp(l_links_str,"list") == 0 ) { } else if ( strcmp(l_links_str,"add") == 0 ) { } else if ( strcmp(l_links_str,"del") == 0 ) { } else if ( strcmp(l_links_str,"info") == 0 ) { } else if ( strcmp (l_links_str,"disconnect_all") == 0 ){ ret = 0; dap_chain_net_stop(l_net); }else { dap_chain_node_cli_set_reply_text(a_str_reply, "Subcommand \"link\" requires one of parameter: list\n"); ret = -3; } } else if( l_sync_str) { if ( strcmp(l_sync_str,"all") == 0 ) { dap_chain_node_cli_set_reply_text(a_str_reply, "SYNC_ALL state requested to state machine. Current state: %s\n", c_net_states[ PVT(l_net)->state] ); dap_chain_net_sync_all(l_net); } else if ( strcmp(l_sync_str,"gdb") == 0) { dap_chain_node_cli_set_reply_text(a_str_reply, "SYNC_GDB state requested to state machine. Current state: %s\n", c_net_states[ PVT(l_net)->state] ); dap_chain_net_sync_gdb(l_net); } else if ( strcmp(l_sync_str,"chains") == 0) { dap_chain_node_cli_set_reply_text(a_str_reply, "SYNC_CHAINS state requested to state machine. Current state: %s\n", c_net_states[ PVT(l_net)->state] ); dap_chain_net_sync_chains(l_net); } else { dap_chain_node_cli_set_reply_text(a_str_reply, "Subcommand \"sync\" requires one of parameter: all,gdb,chains\n"); ret = -2; } } else if (l_ca_str) { if (strcmp(l_ca_str, "add") == 0 ) { const char *l_cert_string = NULL, *l_hash_string = NULL; dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-cert", &l_cert_string); dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-hash", &l_hash_string); if (!l_cert_string && !l_hash_string) { dap_chain_node_cli_set_reply_text(a_str_reply, "One of -cert or -hash parameters is mandatory"); return -6; } char *l_hash_hex_str; //char *l_hash_base58_str; // hash may be in hex or base58 format if(!dap_strncmp(l_hash_string, "0x", 2) || !dap_strncmp(l_hash_string, "0X", 2)) { l_hash_hex_str = dap_strdup(l_hash_string); //l_hash_base58_str = dap_enc_base58_from_hex_str_to_str(l_hash_string); } else { l_hash_hex_str = dap_enc_base58_to_hex_str_from_str(l_hash_string); //l_hash_base58_str = dap_strdup(l_hash_string); } if (l_cert_string) { dap_cert_t * l_cert = dap_cert_find_by_name(l_cert_string); if (l_cert == NULL) { dap_chain_node_cli_set_reply_text(a_str_reply, "Can't find \"%s\" certificate", l_cert_string); return -7; } if (l_cert->enc_key == NULL) { dap_chain_node_cli_set_reply_text(a_str_reply, "No key found in \"%s\" certificate", l_cert_string ); return -8; } // Get publivc key hash size_t l_pub_key_size = 0; uint8_t *l_pub_key = dap_enc_key_serealize_pub_key(l_cert->enc_key, &l_pub_key_size);; if (l_pub_key == NULL) { dap_chain_node_cli_set_reply_text(a_str_reply, "Can't serialize public key of certificate \"%s\"", l_cert_string); return -9; } dap_chain_hash_fast_t l_pkey_hash; dap_hash_fast(l_pub_key, l_pub_key_size, &l_pkey_hash); l_hash_hex_str = dap_chain_hash_fast_to_str_new(&l_pkey_hash); //l_hash_base58_str = dap_enc_base58_encode_hash_to_str(&l_pkey_hash); } const char c = '1'; char *l_gdb_group_str = dap_chain_net_get_gdb_group_acl(l_net); if (!l_gdb_group_str) { dap_chain_node_cli_set_reply_text(a_str_reply, "Database ACL group not defined for this network"); return -11; } ret = dap_chain_global_db_gr_set(dap_strdup(l_hash_hex_str), (void *)&c, 1, dap_chain_net_get_gdb_group_acl(l_net)); DAP_DELETE(l_gdb_group_str); DAP_DELETE(l_hash_hex_str); if (!ret) { dap_chain_node_cli_set_reply_text(a_str_reply, "Can't save public key hash in database"); return -10; } return 0; } else if (strcmp(l_ca_str, "list") == 0 ) { char *l_gdb_group_str = dap_chain_net_get_gdb_group_acl(l_net); if (!l_gdb_group_str) { dap_chain_node_cli_set_reply_text(a_str_reply, "Database ACL group not defined for this network"); return -11; } size_t l_objs_count; dap_global_db_obj_t *l_objs = dap_chain_global_db_gr_load(l_gdb_group_str, &l_objs_count); DAP_DELETE(l_gdb_group_str); dap_string_t *l_reply = dap_string_new(""); for (size_t i = 0; i < l_objs_count; i++) { dap_string_append(l_reply, l_objs[i].key); dap_string_append(l_reply, "\n"); } dap_chain_global_db_objs_delete(l_objs, l_objs_count); *a_str_reply = l_reply->len ? l_reply->str : dap_strdup("No entries found"); dap_string_free(l_reply, false); return 0; } else if (strcmp(l_ca_str, "del") == 0 ) { const char *l_hash_string = NULL; dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-hash", &l_hash_string); if (!l_hash_string) { dap_chain_node_cli_set_reply_text(a_str_reply, "Format should be 'net ca del -hash <hash string>"); return -6; } char *l_gdb_group_str = dap_chain_net_get_gdb_group_acl(l_net); if (!l_gdb_group_str) { dap_chain_node_cli_set_reply_text(a_str_reply, "Database ACL group not defined for this network"); return -11; } ret = dap_chain_global_db_gr_del((char *)l_hash_string, l_gdb_group_str); DAP_DELETE(l_gdb_group_str); if (!ret) { dap_chain_node_cli_set_reply_text(a_str_reply, "Cant't find certificate public key hash in database"); return -10; } return 0; } else { dap_chain_node_cli_set_reply_text(a_str_reply, "Subcommand \"ca\" requires one of parameter: add, list, del\n"); ret = -5; } } else if (l_ledger_str && !strcmp(l_ledger_str, "reload")) { dap_chain_ledger_purge(l_net->pub.ledger); dap_chain_t *l_chain; DL_FOREACH(l_net->pub.chains, l_chain) { if (l_chain->callback_purge) { l_chain->callback_purge(l_chain); } if (!strcmp(DAP_CHAIN_PVT(l_chain)->cs_name, "none")) { dap_chain_gdb_ledger_load((char *)dap_chain_gdb_get_group(l_chain), l_chain); } else { dap_chain_load_all(l_chain); } } } else { dap_chain_node_cli_set_reply_text(a_str_reply,"Command requires one of subcomand: sync, links\n"); ret = -1; } } return ret; } // for sequential loading chains typedef struct list_priority_{ uint16_t prior; char * chains_path; }list_priority; static int callback_compare_prioritity_list(const void * a_item1, const void * a_item2) { list_priority *l_item1 = (list_priority*) a_item1; list_priority *l_item2 = (list_priority*) a_item2; if(!l_item1 || !l_item2 || l_item1->prior == l_item2->prior) return 0; if(l_item1->prior > l_item2->prior) return 1; return -1; } /** * @brief s_net_load * @param a_net_name * @return */ int s_net_load(const char * a_net_name, uint16_t a_acl_idx) { dap_config_t *l_cfg=NULL; dap_string_t *l_cfg_path = dap_string_new("network/"); dap_string_append(l_cfg_path,a_net_name); if( ( l_cfg = dap_config_open ( l_cfg_path->str ) ) == NULL ) { log_it(L_ERROR,"Can't open default network config"); dap_string_free(l_cfg_path,true); return -1; } else { dap_string_free(l_cfg_path,true); dap_chain_net_t * l_net = s_net_new( dap_config_get_item_str(l_cfg , "general" , "id" ), dap_config_get_item_str(l_cfg , "general" , "name" ), dap_config_get_item_str(l_cfg , "general" , "node-role" ) ); if(!l_net) { log_it(L_ERROR,"Can't create l_net"); return -1; } PVT(l_net)->load_mode = true; PVT(l_net)->acl_idx = a_acl_idx; l_net->pub.gdb_groups_prefix = dap_strdup ( dap_config_get_item_str_default(l_cfg , "general" , "gdb_groups_prefix", dap_config_get_item_str(l_cfg , "general" , "name" ) ) ); dap_chain_global_db_add_history_group_prefix( l_net->pub.gdb_groups_prefix, GROUP_LOCAL_HISTORY); dap_chain_global_db_add_history_callback_notify(l_net->pub.gdb_groups_prefix, s_gbd_history_callback_notify, l_net ); l_net->pub.gdb_nodes = dap_strdup_printf("%s.nodes",l_net->pub.gdb_groups_prefix); l_net->pub.gdb_nodes_aliases = dap_strdup_printf("%s.nodes.aliases",l_net->pub.gdb_groups_prefix); // for sync special groups - nodes char **l_gdb_sync_nodes_addrs = dap_config_get_array_str(l_cfg, "general", "gdb_sync_nodes_addrs", &PVT(l_net)->gdb_sync_nodes_addrs_count); if(l_gdb_sync_nodes_addrs && PVT(l_net)->gdb_sync_nodes_addrs_count > 0) { PVT(l_net)->gdb_sync_nodes_addrs = (dap_chain_node_addr_t*) DAP_NEW_Z_SIZE(char**, sizeof(dap_chain_node_addr_t)*PVT(l_net)->gdb_sync_nodes_addrs_count); for(uint16_t i = 0; i < PVT(l_net)->gdb_sync_nodes_addrs_count; i++) { dap_chain_node_addr_from_str(PVT(l_net)->gdb_sync_nodes_addrs + i, l_gdb_sync_nodes_addrs[i]); } } // for sync special groups - groups char **l_gdb_sync_groups = dap_config_get_array_str(l_cfg, "general", "gdb_sync_groups", &PVT(l_net)->gdb_sync_groups_count); if(l_gdb_sync_groups && PVT(l_net)->gdb_sync_groups_count > 0) { PVT(l_net)->gdb_sync_groups = (char **) DAP_NEW_SIZE(char**, sizeof(char*)*PVT(l_net)->gdb_sync_groups_count); for(uint16_t i = 0; i < PVT(l_net)->gdb_sync_groups_count; i++) { PVT(l_net)->gdb_sync_groups[i] = dap_strdup(l_gdb_sync_groups[i]); // added group to history log dap_list_t *l_groups0 = dap_chain_global_db_driver_get_groups_by_mask(PVT(l_net)->gdb_sync_groups[i]); dap_list_t *l_groups = l_groups0; while(l_groups) { char *l_group_name = l_groups->data; // do not use groups with names like *.del if(dap_fnmatch("*.del", l_group_name, 0)) { const char *l_history_group = dap_chain_global_db_add_history_extra_group(l_group_name, PVT(l_net)->gdb_sync_nodes_addrs, &PVT(l_net)->gdb_sync_nodes_addrs_count); dap_chain_global_db_add_history_extra_group_callback_notify(l_group_name, s_gbd_history_callback_notify, l_net); // create history for group if(dap_db_log_get_group_history_last_id(l_history_group) <= 0) { size_t l_data_size_out = 0; dap_store_obj_t *l_obj = dap_chain_global_db_obj_gr_get(NULL, &l_data_size_out, l_group_name); if(l_obj && l_data_size_out > 0) { dap_db_history_add('a', l_obj, l_data_size_out, l_history_group); dap_store_obj_free(l_obj, l_data_size_out); } } } l_groups = dap_list_next(l_groups); } dap_list_free_full(l_groups0, (dap_callback_destroyed_t)free); } } // Add network to the list dap_chain_net_item_t * l_net_item = DAP_NEW_Z( dap_chain_net_item_t); dap_chain_net_item_t * l_net_item2 = DAP_NEW_Z( dap_chain_net_item_t); dap_snprintf(l_net_item->name,sizeof (l_net_item->name),"%s" ,dap_config_get_item_str(l_cfg , "general" , "name" )); l_net_item->chain_net = l_net; l_net_item->net_id.uint64 = l_net->pub.id.uint64; HASH_ADD_STR(s_net_items,name,l_net_item); memcpy( l_net_item2,l_net_item,sizeof (*l_net_item)); HASH_ADD(hh,s_net_items_ids,net_id,sizeof ( l_net_item2->net_id),l_net_item2); // LEDGER model uint16_t l_ledger_flags = 0; switch ( PVT( l_net )->node_role.enums ) { case NODE_ROLE_ROOT_MASTER: case NODE_ROLE_ROOT: case NODE_ROLE_ARCHIVE: l_ledger_flags |= DAP_CHAIN_LEDGER_CHECK_TOKEN_EMISSION; case NODE_ROLE_MASTER: l_ledger_flags |= DAP_CHAIN_LEDGER_CHECK_CELLS_DS; case NODE_ROLE_CELL_MASTER: l_ledger_flags |= DAP_CHAIN_LEDGER_CHECK_TOKEN_EMISSION; case NODE_ROLE_FULL: case NODE_ROLE_LIGHT: l_ledger_flags |= DAP_CHAIN_LEDGER_CHECK_LOCAL_DS; } // init LEDGER model l_net->pub.ledger = dap_chain_ledger_create(l_ledger_flags, l_net->pub.name); // Check if seed nodes are present in local db alias char **l_seed_aliases = dap_config_get_array_str( l_cfg , "general" ,"seed_nodes_aliases" ,&PVT(l_net)->seed_aliases_count); PVT(l_net)->seed_aliases = PVT(l_net)->seed_aliases_count>0 ? (char **)DAP_NEW_SIZE(char**, sizeof(char*)*PVT(l_net)->seed_aliases_count) : NULL; for(size_t i = 0; i < PVT(l_net)->seed_aliases_count; i++) { PVT(l_net)->seed_aliases[i] = dap_strdup(l_seed_aliases[i]); } uint16_t l_seed_nodes_addrs_len =0; char ** l_seed_nodes_addrs = dap_config_get_array_str( l_cfg , "general" ,"seed_nodes_addrs" ,&l_seed_nodes_addrs_len); uint16_t l_seed_nodes_ipv4_len =0; char ** l_seed_nodes_ipv4 = dap_config_get_array_str( l_cfg , "general" ,"seed_nodes_ipv4" ,&l_seed_nodes_ipv4_len); uint16_t l_seed_nodes_ipv6_len =0; char ** l_seed_nodes_ipv6 = dap_config_get_array_str( l_cfg , "general" ,"seed_nodes_ipv6" ,&l_seed_nodes_ipv6_len); uint16_t l_seed_nodes_hostnames_len =0; char ** l_seed_nodes_hostnames = dap_config_get_array_str( l_cfg , "general" ,"seed_nodes_hostnames" ,&l_seed_nodes_hostnames_len); uint16_t l_seed_nodes_port_len =0; char ** l_seed_nodes_port = dap_config_get_array_str( l_cfg , "general" ,"seed_nodes_port" ,&l_seed_nodes_port_len); uint16_t l_bootstrap_nodes_len = 0; char **l_bootstrap_nodes = dap_config_get_array_str(l_cfg, "general", "bootstrap_hostnames", &l_bootstrap_nodes_len); const char * l_node_addr_type = dap_config_get_item_str_default(l_cfg , "general" ,"node_addr_type","auto"); const char * l_node_addr_str = NULL; const char * l_node_alias_str = NULL; // use unique addr from pub key if(!dap_strcmp(l_node_addr_type, "auto")) { size_t l_pub_key_data_size = 0; uint8_t *l_pub_key_data = NULL; // read pub key l_pub_key_data = dap_chain_global_db_gr_get("cur-node-addr-pkey", &l_pub_key_data_size, GROUP_LOCAL_NODE_ADDR); // generate new pub key if(!l_pub_key_data || !l_pub_key_data_size){ const char * l_certs_name_str = "node-addr"; 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) { 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) dap_chain_global_db_gr_set(dap_strdup("cur-node-addr-pkey"), (uint8_t*) l_pub_key_data, l_pub_key_data_size, GROUP_LOCAL_NODE_ADDR); } } // generate addr from pub_key dap_chain_hash_fast_t l_hash; if(l_pub_key_data_size > 0 && dap_hash_fast(l_pub_key_data, l_pub_key_data_size, &l_hash) == 1) { l_node_addr_str = dap_strdup_printf("%04X::%04X::%04X::%04X", (uint16_t) *(uint16_t*) (l_hash.raw), (uint16_t) *(uint16_t*) (l_hash.raw + 2), (uint16_t) *(uint16_t*) (l_hash.raw + DAP_CHAIN_HASH_FAST_SIZE - 4), (uint16_t) *(uint16_t*) (l_hash.raw + DAP_CHAIN_HASH_FAST_SIZE - 2)); } DAP_DELETE(l_pub_key_data); } // use static addr from setting else if(!dap_strcmp(l_node_addr_type, "static")) { //const char * l_node_ipv4_str = dap_config_get_item_str(l_cfg , "general" ,"node-ipv4"); l_node_addr_str = dap_strdup(dap_config_get_item_str(l_cfg, "general", "node-addr")); l_node_alias_str = dap_config_get_item_str(l_cfg, "general", "node-alias"); } log_it (L_DEBUG, "Read %u aliases, %u address and %u ipv4 addresses, check them", PVT(l_net)->seed_aliases_count,l_seed_nodes_addrs_len, l_seed_nodes_ipv4_len ); // save new nodes from cfg file to db for ( size_t i = 0; i < PVT(l_net)->seed_aliases_count && i < l_seed_nodes_addrs_len && ( ( l_seed_nodes_ipv4_len && i < l_seed_nodes_ipv4_len ) || ( l_seed_nodes_ipv6_len && i < l_seed_nodes_ipv6_len ) || ( l_seed_nodes_hostnames_len && i < l_seed_nodes_hostnames_len ) ) ; i++ ){ dap_chain_node_addr_t * l_seed_node_addr; l_seed_node_addr = dap_chain_node_alias_find(l_net, PVT(l_net)->seed_aliases[i] ); //if (l_seed_node_addr == NULL){ log_it(L_NOTICE, "Update alias %s in database, prefill it",PVT(l_net)->seed_aliases[i]); dap_chain_node_info_t * l_node_info = DAP_NEW_Z(dap_chain_node_info_t); l_seed_node_addr = DAP_NEW_Z(dap_chain_node_addr_t); dap_snprintf( l_node_info->hdr.alias,sizeof ( l_node_info->hdr.alias),"%s",PVT(l_net)->seed_aliases[i]); if (sscanf(l_seed_nodes_addrs[i],NODE_ADDR_FP_STR, NODE_ADDR_FPS_ARGS(l_seed_node_addr) ) != 4 ){ log_it(L_ERROR,"Wrong address format, should be like 0123::4567::890AB::CDEF"); DAP_DELETE(l_seed_node_addr); DAP_DELETE(l_node_info); l_seed_node_addr = NULL; continue; } if( l_seed_node_addr ){ if ( l_seed_nodes_ipv4_len ) inet_pton( AF_INET, l_seed_nodes_ipv4[i],&l_node_info->hdr.ext_addr_v4); if ( l_seed_nodes_ipv6_len ) inet_pton( AF_INET6, l_seed_nodes_ipv6[i],&l_node_info->hdr.ext_addr_v6); if(l_seed_nodes_port_len && l_seed_nodes_port_len >= i) l_node_info->hdr.ext_port = strtoul(l_seed_nodes_port[i], NULL, 10); else l_node_info->hdr.ext_port = 8079; if ( l_seed_nodes_hostnames_len ){ struct addrinfo l_hints={0}; l_hints.ai_family = AF_UNSPEC ; /* Allow IPv4 or IPv6 */ //l_hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ log_it( L_DEBUG, "Resolve %s addr", l_seed_nodes_hostnames[i]); struct hostent *l_he; if ( (l_he = gethostbyname (l_seed_nodes_hostnames[i]) ) != NULL ){ struct in_addr **l_addr_list = (struct in_addr **) l_he->h_addr_list; for(int i = 0; l_addr_list[i] != NULL; i++ ) { log_it( L_NOTICE, "Resolved %s to %s (ipv4)", l_seed_nodes_hostnames[i] , inet_ntoa( *l_addr_list[i] ) ); l_node_info->hdr.ext_addr_v4.s_addr = l_addr_list[i]->s_addr; } } else { herror("gethostname"); } } l_node_info->hdr.address.uint64 = l_seed_node_addr->uint64; if ( l_node_info->hdr.ext_addr_v4.s_addr || l_node_info->hdr.ext_addr_v6.s6_addr32[0] ){ int l_ret; if ( (l_ret = dap_chain_node_info_save(l_net, l_node_info)) ==0 ){ if (dap_chain_node_alias_register(l_net,PVT(l_net)->seed_aliases[i],l_seed_node_addr)) log_it(L_NOTICE,"Seed node "NODE_ADDR_FP_STR" added to the curent list",NODE_ADDR_FP_ARGS(l_seed_node_addr) ); else { log_it(L_WARNING,"Cant register alias %s for address "NODE_ADDR_FP_STR,NODE_ADDR_FP_ARGS(l_seed_node_addr)); } }else{ log_it(L_WARNING,"Cant save node info for address "NODE_ADDR_FP_STR" return code %d", NODE_ADDR_FP_ARGS(l_seed_node_addr), l_ret); } } DAP_DELETE( l_seed_node_addr); }else log_it(L_WARNING,"No address for seed node, can't populate global_db with it"); DAP_DELETE( l_node_info); /*}else{ log_it(L_DEBUG,"Seed alias %s is present",PVT(l_net)->seed_aliases[i]); dap_chain_node_info_t * l_node_info= dap_chain_node_info_read(l_net,l_seed_node_addr); l_node DAP_DELETE( l_seed_node_addr); }*/ } PVT(l_net)->bootstrap_nodes_count = 0; PVT(l_net)->bootstrap_nodes_addrs = DAP_NEW_SIZE(struct in_addr, l_bootstrap_nodes_len * sizeof(struct in_addr)); PVT(l_net)->bootstrap_nodes_ports = DAP_NEW_SIZE(uint16_t, l_bootstrap_nodes_len * sizeof(uint16_t)); for (int i = 0; i < l_bootstrap_nodes_len; i++) { char *l_bootstrap_port_str = strchr(l_bootstrap_nodes[i], ':'); if (!l_bootstrap_port_str) { continue; } uint16_t l_bootstrap_port = atoi(l_bootstrap_port_str + 1); if (!l_bootstrap_port) { continue; } int l_bootstrap_name_len = l_bootstrap_port_str - l_bootstrap_nodes[i]; char *l_bootstrap_name = DAP_NEW_SIZE(char, l_bootstrap_name_len + 1); strncpy(l_bootstrap_name, l_bootstrap_nodes[i], l_bootstrap_name_len); struct in_addr l_bootstrap_addr; if (dap_net_resolve_host(l_bootstrap_name, AF_INET, (struct sockaddr* )&l_bootstrap_addr) == 0) { PVT(l_net)->bootstrap_nodes_addrs[PVT(l_net)->bootstrap_nodes_count] = l_bootstrap_addr; PVT(l_net)->bootstrap_nodes_ports[PVT(l_net)->bootstrap_nodes_count] = l_bootstrap_port; PVT(l_net)->bootstrap_nodes_count++; } DAP_DELETE(l_bootstrap_name); } if ( l_node_addr_str || l_node_alias_str ){ dap_chain_node_addr_t * l_node_addr; if ( l_node_addr_str == NULL) l_node_addr = dap_chain_node_alias_find(l_net, l_node_alias_str); else{ l_node_addr = DAP_NEW_Z(dap_chain_node_addr_t); bool parse_succesfully = false; if ( sscanf(l_node_addr_str, "0x%016" DAP_UINT64_FORMAT_x ,&l_node_addr->uint64 ) == 1 ){ log_it(L_DEBUG, "Parse node address with format 0x016llx"); parse_succesfully = true; } if ( !parse_succesfully && dap_chain_node_addr_from_str(l_node_addr, l_node_addr_str) == 0) { log_it(L_DEBUG, "Parse node address with format 04hX::04hX::04hX::04hX"); parse_succesfully = true; } if (!parse_succesfully){ log_it(L_ERROR,"Can't parse node address %s", l_node_addr_str); DAP_DELETE(l_node_addr); l_node_addr = NULL; } if(l_node_addr) log_it(L_NOTICE, "Parse node addr " NODE_ADDR_FP_STR " successfully", NODE_ADDR_FP_ARGS(l_node_addr)); PVT(l_net)->node_addr = l_node_addr; //} } if ( l_node_addr ) { char *l_addr_hash_str = dap_chain_node_addr_to_hash_str(l_node_addr); // save current node address dap_db_set_cur_node_addr(l_node_addr->uint64, l_net->pub.name); if(!l_addr_hash_str){ log_it(L_ERROR,"Can't get hash string for node address!"); } else { PVT(l_net)->node_info = dap_chain_node_info_read (l_net, l_node_addr); if ( !PVT(l_net)->node_info ) { // If not present - create it PVT(l_net)->node_info = DAP_NEW_Z(dap_chain_node_info_t); memcpy(&PVT(l_net)->node_info->hdr.address, l_node_addr,sizeof (*l_node_addr)); dap_chain_node_info_save(l_net,PVT(l_net)->node_info); } log_it(L_NOTICE,"GDB Info: node_addr: " NODE_ADDR_FP_STR" links: %u cell_id: 0x%016X ", NODE_ADDR_FP_ARGS(l_node_addr), PVT(l_net)->node_info->hdr.links_number, PVT(l_net)->node_info->hdr.cell_id.uint64); // save cell_id l_net->pub.cell_id.uint64 = PVT(l_net)->node_info->hdr.cell_id.uint64; } } else{ log_it(L_WARNING, "Not present our own address %s in database", (l_node_alias_str) ? l_node_alias_str: ""); } } char * l_chains_path = dap_strdup_printf("%s/network/%s", dap_config_path(), l_net->pub.name); DIR * l_chains_dir = opendir(l_chains_path); DAP_DELETE (l_chains_path); if ( l_chains_dir ){ // for sequential loading chains dap_list_t *l_prior_list = NULL; struct dirent * l_dir_entry; while ( (l_dir_entry = readdir(l_chains_dir) )!= NULL ){ if (l_dir_entry->d_name[0]=='\0') continue; char * l_entry_name = strdup(l_dir_entry->d_name); if (strlen (l_entry_name) > 4 ){ // It has non zero name excluding file extension if ( strncmp (l_entry_name+ strlen(l_entry_name)-4,".cfg",4) == 0 ) { // its .cfg file l_entry_name [strlen(l_entry_name)-4] = 0; log_it(L_DEBUG,"Open chain config \"%s\"...",l_entry_name); l_chains_path = dap_strdup_printf("network/%s/%s",l_net->pub.name,l_entry_name); dap_config_t * l_cfg = dap_config_open(l_chains_path); if(l_cfg) { list_priority *l_chain_prior = DAP_NEW_Z(list_priority); l_chain_prior->prior = dap_config_get_item_uint16_default(l_cfg, "chain", "load_priority", 100); l_chain_prior->chains_path = l_chains_path; // add chain to load list; l_prior_list = dap_list_append(l_prior_list, l_chain_prior); } } } DAP_DELETE (l_entry_name); } closedir(l_chains_dir); // sort list with chains names by priority l_prior_list = dap_list_sort(l_prior_list, callback_compare_prioritity_list); // load chains by priority dap_list_t *l_list = l_prior_list; while(l_list){ list_priority *l_chain_prior = l_list->data; // Create chain object dap_chain_t * l_chain = dap_chain_load_from_cfg(l_net->pub.ledger, l_net->pub.name, l_net->pub.id, l_chain_prior->chains_path); if(l_chain) { DL_APPEND(l_net->pub.chains, l_chain); if(l_chain->callback_created) l_chain->callback_created(l_chain, l_cfg); // add a callback to monitor changes in the chain dap_chain_add_callback_notify(l_chain, s_chain_callback_notify, l_net); } DAP_DELETE (l_chain_prior->chains_path); l_list = dap_list_next(l_list); } dap_list_free(l_prior_list); const char* l_default_chain_name = dap_config_get_item_str(l_cfg , "general" , "default_chain"); if(l_default_chain_name) l_net->pub.default_chain = dap_chain_net_get_chain_by_name(l_net, l_default_chain_name); else l_net->pub.default_chain = NULL; } else { log_it(L_ERROR,"Can't any chains for network %s",l_net->pub.name); PVT(l_net)->load_mode = false; return -2; } // Do specific role actions post-chain created switch ( PVT( l_net )->node_role.enums ) { case NODE_ROLE_ROOT_MASTER:{ // Set to process everything in datum pool dap_chain_t * l_chain = NULL; DL_FOREACH(l_net->pub.chains, l_chain ) l_chain->is_datum_pool_proc = true; log_it(L_INFO,"Root master node role established"); } // Master root includes root case NODE_ROLE_ROOT:{ // Set to process only zerochain dap_chain_id_t l_chain_id = {{0}}; dap_chain_t * l_chain = dap_chain_find_by_id(l_net->pub.id,l_chain_id); if (l_chain ) l_chain->is_datum_pool_proc = true; PVT(l_net)->state_target = NET_STATE_ONLINE; log_it(L_INFO,"Root node role established"); } break; case NODE_ROLE_CELL_MASTER: case NODE_ROLE_MASTER:{ uint16_t l_proc_chains_count=0; char ** l_proc_chains = dap_config_get_array_str(l_cfg,"role-master" , "proc_chains", &l_proc_chains_count ); for ( size_t i = 0; i< l_proc_chains_count ; i++){ dap_chain_id_t l_chain_id = {{0}}; if(dap_sscanf( l_proc_chains[i], "0x%16"DAP_UINT64_FORMAT_X, &l_chain_id.uint64) ==1 || dap_scanf("0x%16"DAP_UINT64_FORMAT_x, &l_chain_id.uint64) == 1){ dap_chain_t * l_chain = dap_chain_find_by_id(l_net->pub.id, l_chain_id ); if ( l_chain ){ l_chain->is_datum_pool_proc = true; }else{ log_it( L_WARNING, "Can't find chain id " ); } } DAP_DELETE( l_proc_chains[i]); l_proc_chains[i] = NULL; } //if ( l_proc_chains ) // DAP_DELETE (l_proc_chains); //l_proc_chains = NULL; PVT(l_net)->state_target = NET_STATE_ONLINE; log_it(L_INFO,"Master node role established"); } break; case NODE_ROLE_FULL:{ log_it(L_INFO,"Full node role established"); PVT(l_net)->state_target = NET_STATE_ONLINE; } break; case NODE_ROLE_LIGHT: default: log_it(L_INFO,"Light node role established"); } if (s_seed_mode || !dap_config_get_item_bool_default(g_config ,"general", "auto_online",false ) ) { // If we seed we do everything manual. First think - prefil list of node_addrs and its aliases PVT(l_net)->state_target = NET_STATE_OFFLINE; } PVT(l_net)->load_mode = false; PVT(l_net)->flags |= F_DAP_CHAIN_NET_GO_SYNC; // Start the proc thread s_net_check_thread_start(l_net); log_it(L_NOTICE, "Сhain network \"%s\" initialized",l_net_item->name); dap_config_close(l_cfg); } return 0; } /** * @brief dap_chain_net_deinit */ void dap_chain_net_deinit() { } dap_chain_net_t **dap_chain_net_list(uint16_t *a_size) { *a_size = HASH_COUNT(s_net_items); if(*a_size){ dap_chain_net_t **l_net_list = DAP_NEW_SIZE(dap_chain_net_t *, (*a_size) * sizeof(dap_chain_net_t *)); dap_chain_net_item_t *l_current_item, *l_tmp; int i = 0; HASH_ITER(hh, s_net_items, l_current_item, l_tmp) { l_net_list[i++] = l_current_item->chain_net; } return l_net_list; }else return NULL; } /** * @brief dap_chain_net_by_name * @param a_name * @return */ dap_chain_net_t * dap_chain_net_by_name( const char * a_name) { dap_chain_net_item_t * l_net_item = NULL; if(a_name) HASH_FIND_STR(s_net_items,a_name,l_net_item ); return l_net_item ? l_net_item->chain_net : NULL; } /** * @brief dap_chain_ledger_by_net_name * @param a_net_name * @return */ dap_ledger_t * dap_chain_ledger_by_net_name( const char * a_net_name) { dap_chain_net_t *l_net = dap_chain_net_by_name(a_net_name); return l_net ? l_net->pub.ledger : NULL; } /** * @brief dap_chain_net_by_id * @param a_id * @return */ dap_chain_net_t * dap_chain_net_by_id( dap_chain_net_id_t a_id) { dap_chain_net_item_t * l_net_item = NULL; HASH_FIND(hh,s_net_items_ids,&a_id,sizeof (a_id), l_net_item ); return l_net_item ? l_net_item->chain_net : NULL; } /** * @brief dap_chain_net_by_id * @param a_id * @return */ uint16_t dap_chain_net_acl_idx_by_id(dap_chain_net_id_t a_id) { dap_chain_net_item_t * l_net_item = NULL; HASH_FIND(hh,s_net_items_ids,&a_id,sizeof (a_id), l_net_item ); return l_net_item ? PVT(l_net_item->chain_net)->acl_idx : (uint16_t)-1; } /** * @brief dap_chain_net_id_by_name * @param a_name * @return */ dap_chain_net_id_t dap_chain_net_id_by_name( const char * a_name) { dap_chain_net_t *l_net = dap_chain_net_by_name( a_name ); dap_chain_net_id_t l_ret = {0}; if (l_net) l_ret.uint64 = l_net->pub.id.uint64; return l_ret; } /** * @brief dap_chain_net_get_chain_by_name * @param l_net * @param a_name * @return */ dap_chain_t * dap_chain_net_get_chain_by_name( dap_chain_net_t * l_net, const char * a_name) { dap_chain_t * l_chain; DL_FOREACH(l_net->pub.chains, l_chain){ if(dap_strcmp(l_chain->name,a_name) == 0) return l_chain; } return NULL; } /** * @brief dap_chain_net_get_chain_by_chain_type * @param a_datum_type * @return */ dap_chain_t * dap_chain_net_get_chain_by_chain_type(dap_chain_net_t * l_net, dap_chain_type_t a_datum_type) { dap_chain_t * l_chain; if(!l_net) return NULL; DL_FOREACH(l_net->pub.chains, l_chain) { for(uint16_t i = 0; i < l_chain->datum_types_count; i++) { if(l_chain->datum_types[i] == a_datum_type) return l_chain; } } return NULL; } /** * @brief dap_chain_net_get_gdb_group_mempool_by_chain_type * @param a_datum_type * @return */ char * dap_chain_net_get_gdb_group_mempool_by_chain_type(dap_chain_net_t * l_net, dap_chain_type_t a_datum_type) { dap_chain_t * l_chain; if(!l_net) return NULL; DL_FOREACH(l_net->pub.chains, l_chain) { for(uint16_t i = 0; i < l_chain->datum_types_count; i++) { if(l_chain->datum_types[i] == a_datum_type) return dap_chain_net_get_gdb_group_mempool(l_chain); } } return NULL; } /** * @brief dap_chain_net_get_cur_addr * @param l_net * @return */ dap_chain_node_addr_t * dap_chain_net_get_cur_addr( dap_chain_net_t * l_net) { return l_net ? (PVT(l_net)->node_info ? &PVT(l_net)->node_info->hdr.address : PVT(l_net)->node_addr) : NULL; } uint64_t dap_chain_net_get_cur_addr_int(dap_chain_net_t * l_net) { return dap_chain_net_get_cur_addr(l_net) ? dap_chain_net_get_cur_addr(l_net)->uint64 : dap_db_get_cur_node_addr(l_net->pub.name); } dap_chain_cell_id_t * dap_chain_net_get_cur_cell( dap_chain_net_t * l_net) { return PVT(l_net)->node_info ? &PVT(l_net)->node_info->hdr.cell_id: 0; } /** * Get nodes list (list of dap_chain_node_addr_t struct) */ dap_list_t* dap_chain_net_get_link_node_list(dap_chain_net_t * l_net, bool a_is_only_cur_cell) { dap_list_t *l_node_list = NULL; // get cur node address dap_chain_node_addr_t l_cur_node_addr = { 0 }; l_cur_node_addr.uint64 = dap_chain_net_get_cur_addr_int(l_net); dap_chain_node_info_t *l_cur_node_info = dap_chain_node_info_read(l_net, &l_cur_node_addr); // add links to nodes list only from the same cell if(l_cur_node_info) { for(unsigned int i = 0; i < l_cur_node_info->hdr.links_number; i++) { bool l_is_add = true; dap_chain_node_addr_t *l_remote_address = l_cur_node_info->links + i; if(a_is_only_cur_cell) { // get remote node list dap_chain_node_info_t *l_remote_node_info = dap_chain_node_info_read(l_net, l_remote_address); if(!l_remote_node_info || l_remote_node_info->hdr.cell_id.uint64 != l_cur_node_info->hdr.cell_id.uint64) l_is_add = false; DAP_DELETE(l_remote_node_info); } if(l_is_add) { dap_chain_node_addr_t *l_address = DAP_NEW(dap_chain_node_addr_t); l_address->uint64 = l_cur_node_info->links[i].uint64; l_node_list = dap_list_append(l_node_list, l_address); } } } DAP_DELETE(l_cur_node_info); return l_node_list; } /** * Get remote nodes list (list of dap_chain_node_addr_t struct) */ dap_list_t* dap_chain_net_get_node_list(dap_chain_net_t * l_net) { dap_list_t *l_node_list = NULL; /* dap_chain_net_pvt_t *l_net_pvt = PVT(l_net); // get nodes from seed_nodes for(uint16_t i = 0; i < l_net_pvt->seed_aliases_count; i++) { dap_chain_node_addr_t *l_node_address = dap_chain_node_alias_find(l_net, l_net_pvt->seed_aliases[i]); l_node_list = dap_list_append(l_node_list, l_node_address); }*/ // get nodes list from global_db dap_global_db_obj_t *l_objs = NULL; size_t l_nodes_count = 0; // read all node l_objs = dap_chain_global_db_gr_load(l_net->pub.gdb_nodes, &l_nodes_count); if(!l_nodes_count || !l_objs) return l_node_list; for(size_t i = 0; i < l_nodes_count; i++) { dap_chain_node_info_t *l_node_info = (dap_chain_node_info_t *) l_objs[i].value; dap_chain_node_addr_t *l_address = DAP_NEW(dap_chain_node_addr_t); l_address->uint64 = l_node_info->hdr.address.uint64; l_node_list = dap_list_append(l_node_list, l_address); } dap_chain_global_db_objs_delete(l_objs, l_nodes_count); return l_node_list; } /** * @brief dap_chain_net_set_flag_sync_from_zero * @param a_net * @param a_flag_sync_from_zero */ void dap_chain_net_set_flag_sync_from_zero( dap_chain_net_t * a_net, bool a_flag_sync_from_zero) { if( a_flag_sync_from_zero) PVT(a_net)->flags |= F_DAP_CHAIN_NET_SYNC_FROM_ZERO; else PVT(a_net)->flags ^= F_DAP_CHAIN_NET_SYNC_FROM_ZERO; } /** * @brief dap_chain_net_get_flag_sync_from_zero * @param a_net * @return */ bool dap_chain_net_get_flag_sync_from_zero( dap_chain_net_t * a_net) { return PVT(a_net)->flags &F_DAP_CHAIN_NET_SYNC_FROM_ZERO ; } /** * @brief dap_chain_net_proc_datapool * @param a_net */ void dap_chain_net_proc_mempool (dap_chain_net_t * a_net) { dap_string_t * l_str_tmp = dap_string_new(NULL); dap_chain_t *l_chain; DL_FOREACH(a_net->pub.chains, l_chain) { char *l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool(l_chain); size_t l_objs_size = 0; dap_global_db_obj_t * l_objs = dap_chain_global_db_gr_load(l_gdb_group_mempool, &l_objs_size); if(l_objs_size) { log_it(L_INFO, "%s.%s: Found %u records :", a_net->pub.name, l_chain->name, l_objs_size); size_t l_datums_size = l_objs_size; dap_chain_datum_t ** l_datums = DAP_NEW_Z_SIZE(dap_chain_datum_t*, sizeof(dap_chain_datum_t*) * l_datums_size); size_t l_objs_size_tmp = (l_objs_size > 15) ? min(l_objs_size, 10) : 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; int l_verify_datum= dap_chain_net_verify_datum_for_add( a_net, l_datum) ; if (l_verify_datum != 0){ log_it(L_WARNING, "Datum doesn't pass verifications (code %d), delete such datum from pool", l_verify_datum); dap_chain_global_db_gr_del(dap_strdup(l_objs[i].key), l_gdb_group_mempool); l_datums[i] = NULL; }else{ l_datums[i] = l_datum; if(i < l_objs_size_tmp) { char buf[50] = { '\0' }; const char *l_type = NULL; DAP_DATUM_TYPE_STR(l_datum->header.type_id, l_type) time_t l_ts_create = (time_t) l_datum->header.ts_create; log_it(L_INFO, "\t\t0x%s: type_id=%s ts_create=%s data_size=%u", l_objs[i].key, l_type, dap_ctime_r(&l_ts_create, buf), l_datum->header.data_size); } } } size_t l_objs_processed = l_chain->callback_add_datums(l_chain, l_datums, l_datums_size); // Delete processed objects size_t l_objs_processed_tmp = (l_objs_processed > 15) ? min(l_objs_processed, 10) : l_objs_processed; for(size_t i = 0; i < l_objs_processed; i++) { dap_chain_global_db_gr_del(dap_strdup(l_objs[i].key), l_gdb_group_mempool); if(i < l_objs_processed_tmp) { dap_string_append_printf(l_str_tmp, "New event created, removed datum 0x%s from mempool \n", l_objs[i].key); } } if(l_objs_processed < l_datums_size) log_it(L_WARNING, "%s.%s: %d records not processed", a_net->pub.name, l_chain->name, l_datums_size - l_objs_processed); dap_chain_global_db_objs_delete(l_objs, l_objs_size); // Cleanup datums array if(l_datums){ for(size_t i = 0; i < l_objs_size; i++) { if (l_datums[i]) DAP_DELETE(l_datums[i]); } DAP_DEL_Z(l_datums); } } else { log_it(L_INFO, "%s.%s: No records in mempool", a_net->pub.name, l_chain ? l_chain->name : "[no chain]"); } DAP_DELETE(l_gdb_group_mempool); } } /** * @brief dap_chain_net_tx_get_by_hash * @param a_net * @param a_tx_hash * @param a_search_type * @return */ dap_chain_datum_tx_t * dap_chain_net_get_tx_by_hash(dap_chain_net_t * a_net, dap_chain_hash_fast_t * a_tx_hash, dap_chain_net_tx_search_type_t a_search_type) { dap_ledger_t * l_ledger = dap_chain_ledger_by_net_name( a_net->pub.name ); dap_chain_datum_tx_t * l_tx = NULL; switch (a_search_type) { case TX_SEARCH_TYPE_NET: case TX_SEARCH_TYPE_CELL: case TX_SEARCH_TYPE_LOCAL:{ if ( ! l_tx ){ // pass all chains for ( dap_chain_t * l_chain = a_net->pub.chains; l_chain; l_chain = l_chain->next){ if ( l_chain->callback_tx_find_by_hash ){ // try to find transaction in chain ( inside shard ) l_tx = l_chain->callback_tx_find_by_hash( l_chain, a_tx_hash ); if (l_tx) break; } } } }break; case TX_SEARCH_TYPE_NET_UNSPENT: case TX_SEARCH_TYPE_CELL_UNSPENT:{ l_tx = dap_chain_ledger_tx_find_by_hash( l_ledger, a_tx_hash ); }break; } return l_tx; } /** * @brief dap_chain_net_get_add_gdb_group * @param a_net * @param a_node_addr * @return */ dap_list_t * dap_chain_net_get_add_gdb_group(dap_chain_net_t * a_net, dap_chain_node_addr_t a_node_addr) { dap_list_t *l_list_groups = NULL; if(!a_net || !PVT(a_net) || !PVT(a_net)->gdb_sync_nodes_addrs) return NULL; for(uint16_t i = 0; i < PVT(a_net)->gdb_sync_nodes_addrs_count; i++) { if(a_node_addr.uint64 == PVT(a_net)->gdb_sync_nodes_addrs[i].uint64) { for(uint16_t j = 0; j < PVT(a_net)->gdb_sync_groups_count; j++) l_list_groups = dap_list_append(l_list_groups, PVT(a_net)->gdb_sync_groups[j]); } } return l_list_groups; } /** * @brief dap_chain_net_verify_datum_for_add * @param a_net * @param a_datum * @return */ int dap_chain_net_verify_datum_for_add(dap_chain_net_t *a_net, dap_chain_datum_t * a_datum ) { if( ! a_datum) return -10; if( ! a_net ) return -11; switch ( a_datum->header.type_id) { case DAP_CHAIN_DATUM_TX: return dap_chain_ledger_tx_add_check( a_net->pub.ledger, (dap_chain_datum_tx_t*) a_datum->data ); case DAP_CHAIN_DATUM_TOKEN_DECL: return dap_chain_ledger_token_decl_add_check( a_net->pub.ledger, (dap_chain_datum_token_t*) a_datum->data ); case DAP_CHAIN_DATUM_TOKEN_EMISSION : return dap_chain_ledger_token_emission_add_check( a_net->pub.ledger, (dap_chain_datum_token_emission_t*) a_datum->data, a_datum->header.data_size ); default: return 0; } } /** * @brief dap_chain_net_dump_datum * @param a_str_out * @param a_datum */ void dap_chain_net_dump_datum(dap_string_t * a_str_out, dap_chain_datum_t * a_datum, const char *a_hash_out_type) { if( a_datum == NULL){ dap_string_append_printf(a_str_out,"==Datum is NULL\n"); return; } switch (a_datum->header.type_id){ case DAP_CHAIN_DATUM_TOKEN_DECL:{ dap_chain_datum_token_t * l_token = (dap_chain_datum_token_t*) a_datum->data; size_t l_token_size = a_datum->header.data_size; if(l_token_size < sizeof(dap_chain_datum_token_t)){ dap_string_append_printf(a_str_out,"==Datum has incorrect size. Only %lu, while at least %lu is expected\n", l_token_size, sizeof(dap_chain_datum_token_t)); return; } dap_string_append_printf(a_str_out,"==Datum Token Declaration\n"); dap_string_append_printf(a_str_out, "ticker: %s\n", l_token->ticker); dap_string_append_printf(a_str_out, "size: %zd\n", l_token_size); dap_hash_fast_t l_hash ={0}; switch (l_token->type) { case DAP_CHAIN_DATUM_TOKEN_TYPE_SIMPLE:{ dap_string_append_printf(a_str_out, "type: SIMPLE\n"); dap_string_append_printf(a_str_out, "sign_total: %u\n", l_token->header_private.signs_total ); dap_string_append_printf(a_str_out, "sign_valid: %u\n", l_token->header_private.signs_valid ); dap_string_append_printf(a_str_out, "total_supply: %u\n", l_token->header_private.total_supply ); }break; case DAP_CHAIN_DATUM_TOKEN_TYPE_PRIVATE_UPDATE:{ dap_string_append_printf(a_str_out,"type: PRIVATE_UPDATE\n"); dap_chain_datum_token_tsd_t * l_tsd = dap_chain_datum_token_tsd_get(l_token, l_token_size); if (l_tsd == NULL) dap_string_append_printf(a_str_out,"<CORRUPTED TSD SECTION>\n"); else{ size_t l_offset = 0; size_t l_offset_max = l_token->header_private_decl.tsd_total_size; while( l_offset< l_offset_max){ if ( (l_tsd->size+l_offset) >l_offset_max){ log_it(L_WARNING, "<CORRUPTED TSD> too big size %zd when left maximum %zd", l_tsd->size, l_offset_max - l_offset); return; } switch( l_tsd->type){ case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_SET_FLAGS: dap_string_append_printf(a_str_out,"flags_set: "); dap_chain_datum_token_flags_dump(a_str_out, dap_chain_datum_token_tsd_get_scalar(l_tsd, uint16_t)); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_UNSET_FLAGS: dap_string_append_printf(a_str_out,"flags_unset: "); dap_chain_datum_token_flags_dump(a_str_out, dap_chain_datum_token_tsd_get_scalar(l_tsd, uint16_t)); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SUPPLY: dap_string_append_printf(a_str_out,"total_supply: %u\n", dap_chain_datum_token_tsd_get_scalar(l_tsd, uint128_t) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_VALID : dap_string_append_printf(a_str_out,"total_signs_valid: %u\n", dap_chain_datum_token_tsd_get_scalar(l_tsd, uint16_t) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_ADD : if(l_tsd->size == sizeof(dap_chain_hash_fast_t) ){ char *l_hash_str; if(!dap_strcmp(a_hash_out_type, "hex") || !dap_strcmp(a_hash_out_type, "content_hash") ) l_hash_str = dap_chain_hash_fast_to_str_new((dap_chain_hash_fast_t*) l_tsd->data); else l_hash_str = dap_enc_base58_encode_hash_to_str((dap_chain_hash_fast_t*) l_tsd->data); dap_string_append_printf(a_str_out,"total_signs_add: %s\n", l_hash_str ); DAP_DELETE( l_hash_str ); }else dap_string_append_printf(a_str_out,"total_signs_add: <WRONG SIZE %zd>\n", l_tsd->size); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_REMOVE : if(l_tsd->size == sizeof(dap_chain_hash_fast_t) ){ char *l_hash_str; if(!dap_strcmp(a_hash_out_type,"hex")|| !dap_strcmp(a_hash_out_type, "content_hash")) l_hash_str = dap_chain_hash_fast_to_str_new((dap_chain_hash_fast_t*) l_tsd->data); else l_hash_str = dap_enc_base58_encode_hash_to_str((dap_chain_hash_fast_t*) l_tsd->data); dap_string_append_printf(a_str_out,"total_signs_remove: %s\n", l_hash_str ); DAP_DELETE( l_hash_str ); }else dap_string_append_printf(a_str_out,"total_signs_add: <WRONG SIZE %zd>\n", l_tsd->size); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DATUM_TYPE_ALLOWED_ADD : dap_string_append_printf(a_str_out,"datum_type_allowed_add: %s\n", dap_chain_datum_token_tsd_get_string_const(l_tsd) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DATUM_TYPE_ALLOWED_REMOVE : dap_string_append_printf(a_str_out,"datum_type_allowed_remove: %s\n", dap_chain_datum_token_tsd_get_string_const(l_tsd) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DATUM_TYPE_BLOCKED_ADD : dap_string_append_printf(a_str_out,"datum_type_blocked_add: %s\n", dap_chain_datum_token_tsd_get_string_const(l_tsd) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DATUM_TYPE_BLOCKED_REMOVE: dap_string_append_printf(a_str_out,"datum_type_blocked_remove: %s\n", dap_chain_datum_token_tsd_get_string_const(l_tsd) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_ALLOWED_ADD: dap_string_append_printf(a_str_out,"tx_sender_allowed_add: %s\n", dap_chain_datum_token_tsd_get_string_const(l_tsd) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_ALLOWED_REMOVE: dap_string_append_printf(a_str_out,"tx_sender_allowed_remove: %s\n", dap_chain_datum_token_tsd_get_string_const(l_tsd) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_BLOCKED_ADD: dap_string_append_printf(a_str_out,"tx_sender_blocked_add: %s\n", dap_chain_datum_token_tsd_get_string_const(l_tsd) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_BLOCKED_REMOVE: dap_string_append_printf(a_str_out,"tx_sender_blocked_remove: %s\n", dap_chain_datum_token_tsd_get_string_const(l_tsd) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_ALLOWED_ADD: dap_string_append_printf(a_str_out,"tx_receiver_allowed_add: %s\n", dap_chain_datum_token_tsd_get_string_const(l_tsd) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_ALLOWED_REMOVE: dap_string_append_printf(a_str_out,"tx_receiver_allowed: %s\n", dap_chain_datum_token_tsd_get_string_const(l_tsd) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_BLOCKED_ADD: dap_string_append_printf(a_str_out, "tx_receiver_blocked_add: %s\n", dap_chain_datum_token_tsd_get_string_const(l_tsd) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_BLOCKED_REMOVE: dap_string_append_printf(a_str_out, "tx_receiver_blocked_remove: %s\n", dap_chain_datum_token_tsd_get_string_const(l_tsd) ); break; default: dap_string_append_printf(a_str_out, "<0x%04X>: <size %zd>\n", l_tsd->type, l_tsd->size); } l_offset += dap_chain_datum_token_tsd_size(l_tsd); } } }break; case DAP_CHAIN_DATUM_TOKEN_TYPE_PRIVATE_DECL:{ dap_string_append_printf(a_str_out,"type: PRIVATE_DECL\n"); dap_string_append_printf(a_str_out,"flags: "); dap_chain_datum_token_flags_dump(a_str_out, l_token->header_private_decl.flags); dap_chain_datum_token_tsd_t * l_tsd_first = dap_chain_datum_token_tsd_get(l_token, l_token_size); if (l_tsd_first == NULL) dap_string_append_printf(a_str_out,"<CORRUPTED TSD SECTION>\n"); else{ size_t l_offset = 0; size_t l_offset_max = l_token->header_private_decl.tsd_total_size; while( l_offset< l_offset_max){ dap_chain_datum_token_tsd_t * l_tsd = (void*)l_tsd_first + l_offset; if ( (l_tsd->size+l_offset) >l_offset_max){ log_it(L_WARNING, "<CORRUPTED TSD> too big size %zd when left maximum %zd", l_tsd->size, l_offset_max - l_offset); return; } switch( l_tsd->type){ case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SUPPLY: dap_string_append_printf(a_str_out,"total_supply: %lu\n", dap_chain_datum_token_tsd_get_scalar(l_tsd, uint128_t) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_VALID : dap_string_append_printf(a_str_out,"total_signs_valid: %u\n", dap_chain_datum_token_tsd_get_scalar(l_tsd, uint16_t) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DATUM_TYPE_ALLOWED_ADD : dap_string_append_printf(a_str_out,"datum_type_allowed: %s\n", dap_chain_datum_token_tsd_get_string_const(l_tsd) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DATUM_TYPE_BLOCKED_ADD : dap_string_append_printf(a_str_out,"datum_type_blocked: %s\n", dap_chain_datum_token_tsd_get_string_const(l_tsd) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_ALLOWED_ADD: dap_string_append_printf(a_str_out,"tx_sender_allowed: %s\n", dap_chain_datum_token_tsd_get_string_const(l_tsd) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_BLOCKED_ADD: dap_string_append_printf(a_str_out,"tx_sender_blocked: %s\n", dap_chain_datum_token_tsd_get_string_const(l_tsd) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_ALLOWED_ADD: dap_string_append_printf(a_str_out,"tx_receiver_allowed: %s\n", dap_chain_datum_token_tsd_get_string_const(l_tsd) ); break; case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_BLOCKED_ADD: dap_string_append_printf(a_str_out, "tx_receiver_blocked: %s\n", dap_chain_datum_token_tsd_get_string_const(l_tsd) ); break; default: dap_string_append_printf(a_str_out, "<0x%04X>: <size %zd>\n", l_tsd->type, l_tsd->size); } l_offset += dap_chain_datum_token_tsd_size(l_tsd); } } size_t l_certs_field_size = l_token_size - sizeof(*l_token) - l_token->header_private_decl.tsd_total_size; dap_chain_datum_token_certs_dump(a_str_out, l_token->data_n_tsd, l_certs_field_size); }break; case DAP_CHAIN_DATUM_TX:{ dap_chain_datum_tx_t * l_tx =(dap_chain_datum_tx_t *) a_datum->data; char buf[50]; time_t l_ts_created = l_tx->header.ts_created; dap_string_append_printf(a_str_out,"type: TX\n"); dap_string_append_printf(a_str_out,"type: ts_created: %s \n", dap_ctime_r(&l_ts_created, buf)); int l_items_count = -1; dap_list_t * l_items = dap_chain_datum_tx_items_get(l_tx,TX_ITEM_TYPE_ANY,&l_items_count); dap_string_append_printf(a_str_out,"type: items_count: %d \n", l_items_count ); if (l_items_count>0){ size_t n=0; for( dap_list_t * l_cur = l_items; l_cur; l_cur = l_cur->next ){ dap_string_append_printf(a_str_out,"Item #%zd\n",n); byte_t *l_tx_item = l_cur->data; dap_chain_tx_item_type_t l_item_type = dap_chain_datum_tx_item_get_type(l_tx_item); dap_string_append_printf(a_str_out,"\ttype: %s \n", dap_chain_datum_tx_item_type_to_str (l_item_type) ); switch (l_item_type) { case TX_ITEM_TYPE_IN:{ dap_chain_tx_in_t * l_in = l_cur->data; dap_string_append_printf(a_str_out,"\ttx_out_prev_idx: %u\n", l_in->header.tx_out_prev_idx ); dap_string_append_printf(a_str_out,"\ttx_out_prev_idx : %u\n", l_in->header.tx_prev_hash ); char l_tx_prev_hash_str[70]={[0]='\0'}; dap_hash_fast_to_str(&l_in->header.tx_prev_hash, l_tx_prev_hash_str,sizeof (l_tx_prev_hash_str)-1); dap_string_append_printf(a_str_out,"\ttx_prev_hash : %s\n", l_tx_prev_hash_str ); } break; case TX_ITEM_TYPE_OUT:{ dap_chain_tx_out_t * l_out = l_cur->data; dap_string_append_printf(a_str_out,"\tvalue: %u\n", l_out->header.value ); char * l_addr_str = dap_chain_addr_to_str(&l_out->addr); dap_string_append_printf(a_str_out,"\taddr : %s\n", l_addr_str ); DAP_DELETE(l_addr_str); } break; case TX_ITEM_TYPE_OUT_EXT:{ dap_chain_tx_out_ext_t * l_out_ext = l_cur->data; dap_string_append_printf(a_str_out,"\tvalue: %u\n", l_out_ext->header.value ); char * l_addr_str = dap_chain_addr_to_str(&l_out_ext->addr); dap_string_append_printf(a_str_out,"\taddr : %s\n", l_addr_str ); dap_string_append_printf(a_str_out,"\ttoken : %s\n", l_out_ext->token ); DAP_DELETE(l_addr_str); } break; case TX_ITEM_TYPE_SIG:{ dap_chain_tx_sig_t * l_item_sign = l_cur->data; dap_sign_t *l_sign = (dap_sign_t *)l_item_sign->sig; dap_hash_fast_t l_sign_hash; char l_sign_hash_str[70]={[0]='\0'}; dap_string_append_printf(a_str_out,"\tsig_size: %u\n", l_item_sign->header.sig_size ); dap_string_append_printf(a_str_out,"\ttype: %s\n", dap_sign_type_to_str(l_sign->header.type) ); dap_sign_get_pkey_hash(l_sign,&l_sign_hash); dap_hash_fast_to_str(&l_sign_hash,l_sign_hash_str,sizeof (l_sign_hash_str)-1); dap_string_append_printf(a_str_out,"\tpkey_hash: %s\n", l_sign_hash_str ); } break; case TX_ITEM_TYPE_TOKEN:{ dap_chain_tx_token_t * l_token = l_cur->data; dap_string_append_printf(a_str_out,"\tticker: %s\n", l_token->header.ticker ); dap_string_append_printf(a_str_out,"\ttoken_emission_chain: 0x%016x\n", l_token->header.token_emission_chain_id ); char l_token_emission_hash_str[70]={ [0]='\0'}; dap_chain_hash_fast_to_str(& l_token->header.token_emission_hash,l_token_emission_hash_str, sizeof (l_token_emission_hash_str)-1); dap_string_append_printf(a_str_out,"\ttoken_emission_hash: %s", l_token_emission_hash_str ); } break; case TX_ITEM_TYPE_TOKEN_EXT:{ dap_chain_tx_token_ext_t * l_token = l_cur->data; dap_string_append_printf(a_str_out,"\tversion: %u\n",l_token->header.version ); dap_string_append_printf(a_str_out,"\tticker: %s\n", l_token->header.ticker ); dap_string_append_printf(a_str_out,"\text_net: 0x%016x\n",l_token->header.ext_net_id ); dap_string_append_printf(a_str_out,"\text_chain: 0x%016x\n",l_token->header.ext_chain_id ); dap_string_append_printf(a_str_out,"\text_tx_out_idx: %u\n",l_token->header.ext_tx_out_idx ); char l_token_emission_hash_str[70]={ [0]='\0'}; dap_chain_hash_fast_to_str(& l_token->header.ext_tx_hash,l_token_emission_hash_str, sizeof (l_token_emission_hash_str)-1); dap_string_append_printf(a_str_out,"\text_tx_hash: %s", l_token_emission_hash_str ); } break; case TX_ITEM_TYPE_IN_COND:{ dap_chain_tx_in_cond_t * l_in = l_cur->data; dap_string_append_printf(a_str_out,"\ttx_out_prev_idx: %u\n", l_in->header.tx_out_prev_idx ); dap_string_append_printf(a_str_out,"\ttx_out_prev_idx : %u\n", l_in->header.tx_prev_hash ); dap_string_append_printf(a_str_out,"\treceipt_idx : %u\n", l_in->header.receipt_idx ); char l_tx_prev_hash_str[70]={[0]='\0'}; dap_hash_fast_to_str(&l_in->header.tx_prev_hash, l_tx_prev_hash_str,sizeof (l_tx_prev_hash_str)-1); dap_string_append_printf(a_str_out,"\ttx_prev_hash : %s\n", l_tx_prev_hash_str ); } break; case TX_ITEM_TYPE_OUT_COND:{ dap_chain_tx_out_cond_t * l_out = l_cur->data; dap_string_append_printf(a_str_out,"\tvalue: %u\n", l_out->header.value ); switch ( l_out->header.subtype){ case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY:{ dap_string_append_printf(a_str_out,"\tsubtype: DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY\n"); dap_string_append_printf(a_str_out,"\tsrv_uid: 0x%016x\n", l_out->subtype.srv_pay.srv_uid.uint64 ); switch (l_out->subtype.srv_pay.unit.enm) { case SERV_UNIT_UNDEFINED: dap_string_append_printf(a_str_out,"\tunit: SERV_UNIT_UNDEFINED\n"); break; case SERV_UNIT_MB: dap_string_append_printf(a_str_out,"\tunit: SERV_UNIT_MB\n"); break; case SERV_UNIT_SEC: dap_string_append_printf(a_str_out,"\tunit: SERV_UNIT_SEC\n"); break; case SERV_UNIT_DAY: dap_string_append_printf(a_str_out,"\tunit: SERV_UNIT_DAY\n"); break; case SERV_UNIT_KB: dap_string_append_printf(a_str_out,"\tunit: SERV_UNIT_KB\n"); break; case SERV_UNIT_B : dap_string_append_printf(a_str_out,"\tunit: SERV_UNIT_B\n"); break; default: dap_string_append_printf(a_str_out,"\tunit: SERV_UNIT_UNKNOWN\n"); break; } dap_string_append_printf(a_str_out,"\tunit_price_max: %llu\n", l_out->subtype.srv_pay.unit_price_max_datoshi); char l_pkey_hash_str[70]={[0]='\0'}; dap_chain_hash_fast_to_str(&l_out->subtype.srv_pay.pkey_hash, l_pkey_hash_str, sizeof (l_pkey_hash_str)-1); dap_string_append_printf(a_str_out,"\tpkey_hash: %s\n", l_pkey_hash_str ); }break; case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE:{ dap_string_append_printf(a_str_out,"\tsubtype: DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE\n"); }break; case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE:{ dap_string_append_printf(a_str_out,"\tsubtype: DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE\n"); }break; } dap_string_append_printf(a_str_out,"\tparams_size : %u\n", l_out->params_size ); } break; case TX_ITEM_TYPE_RECEIPT:{} break; default:{} } n++; } } dap_list_free(l_items); }break; case DAP_CHAIN_DATUM_TOKEN_TYPE_PUBLIC:{ dap_string_append_printf(a_str_out,"type: PUBLIC\n"); }break; default: dap_string_append_printf(a_str_out,"type: UNKNOWN\n"); } }break; } } static bool s_net_check_acl(dap_chain_net_t *a_net, dap_chain_hash_fast_t *a_pkey_hash) { const char l_path[] = "network/"; char l_cfg_path[strlen(a_net->pub.name) + strlen(l_path) + 1]; strcpy(l_cfg_path, l_path); strcat(l_cfg_path, a_net->pub.name); dap_config_t *l_cfg = dap_config_open(l_cfg_path); const char *l_auth_type = dap_config_get_item_str(l_cfg, "auth", "type"); bool l_authorized = true; if (l_auth_type && !strcmp(l_auth_type, "ca")) { if (dap_hash_fast_is_blank(a_pkey_hash)) { return false; } l_authorized = false; const char *l_auth_hash_str = dap_chain_hash_fast_to_str_new(a_pkey_hash); uint16_t l_acl_list_len = 0; char **l_acl_list = dap_config_get_array_str(l_cfg, "auth", "acl_accept_ca_list", &l_acl_list_len); for (uint16_t i = 0; i < l_acl_list_len; i++) { if (!strcmp(l_acl_list[i], l_auth_hash_str)) { l_authorized = true; break; } } if (!l_authorized) { const char *l_acl_gdb = dap_config_get_item_str(l_cfg, "auth", "acl_accept_ca_gdb"); if (l_acl_gdb) { size_t l_objs_count; dap_global_db_obj_t *l_objs = dap_chain_global_db_gr_load(l_acl_gdb, &l_objs_count); for (size_t i = 0; i < l_objs_count; i++) { if (!strcmp(l_objs[i].key, l_auth_hash_str)) { l_authorized = true; break; } } dap_chain_global_db_objs_delete(l_objs, l_objs_count); } } if (!l_authorized) { const char *l_acl_chains = dap_config_get_item_str(l_cfg, "auth", "acl_accept_ca_chains"); if (l_acl_chains && !strcmp(l_acl_chains, "all")) { dap_list_t *l_certs = dap_cert_get_all_mem(); for (dap_list_t *l_tmp = l_certs; l_tmp; l_tmp = dap_list_next(l_tmp)) { dap_cert_t *l_cert = (dap_cert_t *)l_tmp->data; size_t l_pkey_size; uint8_t *l_pkey_ser = dap_enc_key_serealize_pub_key(l_cert->enc_key, &l_pkey_size); dap_chain_hash_fast_t l_cert_hash; dap_hash_fast(l_pkey_ser, l_pkey_size, &l_cert_hash); if (!memcmp(l_pkey_ser, a_pkey_hash, sizeof(dap_chain_hash_fast_t))) { l_authorized = true; DAP_DELETE(l_pkey_ser); break; } DAP_DELETE(l_pkey_ser); } } } } return l_authorized; } static uint8_t *dap_chain_net_set_acl(dap_chain_hash_fast_t *a_pkey_hash) { uint16_t l_net_count; dap_chain_net_t **l_net_list = dap_chain_net_list(&l_net_count); if (l_net_count && l_net_list) { uint8_t *l_ret = DAP_NEW_SIZE(uint8_t, l_net_count); for (uint16_t i = 0; i < l_net_count; i++) { l_ret[i] = s_net_check_acl(l_net_list[i], a_pkey_hash); } DAP_DELETE(l_net_list); return l_ret; } return NULL; }