Skip to content
Snippets Groups Projects
dap_chain_net_node_list.c 14.45 KiB
/*
* Authors:
* Dmitriy Gerasimov <naeper@demlabs.net>
* Roman Padenkov <roman.padenkov@demlabs.net>
* Cellframe       https://cellframe.net
* DeM Labs Inc.   https://demlabs.net
* Copyright  (c) 2017-2023
* All rights reserved.

This file is part of CellFrame SDK the open source project

CellFrame SDK 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.

CellFrame SDK 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 CellFrame SDK based project.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "dap_chain_net_node_list.h"
#include "http_status_code.h"
#include "dap_chain_net_balancer.h"
#include "dap_client.h"
#include "dap_client_http.h"

#define LOG_TAG "dap_chain_net_node_list"

enum RetCode {
    ADD_OK = 1,
    ERR_NO_SERVER,
    ERR_NOT_ADDED,
    ERR_HASH,
    ERR_HANDSHAKE,
    ERR_EXISTS,
    ERR_NOT_PINNER,
    DELETED_OK,
    ERR_WAIT_TIMEOUT,
    ERR_UNKNOWN
};

static int s_dap_chain_net_node_list_add(dap_chain_net_t *a_net, dap_chain_node_info_t *a_node_info) {
    return !dap_chain_node_info_save(a_net, a_node_info)
        ? ( log_it( L_DEBUG, "Add address " NODE_ADDR_FP_STR " '%s : %u' to nodelist",
                 NODE_ADDR_FP_ARGS_S(a_node_info->address),
                 a_node_info->ext_host, a_node_info->ext_port ), ADD_OK )
        : ( log_it( L_ERROR, "Address " NODE_ADDR_FP_STR " '%s : %u' not added",
                 NODE_ADDR_FP_ARGS_S(a_node_info->address),
                 a_node_info->ext_host, a_node_info->ext_port ), ERR_NOT_ADDED );
}

static int s_dap_chain_net_node_list_del(dap_chain_net_t *a_net, dap_chain_node_info_t *a_node_info) {
    return !dap_chain_node_info_del(a_net, a_node_info)
        ? ( log_it( L_DEBUG, "Delete address" NODE_ADDR_FP_STR " '%s : %u' from nodelist",
                 NODE_ADDR_FP_ARGS_S(a_node_info->address),
                 a_node_info->ext_host, a_node_info->ext_port ), DELETED_OK )
        : ( log_it( L_ERROR, "Address" NODE_ADDR_FP_STR " '%s : %u' not deleted",
                 NODE_ADDR_FP_ARGS_S(a_node_info->address),
                 a_node_info->ext_host, a_node_info->ext_port ), ERR_UNKNOWN );
}

/**
 * @brief server function, makes handshake and add node to node list
 *
 * @param dap_http_simple_t *a_http_simple, void *a_arg
 * @return void
 * send value
 * 1 - Node addr successfully added to node list
 * 2 - Can't add this addres to node list
 * 3 - Can't calculate hash for addr
 * 4 - Can't do handshake
 * 5 - Already exists
 * 6 - I'am not the pinner this node (only for update links count)
 * 7 - Node deleted
 */
void dap_chain_net_node_check_http_issue_link(dap_http_simple_t *a_http_simple, void *a_arg)
{
    log_it(L_DEBUG,"Proc enc http request");
    http_status_code_t *l_return_code = (http_status_code_t*)a_arg;    
    if ( strcmp(a_http_simple->http_client->url_path, DAP_NODE_LIST_URI_HASH) ) {
        log_it(L_ERROR, "Wrong path '%s' in the request to dap_chain_net_node_list module",
                        a_http_simple->http_client->url_path);
        *l_return_code = Http_Status_BadRequest;
        return;
    }
    int l_protocol_version = 0;
    char l_issue_method = 0;
    uint64_t addr = 0;
    uint16_t port = 0;
    const char l_net_token[] = "net=";
    if ( 4 != sscanf(a_http_simple->http_client->in_query_string, "version=%d,method=%c,addr=%zu,port=%hu,net=",
                                                                  &l_protocol_version, &l_issue_method, &addr, &port) )
    {
        log_it( L_ERROR, "Bad request \"%s\"", a_http_simple->http_client->in_query_string );
        *l_return_code = Http_Status_BadRequest;
        return;
    }
    if (l_protocol_version != 1) {
        log_it(L_ERROR, "Unsupported protocol version/method in the request to dap_chain_net_node_list module");
        *l_return_code = Http_Status_MethodNotAllowed;
        return;
    }
    const char *l_key = dap_stream_node_addr_to_str_static( (dap_chain_node_addr_t){.uint64 = addr} );
    if (!l_key) {
        log_it(L_ERROR, "Bad node address %zu", addr);
        *l_return_code = Http_Status_BadRequest;
        return;
    }
    char *l_net_str = strstr(a_http_simple->http_client->in_query_string, l_net_token);
    if (!l_net_str) {
        log_it(L_ERROR, "Net name token not found in the request to dap_chain_net_node_list module");
        *l_return_code = Http_Status_NotFound;
        return;
    }
    l_net_str += strlen(l_net_token);
    log_it(L_DEBUG, "HTTP Node check parser retrieve netname %s", l_net_str);
    dap_chain_node_info_t *l_node_info;
    dap_chain_net_t *l_net = dap_chain_net_by_name(l_net_str);
    uint8_t l_response = ERR_UNKNOWN;
    switch (l_issue_method) {
    case 'a': {
        uint8_t l_host_size = dap_min(INET6_ADDRSTRLEN, (int)dap_strlen(a_http_simple->es_hostaddr) + 1);
        l_node_info = DAP_NEW_STACK_SIZE(dap_chain_node_info_t, sizeof(dap_chain_node_info_t) + l_host_size);
        *l_node_info = (dap_chain_node_info_t) {
            .address.uint64 = addr,
            .ext_port = port,
            .ext_host_len = dap_strncpy(l_node_info->ext_host, a_http_simple->es_hostaddr, l_host_size) - l_node_info->ext_host
        };
        l_response = !dap_chain_net_balancer_handshake(l_node_info, l_net)
            ? s_dap_chain_net_node_list_add(l_net, l_node_info)
            : ( log_it(L_DEBUG, "Can't do handshake with %s [ %s : %u ]", l_key, l_node_info->ext_host, l_node_info->ext_port),
            ERR_HANDSHAKE );
        *l_return_code = Http_Status_OK;
    } break;
    case 'r': {
        if ( !(l_node_info = (dap_chain_node_info_t*)dap_global_db_get_sync(l_net->pub.gdb_nodes, l_key, NULL, NULL, NULL)) ) {
            log_it(L_DEBUG,"Address %s is not present in nodelist", l_key);
            l_response = ERR_NOT_ADDED;
        } else {
            if ( dap_strcmp(l_node_info->ext_host, a_http_simple->es_hostaddr) ) {
                l_response = ERR_NOT_PINNER;
                *l_return_code = Http_Status_Forbidden;
            } else {
                l_response = !dap_global_db_del_sync(l_net->pub.gdb_nodes, l_key)
                    ? ( log_it(L_DEBUG, "Node %s successfully deleted from nodelist", l_key),
                    DELETED_OK )
                    : ( log_it(L_DEBUG, "Can't delete node %s from nodelist", l_key),
                    ERR_EXISTS );
                *l_return_code = Http_Status_OK;
            }
            DAP_DELETE(l_node_info);
        }
    } break;

    default:
        log_it(L_ERROR, "Unsupported protocol version/method in the request to dap_chain_net_node_list module");
        *l_return_code = Http_Status_MethodNotAllowed;
        return;
    }
    
    dap_http_simple_reply(a_http_simple, &l_response, sizeof(uint8_t));
}

static void s_net_node_link_prepare_success(void *a_response, size_t a_response_size, void *a_arg) {
    struct node_link_request *l_node_list_request = (struct node_link_request *)a_arg;
    pthread_mutex_lock(&l_node_list_request->wait_mutex);
    l_node_list_request->response = *(uint8_t*)a_response;
    pthread_cond_signal(&l_node_list_request->wait_cond);
    pthread_mutex_unlock(&l_node_list_request->wait_mutex);
}
static void s_net_node_link_prepare_error(int a_error_code, void *a_arg){
    struct node_link_request * l_node_list_request = (struct node_link_request *)a_arg;
    dap_chain_node_info_t *l_node_info = l_node_list_request->link_info;
    if (!l_node_info) {
        log_it(L_WARNING, "Link prepare error, code %d", a_error_code);
        return;
    }
    pthread_mutex_lock(&l_node_list_request->wait_mutex);
    l_node_list_request->response = a_error_code;
    pthread_cond_signal(&l_node_list_request->wait_cond);
    pthread_mutex_unlock(&l_node_list_request->wait_mutex);
    log_it(L_WARNING, "Link from  "NODE_ADDR_FP_STR" [ %s : %u ] prepare error with code %d",
           NODE_ADDR_FP_ARGS_S(l_node_info->address), l_node_info->ext_host,
           l_node_info->ext_port, a_error_code);
}
static struct node_link_request *s_node_list_request_init ()
{
    struct node_link_request *l_node_list_request = DAP_NEW_Z(struct node_link_request);
    if(!l_node_list_request){
        return NULL;
    }
    l_node_list_request->worker = dap_events_worker_get_auto();
    l_node_list_request->response = 0;

    pthread_condattr_t attr;
    pthread_condattr_init(&attr);
#ifdef DAP_OS_DARWIN
    struct timespec ts;
    ts.tv_sec = 8;
    ts.tv_nsec = 0;
    pthread_cond_timedwait_relative_np(&l_node_list_request->wait_cond, &l_node_list_request->wait_mutex,
                                       &ts);
#else
    pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
#endif
    pthread_cond_init(&l_node_list_request->wait_cond, &attr);
    pthread_mutex_init(&l_node_list_request->wait_mutex, NULL);
    return l_node_list_request;
}

static void s_node_list_request_deinit (struct node_link_request *a_node_list_request)
{
    pthread_cond_destroy(&a_node_list_request->wait_cond);
    pthread_mutex_destroy(&a_node_list_request->wait_mutex);
    DAP_DEL_Z(a_node_list_request->link_info);
}
static int dap_chain_net_node_list_wait(struct node_link_request *a_node_list_request, int a_timeout_ms){
    pthread_mutex_lock(&a_node_list_request->wait_mutex);
    if(a_node_list_request->response)
    {
        pthread_mutex_unlock(&a_node_list_request->wait_mutex);
        return a_node_list_request->response;
    }
    struct timespec l_cond_timeout;
    clock_gettime(CLOCK_MONOTONIC, &l_cond_timeout);
    l_cond_timeout.tv_sec += a_timeout_ms/1000;
    while (!a_node_list_request->response) {
        int l_wait = pthread_cond_timedwait(&a_node_list_request->wait_cond, &a_node_list_request->wait_mutex, &l_cond_timeout);
        if (l_wait == ETIMEDOUT) {
            log_it(L_NOTICE, "Waiting for status timeout");
            a_node_list_request->response = ERR_WAIT_TIMEOUT;
            break;
        } else {
            break;
        }
    }
    pthread_mutex_unlock(&a_node_list_request->wait_mutex);
    return a_node_list_request->response;
}

static int s_cb_node_addr_compare(dap_list_t *a_list_elem, dap_list_t *a_addr_elem) {
    dap_chain_node_info_t *l_link_node_info = (dap_chain_node_info_t*)a_list_elem->data;
    dap_chain_node_addr_t *l_addr = (dap_chain_node_addr_t*)a_addr_elem->data;
    return l_addr->uint64 != l_link_node_info->address.uint64;
}

int dap_chain_net_node_list_request(dap_chain_net_t *a_net, uint16_t a_port, bool a_sync, char a_cmd)
{
    if (!a_net)
        return -1;
    
    struct node_link_request *l_link_node_request = s_node_list_request_init();
    if (!l_link_node_request) {
        log_it(L_CRITICAL, "%s", g_error_memory_alloc);
        return -4;
    };

    char *l_request = dap_strdup_printf( "%s/%s?version=1,method=%c,addr=%zu,port=%hu,net=%s",
                                         DAP_UPLINK_PATH_NODE_LIST, DAP_NODE_LIST_URI_HASH, a_cmd,
                                         g_node_addr.uint64, a_port, a_net->pub.name );
    int l_ret = -1;
    size_t l_seeds_count = 0;
    dap_stream_node_addr_t *l_seeds_addrs = dap_chain_net_get_authorized_nodes(a_net, &l_seeds_count);
    for (size_t i = 0; i < l_seeds_count; ++i) {
        dap_chain_node_info_t *l_remote = dap_chain_node_info_read(a_net, l_seeds_addrs + i);
        if (!l_remote)
            continue;
        if ( dap_client_http_request(l_link_node_request->worker, l_remote->ext_host, l_remote->ext_port,
                                    "GET", "text/text", l_request, NULL, 0, NULL,
                                    s_net_node_link_prepare_success, s_net_node_link_prepare_error,
                                    l_link_node_request, NULL) )
        {
            l_ret = a_sync ? dap_chain_net_node_list_wait(l_link_node_request, 8000) : ADD_OK;
        }
        DAP_DELETE(l_remote);
        if (l_ret == ADD_OK || l_ret == ERR_EXISTS || l_ret == DELETED_OK)
            break;
        else {
            l_link_node_request->response = 0;
            switch (l_ret) {
            case ERR_NO_SERVER:
                log_it(L_WARNING, "No server");
                break;
            case ERR_NOT_ADDED:
                log_it(L_WARNING, "Didn't add your addres node to node list");
                break;
            case ERR_HASH:
                log_it(L_WARNING, "Can't calculate hash for your addr");
                break;
            case ERR_HANDSHAKE:
                log_it(L_WARNING, "Can't do handshake for your node");
                break;
            default:
                log_it(L_WARNING, "Can't process node list HTTP request, error %d", l_ret);
                break;
            }
        }
    }
    DAP_DEL_MULTY(l_request, l_seeds_addrs);
    s_node_list_request_deinit(l_link_node_request);
    return l_ret;
}

int dap_chain_net_node_list_init()
{
    /*for (dap_chain_net_t *it = dap_chain_net_iter_start(); it; it = dap_chain_net_iter_next(it)) {
        dap_chain_net_add_nodelist_notify_callback(it, s_node_list_callback_notify, it);
    }*/
    return 0;
}

/*static int node_info_del_with_reply(dap_chain_net_t *a_net, dap_chain_node_info_t *a_node_info, const char *alias_str,
        void **a_str_reply)
{
    int l_res = -1;
    if ( !a_node_info->address.uint64 && !alias_str ) {
        dap_cli_server_cmd_set_reply_text(a_str_reply, "addr not found");
        return l_res;
    }
    // find addr by alias or addr_str
    dap_chain_node_addr_t *l_addr_by_alias = dap_chain_node_alias_find(a_net, alias_str);
    if ( alias_str && !l_addr_by_alias ) {
        dap_cli_server_cmd_set_reply_text(a_str_reply, "alias not found");
        return l_res;
    }
    dap_chain_node_addr_t l_addr = l_addr_by_alias ? *l_addr_by_alias : a_node_info->address;
    char *a_key = dap_stream_node_addr_to_str_static(l_addr);
    if ( !(l_res = dap_global_db_del_sync(a_net->pub.gdb_nodes, a_key)) ) {
        dap_list_t *list_aliases = get_aliases_by_name(a_net, &l_addr), *l_el = list_aliases;
        while (l_el) {
            dap_chain_node_alias_delete(a_net, (const char*)l_el->data);
            l_el = l_el->next;
        }
        dap_list_free_full(list_aliases, NULL);
        dap_cli_server_cmd_set_reply_text(a_str_reply, "Node deleted with all it's aliases");
    } else {
        dap_cli_server_cmd_set_reply_text(a_str_reply, "Node was not deleted from database");
    }
    DAP_DELETE(l_addr_by_alias);
    return l_res;
}*/