Skip to content
Snippets Groups Projects
Commit 709e15cd authored by Sergei Rashitov's avatar Sergei Rashitov
Browse files

Merge submodule into libdap-chain-gdb

parents 27455fe4 2f9b3618
No related branches found
No related tags found
1 merge request!24Support 3689
Subproject commit 2f9b36184b1f36cb3026fd4d242b6e47f0af6fca
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
cmake_minimum_required(VERSION 2.8)
project (dap_chain_gdb)
file(GLOB DAP_CHAIN_GDB_SRC *.c)
file(GLOB DAP_CHAIN_GDB_HDR *.h)
if(WIN32)
include_directories(../3rdparty/wepoll/)
include_directories(../3rdparty/uthash/src/)
endif()
add_library(${PROJECT_NAME} STATIC ${DAP_CHAIN_GDB_SRC} ${DAP_CHAIN_GDB_HDR})
target_link_libraries(dap_chain_gdb dap_core dap_chain dap_chain_global_db dap_chain_mempool dap_chain_net)
target_include_directories(dap_chain_gdb INTERFACE .)
set(${PROJECT_NAME}_DEFINITIONS CACHE INTERNAL "${PROJECT_NAME}: Definitions" FORCE)
set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR} CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE)
# libdap-chain-gdb
blockchain loading library
/*
* Authors:
* Alexander Lysikov <alexander.lysikov@demlabs.net>
* DeM Labs Inc. https://demlabs.net
* Kelvin Project https://github.com/kelvindap_chain_global_dbblockchain
* Copyright (c) 2019
* 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 <stdlib.h>
#include <stdbool.h>
#include <pthread.h>
#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#include <mswsock.h>
#include <ws2tcpip.h>
#include <io.h>
#include <time.h>
#include <pthread.h>
#endif
#include "utlist.h"
#include "dap_common.h"
#include "dap_strfuncs.h"
#include "dap_config.h"
#include "dap_hash.h"
#include "dap_chain_ledger.h"
#include "dap_chain_global_db.h"
#include "dap_chain_global_db_driver.h"
#include "dap_chain_net.h"
#include "dap_chain_cs.h"
#include "dap_chain_gdb.h"
#define LOG_TAG "dap_chain_gdb"
#define CONSENSUS_NAME "gdb"
typedef struct dap_chain_gdb_datum_hash_item{
char key[70];
dap_chain_hash_fast_t datum_data_hash;
uint8_t padding[2];
struct dap_chain_gdb_datum_hash_item * prev;
struct dap_chain_gdb_datum_hash_item * next;
} dap_chain_gdb_datum_hash_item_t;
typedef struct dap_chain_gdb_private
{
bool celled;
bool is_load_mode; // If load mode - not save when new atom adds
uint8_t padding[7];
char *group_datums;
dap_chain_t *chain;
dap_chain_gdb_datum_hash_item_t * hash_items;
} dap_chain_gdb_private_t;
#define PVT(a) ( (a) ? (dap_chain_gdb_private_t* ) (a)->_internal : NULL)
static int dap_chain_gdb_ledger_load(dap_chain_gdb_t *a_gdb, dap_chain_net_t *a_net);
// Atomic element organization callbacks
static int s_chain_callback_atom_add(dap_chain_t * a_chain, dap_chain_atom_ptr_t); // Accept new event in gdb
static int s_chain_callback_atom_verify(dap_chain_t * a_chain, dap_chain_atom_ptr_t); // Verify new event in gdb
static size_t s_chain_callback_atom_hdr_get_size(dap_chain_atom_ptr_t); // Get gdb event size
static size_t s_chain_callback_atom_get_static_hdr_size(void); // Get gdb event header size
static dap_chain_atom_iter_t* s_chain_callback_atom_iter_create(dap_chain_t * a_chain);
static dap_chain_atom_iter_t* s_chain_callback_atom_iter_create_from(dap_chain_t * a_chain,
dap_chain_atom_ptr_t a);
// Delete iterator
static void s_chain_callback_atom_iter_delete(dap_chain_atom_iter_t * a_atom_iter); // Get the fisrt event from gdb
static dap_chain_atom_ptr_t s_chain_callback_atom_iter_find_by_hash(dap_chain_atom_iter_t * a_atom_iter,
dap_chain_hash_fast_t * a_atom_hash);
static dap_chain_atom_ptr_t s_chain_callback_atom_iter_find_by_tx_hash(dap_chain_atom_iter_t * a_atom_iter ,
dap_chain_hash_fast_t * a_atom_hash);
// Get event(s) from gdb
static dap_chain_atom_ptr_t s_chain_callback_atom_iter_get_first(dap_chain_atom_iter_t * a_atom_iter); // Get the fisrt event from gdb
static dap_chain_atom_ptr_t s_chain_callback_atom_iter_get_next(dap_chain_atom_iter_t * a_atom_iter); // Get the next event from gdb
static dap_chain_atom_ptr_t *s_chain_callback_atom_iter_get_links(dap_chain_atom_iter_t * a_atom_iter,
size_t * a_links_size_ptr); // Get list of linked events
static dap_chain_atom_ptr_t *s_chain_callback_atom_iter_get_lasts(dap_chain_atom_iter_t * a_atom_iter,
size_t * a_lasts_size_ptr); // Get list of linked events
static size_t s_chain_callback_datums_pool_proc(dap_chain_t * a_chain, dap_chain_datum_t ** a_datums,
size_t a_datums_size);
static size_t s_chain_callback_datums_pool_proc_with_group(dap_chain_t * a_chain, dap_chain_datum_t ** a_datums,
size_t a_datums_size, const char *a_group);
/**
* Stub for consensus
*/
static int s_cs_callback_new(dap_chain_t * a_chain, dap_config_t * a_chain_cfg)
{
return dap_chain_gdb_new(a_chain, a_chain_cfg);
}
/**
* @brief dap_chain_cs_gdb_init
* @return
*/
int dap_chain_gdb_init(void)
{
dap_chain_cs_add(CONSENSUS_NAME, s_cs_callback_new);
dap_chain_class_add(CONSENSUS_NAME, dap_chain_gdb_new);
log_it(L_NOTICE, "Initialized GDB chain items organization class");
return 0;
}
/**
* @brief s_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_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_size)
{
(void) a_value;
(void) a_prefix;
if (a_arg){
dap_chain_gdb_t * l_gdb = (dap_chain_gdb_t *) a_arg;
dap_chain_net_t *l_net = dap_chain_net_by_id( l_gdb->chain->net_id);
log_it(L_DEBUG,"%s.%s: op_code='%c' group=\"%s\" key=\"%s\" value_size=%u",l_net->pub.name,
l_gdb->chain->name, a_op_code, a_group, a_key, a_value_size);
}
}
/**
* @brief dap_chain_gdb_new
* @param a_chain
* @param a_chain_cfg
*/
int dap_chain_gdb_new(dap_chain_t * a_chain, dap_config_t * a_chain_cfg)
{
dap_chain_gdb_t *l_gdb = DAP_NEW_Z(dap_chain_gdb_t);
dap_chain_gdb_private_t *l_gdb_priv = DAP_NEW_Z(dap_chain_gdb_private_t);
l_gdb->chain = a_chain;
l_gdb->_internal = (void*) l_gdb_priv;
a_chain->_inheritor = l_gdb;
l_gdb_priv->celled = dap_config_get_item_bool_default(a_chain_cfg, CONSENSUS_NAME, "celled",false);
dap_chain_net_t *l_net = dap_chain_net_by_id(a_chain->net_id);
l_gdb_priv->chain = a_chain;
if(!l_gdb_priv->celled){
l_gdb_priv->group_datums = dap_strdup_printf( "chain-gdb.%s.chain-%016llX",l_net->pub.name,
a_chain->id.uint64);
}else {
// here is not work because dap_chain_net_load() not yet fully performed
l_gdb_priv->group_datums = dap_strdup_printf( "chain-gdb.%s.chain-%016llX.cell-%016llX",l_net->pub.name,
a_chain->id.uint64, l_net->pub.cell_id.uint64);
}
// Add group prefix that will be tracking all changes
dap_chain_global_db_add_history_group_prefix("chain-gdb", GROUP_LOCAL_HISTORY);
dap_chain_global_db_add_history_callback_notify("chain-gdb", s_history_callback_notify,l_gdb);
// load ledger
l_gdb_priv->is_load_mode = true;
dap_chain_gdb_ledger_load(l_gdb, l_net);
l_gdb_priv->is_load_mode = false;
a_chain->callback_delete = dap_chain_gdb_delete;
// Atom element callbacks
a_chain->callback_atom_add = s_chain_callback_atom_add; // Accept new element in chain
a_chain->callback_atom_verify = s_chain_callback_atom_verify; // Verify new element in chain
a_chain->callback_atom_get_size = s_chain_callback_atom_hdr_get_size; // Get dag event size
a_chain->callback_atom_get_hdr_static_size = s_chain_callback_atom_get_static_hdr_size; // Get dag event hdr size
a_chain->callback_atom_iter_create = s_chain_callback_atom_iter_create;
a_chain->callback_atom_iter_create_from = s_chain_callback_atom_iter_create_from;
a_chain->callback_atom_iter_delete = s_chain_callback_atom_iter_delete;
a_chain->callback_atom_find_by_hash = s_chain_callback_atom_iter_find_by_hash;
a_chain->callback_datums_pool_proc = s_chain_callback_datums_pool_proc;
a_chain->callback_datums_pool_proc_with_group = s_chain_callback_datums_pool_proc_with_group;
// Linear pass through
a_chain->callback_atom_iter_get_first = s_chain_callback_atom_iter_get_first; // Get the fisrt element from chain
a_chain->callback_atom_iter_get_next = s_chain_callback_atom_iter_get_next; // Get the next element from chain from the current one
a_chain->callback_atom_iter_get_links = s_chain_callback_atom_iter_get_links; // Get the next element from chain from the current one
a_chain->callback_atom_iter_get_lasts = s_chain_callback_atom_iter_get_lasts;
return 0;
}
/**
* @brief dap_chain_cs_gdb_delete
* @param a_chain
* @return
*/
void dap_chain_gdb_delete(dap_chain_t * a_chain)
{
dap_chain_gdb_t * l_gdb = DAP_CHAIN_GDB(a_chain);
dap_chain_gdb_private_t *l_gdb_priv = PVT(l_gdb);
DAP_DELETE(l_gdb_priv->group_datums);
DAP_DELETE(l_gdb);
a_chain->_inheritor = NULL;
}
/**
* @brief dap_chain_gdb_get_group
* @param a_chain
* @return group name for ledger
*/
const char* dap_chain_gdb_get_group(dap_chain_t * a_chain)
{
if(!a_chain)
return NULL;
dap_chain_gdb_t * l_gdb = DAP_CHAIN_GDB(a_chain);
dap_chain_gdb_private_t *l_gdb_priv = PVT(l_gdb);
return l_gdb_priv->group_datums;
}
/**
* @brief compare_datum_items
* @param l_a
* @param l_b
* @return
*/
/*static int compare_datum_items(const void * l_a, const void * l_b)
{
const dap_chain_datum_t *l_item_a = (const dap_chain_datum_t*) l_a;
const dap_chain_datum_t *l_item_b = (const dap_chain_datum_t*) l_b;
if(l_item_a->header.ts_create == l_item_b->header.ts_create)
return 0;
if(l_item_a->header.ts_create < l_item_b->header.ts_create)
return -1;
return 1;
}*/
/**
* Load ledger from mempool
*
* return 0 if OK otherwise negative error code
*/
static int dap_chain_gdb_ledger_load(dap_chain_gdb_t *a_gdb, dap_chain_net_t *a_net)
{
dap_chain_gdb_private_t *l_gdb_priv = PVT(a_gdb);
dap_list_t *l_datum_list = NULL, *l_list_tmp = NULL;
// protect from reloading
if(dap_chain_ledger_count( a_net->pub.ledger ) > 0)
return 0;
size_t l_data_size = 0;
// Read the entire database into an array of size bytes
dap_global_db_obj_t *data = dap_chain_global_db_gr_load(l_gdb_priv->group_datums , &l_data_size);
// make list of datums
for(size_t i = 0; i < l_data_size; i++) {
l_datum_list = dap_list_append(l_datum_list, data[i].value);
}
// sort list by time
//l_datum_list = dap_list_sort(l_datum_list, (dap_callback_compare_t) compare_datum_items);
l_list_tmp = l_datum_list;
// add datum_tx from list to ledger
while(l_list_tmp) {
dap_chain_datum_t *l_datum = (dap_chain_datum_t*) l_list_tmp->data;
s_chain_callback_atom_add(a_gdb->chain,l_datum);
l_list_tmp = dap_list_next(l_list_tmp);
}
dap_chain_global_db_objs_delete(data, l_data_size);
dap_list_free(l_datum_list);
return 0;
}
/**
* @brief s_chain_callback_datums_add
* @param a_chain
* @param a_datums
* @param a_datums_size
*/
static size_t s_chain_callback_datums_pool_proc(dap_chain_t * a_chain, dap_chain_datum_t ** a_datums,
size_t a_datums_count)
{
for(size_t i = 0; i < a_datums_count; i++) {
dap_chain_datum_t * l_datum = a_datums[i];
s_chain_callback_atom_add(a_chain, l_datum );
}
return a_datums_count;
}
static size_t s_chain_callback_datums_pool_proc_with_group(dap_chain_t * a_chain, dap_chain_datum_t ** a_datums,
size_t a_datums_count, const char *a_group)
{
if(dap_strcmp(dap_chain_gdb_get_group(a_chain), a_group))
return 0;
return s_chain_callback_datums_pool_proc(a_chain, a_datums, a_datums_count);
}
/**
* @brief s_chain_callback_datums_add
* @param a_chain
* @param a_datums
* @param a_datums_size
*/
static int s_chain_callback_atom_add(dap_chain_t * a_chain, dap_chain_atom_ptr_t a_atom)
{
dap_chain_gdb_t * l_gdb = DAP_CHAIN_GDB(a_chain);
dap_chain_gdb_private_t *l_gdb_priv = PVT(l_gdb);
dap_chain_datum_t *l_datum = (dap_chain_datum_t*) a_atom;
switch (l_datum->header.type_id) {
case DAP_CHAIN_DATUM_TOKEN_DECL:{
dap_chain_datum_token_t *l_token = (dap_chain_datum_token_t*) l_datum->data;
dap_chain_ledger_token_add(a_chain->ledger,l_token, l_datum->header.data_size);
}break;
case DAP_CHAIN_DATUM_TOKEN_EMISSION: {
dap_chain_datum_token_emission_t *l_token_emission = (dap_chain_datum_token_emission_t*) l_datum->data;
dap_chain_ledger_token_emission_add(a_chain->ledger, l_token_emission, l_datum->header.data_size);
}break;
case DAP_CHAIN_DATUM_TX:{
dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t*) l_datum->data;
//if ( !l_gdb_priv->is_load_mode ) // If its not load module but mempool proc
// l_tx->header.ts_created = time(NULL);
//if(dap_chain_datum_tx_get_size(l_tx) == l_datum->header.data_size){
// don't save bad transactions to base
if(dap_chain_ledger_tx_add(a_chain->ledger, l_tx) != 1)
return -1;
//}else
// return -2;
}break;
default: return -1;
}
dap_chain_gdb_datum_hash_item_t * l_hash_item = DAP_NEW_Z(dap_chain_gdb_datum_hash_item_t);
size_t l_datum_size = dap_chain_datum_size(l_datum);
dap_hash_fast(l_datum->data,l_datum->header.data_size,&l_hash_item->datum_data_hash );
dap_chain_hash_fast_to_str(&l_hash_item->datum_data_hash,l_hash_item->key,sizeof(l_hash_item->key)-1);
if ( !l_gdb_priv->is_load_mode ){
dap_chain_global_db_gr_set(l_hash_item->key, l_datum, l_datum_size, l_gdb_priv->group_datums);
}else
log_it(L_DEBUG,"Load mode, doesnt save item %s:%s", l_hash_item->key, l_gdb_priv->group_datums);
DL_APPEND(l_gdb_priv->hash_items, l_hash_item);
return 0;
}
/**
* @brief s_chain_callback_atom_verify Verify atomic element
* @param a_chain
* @param a_atom
* @return
*/
static int s_chain_callback_atom_verify(dap_chain_t * a_chain, dap_chain_atom_ptr_t a_atom)
{
(void) a_chain;
(void) a_atom;
return 0;
}
/**
* @brief s_chain_callback_atom_get_size Get size of atomic element
* @param a_atom
* @return
*/
static size_t s_chain_callback_atom_hdr_get_size(dap_chain_atom_ptr_t a_atom)
{
return ((dap_chain_datum_t *) a_atom)->header.data_size +sizeof (((dap_chain_datum_t *) a_atom)->header);
}
/**
* @brief s_chain_callback_atom_get_static_hdr_size
* @param a_chain
* @return
*/
static size_t s_chain_callback_atom_get_static_hdr_size()
{
static dap_chain_datum_t *l_datum_null=NULL;
return sizeof(l_datum_null->header);
}
/**
* @brief s_chain_callback_atom_iter_create Create atomic element iterator
* @param a_chain
* @return
*/
static dap_chain_atom_iter_t* s_chain_callback_atom_iter_create(dap_chain_t * a_chain)
{
dap_chain_atom_iter_t * l_iter = DAP_NEW_Z(dap_chain_atom_iter_t);
l_iter->chain = a_chain;
return l_iter;
}
/**
* @brief s_chain_callback_atom_iter_create_from
* @param a_chain
* @param a_atom
* @return
*/
static dap_chain_atom_iter_t* s_chain_callback_atom_iter_create_from(dap_chain_t * a_chain,
dap_chain_atom_ptr_t a_atom)
{
dap_chain_atom_iter_t * l_iter = DAP_NEW_Z(dap_chain_atom_iter_t);
l_iter->chain = a_chain;
l_iter->cur = a_atom;
return l_iter;
}
/**
* @brief s_chain_callback_atom_iter_delete Delete dag event iterator
* @param a_atom_iter
*/
static void s_chain_callback_atom_iter_delete(dap_chain_atom_iter_t * a_atom_iter)
{
DAP_DELETE(a_atom_iter);
}
/**
* @brief s_chain_callback_atom_iter_find_by_hash
* @details Searchs by datum data hash, not for datum's hash itself
* @param a_atom_iter
* @param a_atom_hash
* @return
*/
static dap_chain_atom_ptr_t s_chain_callback_atom_iter_find_by_hash(dap_chain_atom_iter_t * a_atom_iter,
dap_chain_hash_fast_t * a_atom_hash)
{
char * l_key = dap_chain_hash_fast_to_str_new(a_atom_hash);
size_t l_ret_size;
dap_chain_atom_ptr_t l_ret;
dap_chain_gdb_t * l_gdb = DAP_CHAIN_GDB(a_atom_iter->chain );
l_ret = dap_chain_global_db_gr_get(l_key,&l_ret_size,
PVT ( l_gdb )->group_datums );
return l_ret;
}
/**
* @brief s_chain_callback_atom_iter_get_first Get the first dag event
* @param a_atom_iter
* @return
*/
static dap_chain_atom_ptr_t s_chain_callback_atom_iter_get_first(dap_chain_atom_iter_t * a_atom_iter)
{
dap_chain_datum_t * l_datum = NULL;
a_atom_iter->cur_item = PVT ( DAP_CHAIN_GDB(a_atom_iter->chain) )->hash_items;
if (a_atom_iter->cur_item ){
dap_chain_gdb_datum_hash_item_t * l_item = PVT ( DAP_CHAIN_GDB(a_atom_iter->chain) )->hash_items;
size_t l_datum_size =0;
l_datum= (dap_chain_datum_t*) dap_chain_global_db_gr_get(l_item->key,&l_datum_size,PVT(DAP_CHAIN_GDB(a_atom_iter->chain))->group_datums );
if (a_atom_iter->cur) // This iterator should clean up data for it because its allocate it
DAP_DELETE( a_atom_iter->cur);
a_atom_iter->cur = l_datum;
}
return l_datum;
}
/**
* @brief s_chain_callback_atom_iter_get_next Get the next dag event
* @param a_atom_iter
* @return
*/
static dap_chain_atom_ptr_t s_chain_callback_atom_iter_get_next(dap_chain_atom_iter_t * a_atom_iter)
{
dap_chain_datum_t * l_datum = NULL;
a_atom_iter->cur_item = a_atom_iter->cur_item?
((dap_chain_gdb_datum_hash_item_t*) a_atom_iter->cur_item)->next : NULL;
if (a_atom_iter->cur_item ){
size_t l_datum_size =0;
l_datum= (dap_chain_datum_t*) dap_chain_global_db_gr_get(
((dap_chain_gdb_datum_hash_item_t*) a_atom_iter->cur_item)->key,
&l_datum_size, PVT(DAP_CHAIN_GDB(a_atom_iter->chain))->group_datums );
if (a_atom_iter->cur) // This iterator should clean up data for it because its allocate it
DAP_DELETE( a_atom_iter->cur);
a_atom_iter->cur = l_datum;
}
return l_datum;
}
/**
* @brief s_chain_callback_atom_iter_get_links
* @param a_atom_iter
* @param a_links_size_ptr
* @return
*/
static dap_chain_atom_ptr_t* s_chain_callback_atom_iter_get_links(dap_chain_atom_iter_t * a_atom_iter,
size_t * a_links_size_ptr)
{
(void) a_atom_iter;
(void) a_links_size_ptr;
return NULL;
}
/**
* @brief s_chain_callback_atom_iter_get_lasts
* @param a_atom_iter
* @param a_lasts_size_ptr
* @return
*/
static dap_chain_atom_ptr_t* s_chain_callback_atom_iter_get_lasts(dap_chain_atom_iter_t * a_atom_iter,
size_t * a_lasts_size_ptr)
{
(void) a_atom_iter;
(void) a_lasts_size_ptr;
return NULL;
}
/*
* Authors:
* Alexander Lysikov <alexander.lysikov@demlabs.net>
* DeM Labs Inc. https://demlabs.net
* Kelvin Project https://github.com/kelvinblockchain
* Copyright (c) 2019
* 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/>.
*/
#pragma once
#include "dap_chain.h"
typedef struct dap_chain_gdb {
dap_chain_t *chain;
void * _internal; // private data
void * _inheritor; // inheritor object
} dap_chain_gdb_t;
#define DAP_CHAIN_GDB(a) ( (a) ? (dap_chain_gdb_t *) (a)->_inheritor : NULL)
int dap_chain_gdb_init(void);
int dap_chain_gdb_new(dap_chain_t * a_chain, dap_config_t * a_chain_cfg);
void dap_chain_gdb_delete(dap_chain_t * a_chain);
const char* dap_chain_gdb_get_group(dap_chain_t * a_chain);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment