Skip to content
Snippets Groups Projects
dap_chain_datum.c 47.82 KiB
/*
 * Authors:
 * Dmitriy A. Gearasimov <gerasimov.dmitriy@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 <string.h>

#include "dap_common.h"
#include "dap_time.h"
#include "dap_chain_datum.h"
#include "dap_chain_datum_tx.h"
#include "dap_chain_datum_token.h"
#include "dap_chain_datum_tx_items.h"
#include "dap_chain_datum_decree.h"
#include "dap_chain_datum_anchor.h"
#include "dap_chain_datum_hashtree_roots.h"
#include "dap_enc_base58.h"

#define LOG_TAG "dap_chain_datum"

/**
 * @brief dap_chain_datum_create
 * @param a_type_id
 * @param a_data
 * @param a_data_size
 * @return
 */
dap_chain_datum_t * dap_chain_datum_create(uint16_t a_type_id, const void * a_data, size_t a_data_size)
{
   dap_chain_datum_t * l_datum = DAP_NEW_Z_SIZE(dap_chain_datum_t, sizeof(l_datum->header)+ a_data_size);
   memcpy(l_datum->data, a_data, (uint32_t)a_data_size);        // Compiler warning escape
   l_datum->header.type_id = a_type_id;
   l_datum->header.data_size = (uint32_t) a_data_size;
   l_datum->header.version_id = DAP_CHAIN_DATUM_VERSION;
   l_datum->header.ts_create =(uint64_t) time(NULL);
   return  l_datum;
}

void s_datum_token_dump_tsd(dap_string_t *a_str_out, dap_chain_datum_token_t *a_token, size_t a_token_size, const char *a_hash_out_type)
{
    dap_tsd_t *l_tsd = dap_chain_datum_token_tsd_get(a_token, a_token_size);
    if (l_tsd == NULL) {
        dap_string_append_printf(a_str_out,"<CORRUPTED TSD SECTION>\n");
        return;
    }
    size_t l_tsd_total_size = 0;
    switch (a_token->type) {
        case DAP_CHAIN_DATUM_TOKEN_TYPE_DECL:
            switch (a_token->subtype) {
                case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PRIVATE:
                    l_tsd_total_size = a_token->header_private_decl.tsd_total_size; break;
                case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE:
                    l_tsd_total_size = a_token->header_native_decl.tsd_total_size; break;
                default: break;
            } break;
        case DAP_CHAIN_DATUM_TOKEN_TYPE_UPDATE:
            switch (a_token->subtype) {
                case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PRIVATE:
                    l_tsd_total_size = a_token->header_private_update.tsd_total_size; break;
                case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE:
                    l_tsd_total_size = a_token->header_native_update.tsd_total_size; break;
                default: break;
            } break;
        default: break;
    }
    size_t l_tsd_size = 0;
    for (size_t l_offset = 0; l_offset < l_tsd_total_size; l_offset += l_tsd_size) {
        l_tsd = (dap_tsd_t *) (((byte_t*)l_tsd) + l_tsd_size);
        l_tsd_size = l_tsd ? dap_tsd_size(l_tsd) : 0;
        if (l_tsd_size == 0) {
            log_it(L_ERROR,"Wrong zero TSD size, exiting s_datum_token_dump_tsd()");
            return;
        } else if (l_tsd_size+l_offset > l_tsd_total_size) {
            log_it(L_WARNING, "<CORRUPTED TSD> too big size %u when left maximum %zu",
                   l_tsd->size, l_tsd_total_size - 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_tsd_get_scalar(l_tsd, uint16_t));
                continue;
            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_tsd_get_scalar(l_tsd, uint16_t));
            continue;
            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SUPPLY: { // 256
                char *l_balance = dap_chain_balance_print(dap_tsd_get_scalar(l_tsd, uint256_t));
                dap_string_append_printf(a_str_out, "total_supply: %s\n", l_balance);
                DAP_DELETE(l_balance);
            }
            continue;
            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SUPPLY_OLD: {// 128
                char *l_balance = dap_chain_balance_print(GET_256_FROM_128(dap_tsd_get_scalar(l_tsd, uint128_t)));
                dap_string_append_printf(a_str_out, "total_supply: %s\n", l_balance);
                DAP_DELETE(l_balance);
            }
            continue;
            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_VALID :
                dap_string_append_printf(a_str_out,"total_signs_valid: %u\n",
                                         dap_tsd_get_scalar(l_tsd, uint16_t) );
            continue;
            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_ADD:
                if(l_tsd->size >= sizeof(dap_pkey_t)){
                    char *l_hash_str;
                    dap_pkey_t *l_pkey = (dap_pkey_t*)l_tsd->data;
                    dap_hash_fast_t l_hf = {0};
                    if (!dap_pkey_get_hash(l_pkey, &l_hf)) {
                        dap_string_append_printf(a_str_out,"total_pkeys_add: <WRONG CALCULATION FINGERPRINT>\n");
                    } else {
                        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(&l_hf);
                        else
                            l_hash_str = dap_enc_base58_encode_hash_to_str(&l_hf);
                        dap_string_append_printf(a_str_out, "total_pkeys_add: %s\n", l_hash_str);
                        DAP_DELETE(l_hash_str);
                    }
                } else
                    dap_string_append_printf(a_str_out,"total_pkeys_add: <WRONG SIZE %u>\n", l_tsd->size);
            continue;
            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_REMOVE:
                if(l_tsd->size == sizeof(dap_chain_hash_fast_t) ){
                    char *l_hash_str = (!dap_strcmp(a_hash_out_type,"hex")|| !dap_strcmp(a_hash_out_type, "content_hash"))
                            ? dap_chain_hash_fast_to_str_new((dap_chain_hash_fast_t*) l_tsd->data)
                            : dap_enc_base58_encode_hash_to_str((dap_chain_hash_fast_t*) l_tsd->data);
                    dap_string_append_printf(a_str_out,"total_pkeys_remove: %s\n", l_hash_str);
                    DAP_DELETE( l_hash_str );
                }else
                    dap_string_append_printf(a_str_out,"total_pkeys_remove: <WRONG SIZE %u>\n", l_tsd->size);
            continue;
            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DELEGATE_EMISSION_FROM_STAKE_LOCK: {
                char *balance = NULL;
                dap_chain_datum_token_tsd_delegate_from_stake_lock_t *l_tsd_section = dap_tsd_get_object(l_tsd, dap_chain_datum_token_tsd_delegate_from_stake_lock_t);
                dap_string_append_printf(a_str_out, "ticker_token_from: %s\nemission_rate: %s\n",
                                         l_tsd_section->ticker_token_from, (balance = dap_chain_balance_to_coins(l_tsd_section->emission_rate)));
                DAP_DEL_Z(balance);
            }continue;
            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_tsd_get_string_const(l_tsd) );
            continue;
            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_tsd_get_string_const(l_tsd) );
            continue;
            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_tsd_get_string_const(l_tsd) );
            continue;
            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_tsd_get_string_const(l_tsd) );
            continue;
            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_tsd_get_string_const(l_tsd) );
            continue;
            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_tsd_get_string_const(l_tsd) );
            continue;
            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_tsd_get_string_const(l_tsd) );
            continue;
            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_tsd_get_string_const(l_tsd) );
            continue;
            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_tsd_get_string_const(l_tsd) );
            continue;
            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_ALLOWED_REMOVE:
                dap_string_append_printf(a_str_out,"tx_receiver_allowed: %s\n",
                                         dap_tsd_get_string_const(l_tsd) );
            continue;
            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_tsd_get_string_const(l_tsd) );
            continue;
            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_tsd_get_string_const(l_tsd) );
            continue;
            default: dap_string_append_printf(a_str_out, "<0x%04hX>: <size %u>\n", l_tsd->type, l_tsd->size);
        }
    }
}
/**
 * @brief _dap_chain_datum_tx_out_data
 *
 * @param a_datum
 * @param a_ledger
 * @param a_str_out
 * @param a_hash_out_type
 * @param save_processed_tx
 * @param a_tx_hash_processed
 * @param l_tx_num
 */
bool dap_chain_datum_dump_tx(dap_chain_datum_tx_t *a_datum,
                             const char *a_ticker,
                             dap_string_t *a_str_out,
                             const char *a_hash_out_type,
                             dap_hash_fast_t *a_tx_hash,
                             dap_chain_net_id_t a_net_id)
{
    dap_time_t l_ts_create = (dap_time_t)a_datum->header.ts_created;
    bool l_is_first = false;
    dap_chain_tx_in_t *l_in_item = (dap_chain_tx_in_t *)dap_chain_datum_tx_item_get(a_datum, NULL, TX_ITEM_TYPE_IN, NULL);
    if (l_in_item && dap_hash_fast_is_blank(&l_in_item->header.tx_prev_hash))
        l_is_first = true;
    char l_tmp_buf[70];
    char *l_hash_str = dap_strcmp(a_hash_out_type, "hex")
            ? dap_enc_base58_encode_hash_to_str(a_tx_hash)
            : dap_chain_hash_fast_to_str_new(a_tx_hash);
    dap_string_append_printf(a_str_out, "transaction:%s hash %s\n TS Created: %s%s%s\n Items:\n",
                              l_is_first ? " (emit)" : "", l_hash_str, dap_ctime_r(&l_ts_create, l_tmp_buf),
                              a_ticker ? " Token ticker: " : "", a_ticker ? a_ticker : "");
    DAP_DELETE(l_hash_str);
    uint32_t l_tx_items_count = 0;
    uint32_t l_tx_items_size = a_datum->header.tx_items_size;
    dap_sign_t *l_sign_tmp;
    dap_hash_fast_t *l_hash_tmp = NULL;
    dap_pkey_t *l_pkey_tmp;
    while (l_tx_items_count < l_tx_items_size) {
        uint8_t *item = a_datum->tx_items + l_tx_items_count;
        size_t l_item_tx_size = dap_chain_datum_item_tx_get_size(item);
        switch(dap_chain_datum_tx_item_get_type(item)){
        case TX_ITEM_TYPE_IN:
            l_hash_tmp = &((dap_chain_tx_in_t*)item)->header.tx_prev_hash;
            if (dap_hash_fast_is_blank(l_hash_tmp)) {
                l_hash_str = dap_strdup("BLANK");
            } else {
                l_hash_str = dap_strcmp(a_hash_out_type, "hex")
                        ? dap_enc_base58_encode_hash_to_str(l_hash_tmp)
                        : dap_chain_hash_fast_to_str_new(l_hash_tmp);
            }
            dap_string_append_printf(a_str_out, "\t IN:\nTx_prev_hash: %s\n"
                                                "\t\t Tx_out_prev_idx: %u\n",
                                        l_hash_str,
                                        ((dap_chain_tx_in_t*)item)->header.tx_out_prev_idx);
            DAP_DELETE(l_hash_str);
            break;
        case TX_ITEM_TYPE_OUT_OLD: {
            char *l_value_str = dap_chain_balance_to_coins(dap_chain_uint256_from(
                                                               ((dap_chain_tx_out_old_t*)item)->header.value));
            char *l_addr_str = dap_chain_addr_to_str(&((dap_chain_tx_out_old_t*)item)->addr);
            dap_string_append_printf(a_str_out, "\t OUT OLD (64):\n"
                                                "\t\t Value: %s (%"DAP_UINT64_FORMAT_U")\n"
                                                "\t\t Address: %s\n",
                                        l_value_str,
                                        ((dap_chain_tx_out_old_t*)item)->header.value,
                                        l_addr_str);
            DAP_DELETE(l_value_str);
            DAP_DELETE(l_addr_str);
        } break;
        case TX_ITEM_TYPE_OUT: { // 256
            char *l_value_str = dap_chain_balance_print(((dap_chain_tx_out_t*)item)->header.value);
            char *l_coins_str = dap_chain_balance_to_coins(((dap_chain_tx_out_t*)item)->header.value);
            char *l_addr_str = dap_chain_addr_to_str(&((dap_chain_tx_out_t*)item)->addr);
            dap_string_append_printf(a_str_out, "\t OUT:\n"
                                                "\t\t Value: %s (%s)\n"
                                                "\t\t Address: %s\n",
                                        l_coins_str,
                                        l_value_str,
                                        l_addr_str);
            DAP_DELETE(l_value_str);
            DAP_DELETE(l_coins_str);
            DAP_DELETE(l_addr_str);
        } break;
        case TX_ITEM_TYPE_IN_EMS: {
            l_hash_tmp = &((dap_chain_tx_in_ems_t*)item)->header.token_emission_hash;
            l_hash_str = dap_strcmp(a_hash_out_type, "hex")
                    ? dap_enc_base58_encode_hash_to_str(l_hash_tmp)
                    : dap_chain_hash_fast_to_str_new(l_hash_tmp);
            dap_string_append_printf(a_str_out, "\t IN_EMS:\n"
                                                "\t\t ticker: %s \n"
                                                "\t\t token_emission_hash: %s\n"
                                                "\t\t token_emission_chain_id: 0x%016"DAP_UINT64_FORMAT_x"\n",
                                                ((dap_chain_tx_in_ems_t*)item)->header.ticker,
                                                l_hash_str,
                                                ((dap_chain_tx_in_ems_t*)item)->header.token_emission_chain_id.uint64);
            DAP_DELETE(l_hash_str);
        } break;
        case TX_ITEM_TYPE_IN_EMS_EXT: {
            l_hash_tmp = &((dap_chain_tx_in_ems_ext_t*)item)->header.ext_tx_hash;
            l_hash_str = dap_strcmp(a_hash_out_type, "hex")
                    ? dap_enc_base58_encode_hash_to_str(l_hash_tmp)
                    : dap_chain_hash_fast_to_str_new(l_hash_tmp);
            dap_string_append_printf(a_str_out, "\t IN_EMS EXT:\n"
                                         "\t\t Version: %u\n"
                                         "\t\t Ticker: %s\n"
                                         "\t\t Ext chain id: 0x%016"DAP_UINT64_FORMAT_x"\n"
                                         "\t\t Ext net id: 0x%016"DAP_UINT64_FORMAT_x"\n"
                                         "\t\t Ext tx hash: %s\n"
                                         "\t\t Ext tx out idx: %u\n",
                                     ((dap_chain_tx_in_ems_ext_t*)item)->header.version,
                                     ((dap_chain_tx_in_ems_ext_t*)item)->header.ticker,
                                     ((dap_chain_tx_in_ems_ext_t*)item)->header.ext_chain_id.uint64,
                                     ((dap_chain_tx_in_ems_ext_t*)item)->header.ext_net_id.uint64,
                                     l_hash_str,
                                     ((dap_chain_tx_in_ems_ext_t*)item)->header.ext_tx_out_idx);
            DAP_DELETE(l_hash_str);
        } break;
        case TX_ITEM_TYPE_SIG: {
            l_sign_tmp = dap_chain_datum_tx_item_sign_get_sig((dap_chain_tx_sig_t *)item);
            dap_sign_get_information(l_sign_tmp, a_str_out, a_hash_out_type);
            dap_chain_addr_t l_sender_addr;
            dap_chain_addr_fill_from_sign(&l_sender_addr, l_sign_tmp, a_net_id);
            const char *l_addr_str = dap_chain_addr_to_str(&l_sender_addr);
            dap_string_append_printf(a_str_out, "\tSender addr: %s", l_addr_str);
            DAP_DELETE(l_addr_str);
        } break;
        case TX_ITEM_TYPE_RECEIPT: {
            char *l_value_str = dap_chain_balance_print(
                        ((dap_chain_datum_tx_receipt_t*)item)->receipt_info.value_datoshi);
            char *l_coins_str = dap_chain_balance_to_coins(
                        ((dap_chain_datum_tx_receipt_t*)item)->receipt_info.value_datoshi);
            dap_string_append_printf(a_str_out, "\t Receipt:\n"
                                                "\t\t size: %"DAP_UINT64_FORMAT_U"\n"
                                                "\t\t ext size: %"DAP_UINT64_FORMAT_U"\n"
                                                "\t\t Info:"
                                                "\t\t\t   units: 0x%016"DAP_UINT64_FORMAT_x"\n"
                                                "\t\t\t   uid: 0x%016"DAP_UINT64_FORMAT_x"\n"
                                                "\t\t\t   units type: %s \n"
                                                "\t\t\t   value: %s (%s)\n",
                                     ((dap_chain_datum_tx_receipt_t*)item)->size,
                                     ((dap_chain_datum_tx_receipt_t*)item)->exts_size,
                                     ((dap_chain_datum_tx_receipt_t*)item)->receipt_info.units,
                                     ((dap_chain_datum_tx_receipt_t*)item)->receipt_info.srv_uid.uint64,
                                     dap_chain_srv_unit_enum_to_str(((dap_chain_datum_tx_receipt_t*)item)->receipt_info.units_type.enm),
                                     l_coins_str,
                                     l_value_str);
            if (((dap_chain_datum_tx_receipt_t*)item)->exts_size == sizeof(dap_sign_t) + sizeof(dap_sign_t)){
                dap_sign_t *l_provider = DAP_NEW_Z(dap_sign_t);
                if (!l_provider) {
                    log_it(L_CRITICAL, "Memory allocation error");
                    DAP_DELETE(l_value_str);
                    DAP_DELETE(l_coins_str);
                    return false;
                }
                memcpy(l_provider, ((dap_chain_datum_tx_receipt_t*)item)->exts_n_signs, sizeof(dap_sign_t));
                dap_sign_t *l_client = DAP_NEW_Z(dap_sign_t);
                if (!l_client) {
                    log_it(L_CRITICAL, "Memory allocation error");
                    DAP_DEL_Z(l_provider);
                    DAP_DELETE(l_value_str);
                    DAP_DELETE(l_coins_str);
                    return false;
                }
                memcpy(l_client,
                       ((dap_chain_datum_tx_receipt_t*)item)->exts_n_signs + sizeof(dap_sign_t),
                       sizeof(dap_sign_t));
                dap_string_append_printf(a_str_out, "Exts:\n"
                                                    "   Provider:\n");
                dap_sign_get_information(l_provider, a_str_out, a_hash_out_type);
                dap_string_append_printf(a_str_out, "   Client:\n");
                dap_sign_get_information(l_client, a_str_out, a_hash_out_type);
            } else if (((dap_chain_datum_tx_receipt_t*)item)->exts_size == sizeof(dap_sign_t)) {
                dap_sign_t *l_provider = DAP_NEW_Z(dap_sign_t);
                if (!l_provider) {
                    log_it(L_CRITICAL, "Memory allocation error");
                    DAP_DELETE(l_value_str);
                    DAP_DELETE(l_coins_str);
                    return false;
                }
                memcpy(l_provider, ((dap_chain_datum_tx_receipt_t*)item)->exts_n_signs, sizeof(dap_sign_t));
                dap_string_append_printf(a_str_out, "Exts:\n"
                                                    "   Provider:\n");
                dap_sign_get_information(l_provider, a_str_out, a_hash_out_type);
            }
            DAP_DELETE(l_value_str);
            DAP_DELETE(l_coins_str);
        } break;
        case TX_ITEM_TYPE_PKEY: {
            l_pkey_tmp = (dap_pkey_t*)((dap_chain_tx_pkey_t*)item)->pkey;
            dap_chain_hash_fast_t l_pkey_hash_tmp;
            dap_hash_fast(l_pkey_tmp->pkey, l_pkey_tmp->header.size, &l_pkey_hash_tmp);
            l_hash_str = dap_strcmp(a_hash_out_type, "hex")
                    ? dap_enc_base58_encode_hash_to_str(&l_pkey_hash_tmp)
                    : dap_chain_hash_fast_to_str_new(&l_pkey_hash_tmp);
            dap_string_append_printf(a_str_out, "\t PKey: \n"
                                                "\t\t SIG type: %s\n"
                                                "\t\t SIG size: %u\n"
                                                "\t\t Sequence number: %u \n"
                                                "\t\t Key: \n"
                                                "\t\t\t Type: %s\n"
                                                "\t\t\t Size: %u\n"
                                                "\t\t\t Hash: %s\n",
                                     dap_sign_type_to_str(((dap_chain_tx_pkey_t*)item)->header.sig_type),
                                     ((dap_chain_tx_pkey_t*)item)->header.sig_size,
                                     ((dap_chain_tx_pkey_t*)item)->seq_no,
                                     dap_pkey_type_to_str(l_pkey_tmp->header.type),
                                     l_pkey_tmp->header.size,
                                     l_hash_str);
            DAP_DELETE(l_hash_str);
        } break;
        case TX_ITEM_TYPE_TSD: {
            dap_string_append_printf(a_str_out, "\t TSD data: \n"
                                                "\t\t type: %d\n"
                                                "\t\t size: %lu\n",
                                     ((dap_chain_tx_tsd_t*)item)->header.type,
                                     ((dap_chain_tx_tsd_t*)item)->header.size);
        } break;
        case TX_ITEM_TYPE_IN_COND:
            l_hash_tmp = &((dap_chain_tx_in_cond_t*)item)->header.tx_prev_hash;
            l_hash_str = dap_strcmp(a_hash_out_type, "hex")
                    ? dap_enc_base58_encode_hash_to_str(l_hash_tmp)
                    : dap_chain_hash_fast_to_str_new(l_hash_tmp);
            dap_string_append_printf(a_str_out, "\t IN COND:\n\t\tReceipt_idx: %u\n"
                                                "\t\t Tx_prev_hash: %s\n"
                                                "\t\t Tx_out_prev_idx: %u\n",
                                     ((dap_chain_tx_in_cond_t*)item)->header.receipt_idx,
                                     l_hash_str,
                                     ((dap_chain_tx_in_cond_t*)item)->header.tx_out_prev_idx);
            DAP_DELETE(l_hash_str);
            break;
        case TX_ITEM_TYPE_OUT_COND: {
            char *l_value_str = dap_chain_balance_print(((dap_chain_tx_out_cond_t*)item)->header.value);
            char *l_coins_str = dap_chain_balance_to_coins(((dap_chain_tx_out_cond_t*)item)->header.value);
            dap_time_t l_ts_exp = ((dap_chain_tx_out_cond_t*)item)->header.ts_expires;
            dap_string_append_printf(a_str_out, "\t OUT COND:\n"
                                                "\t Header:\n"
                                                "\t\t ts_expires: %s"
                                                "\t\t value: %s (%s)\n"
                                                "\t\t subtype: %s\n"
                                                "\t\t uid: 0x%016"DAP_UINT64_FORMAT_x"\n",
                                     l_ts_exp ? dap_ctime_r(&l_ts_exp, l_tmp_buf) : "never\n",
                                     l_coins_str, l_value_str,
                                     dap_chain_tx_out_cond_subtype_to_str(((dap_chain_tx_out_cond_t*)item)->header.subtype),
                                     ((dap_chain_tx_out_cond_t*)item)->header.srv_uid.uint64);
            DAP_DELETE(l_value_str);
            DAP_DELETE(l_coins_str);
            switch (((dap_chain_tx_out_cond_t*)item)->header.subtype) {
                case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_PAY: {
                    char *l_value_str = dap_chain_balance_print(((dap_chain_tx_out_cond_t*)item)->subtype.srv_pay.unit_price_max_datoshi);
                    char *l_coins_str = dap_chain_balance_to_coins(((dap_chain_tx_out_cond_t*)item)->subtype.srv_pay.unit_price_max_datoshi);
                    l_hash_tmp = &((dap_chain_tx_out_cond_t*)item)->subtype.srv_pay.pkey_hash;
                    l_hash_str = dap_strcmp(a_hash_out_type, "hex")
                            ? dap_enc_base58_encode_hash_to_str(l_hash_tmp)
                            : dap_chain_hash_fast_to_str_new(l_hash_tmp);
                    dap_string_append_printf(a_str_out, "\t\t\t unit: 0x%08x\n"
                                                        "\t\t\t pkey: %s\n"
                                                        "\t\t\t max price: %s (%s)\n",
                                             ((dap_chain_tx_out_cond_t*)item)->subtype.srv_pay.unit.uint32,
                                             l_hash_str,
                                             l_coins_str,
                                             l_value_str);
                    DAP_DELETE(l_hash_str);
                    DAP_DELETE(l_value_str);
                    DAP_DELETE(l_coins_str);
                } break;
                case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE: {
                    dap_chain_node_addr_t *l_signer_node_addr = &((dap_chain_tx_out_cond_t*)item)->subtype.srv_stake_pos_delegate.signer_node_addr;
                    dap_chain_addr_t *l_signing_addr = &((dap_chain_tx_out_cond_t*)item)->subtype.srv_stake_pos_delegate.signing_addr;
                    char *l_addr_str = dap_chain_addr_to_str(l_signing_addr);
                    l_hash_tmp = &l_signing_addr->data.hash_fast;
                    l_hash_str = dap_strcmp(a_hash_out_type, "hex")
                            ? dap_enc_base58_encode_hash_to_str(l_hash_tmp)
                            : dap_chain_hash_fast_to_str_new(l_hash_tmp);
                    dap_string_append_printf(a_str_out, "\t\t\t signing_addr: %s\n"
                                                        "\t\t\t with pkey hash %s\n"
                                                        "\t\t\t signer_node_addr: "NODE_ADDR_FP_STR"\n",
                                                        l_addr_str,
                                                        l_hash_str,
                                                        NODE_ADDR_FP_ARGS(l_signer_node_addr));
                    DAP_DELETE(l_addr_str);
                    DAP_DELETE(l_hash_str);
                } break;
                case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_XCHANGE: {
                    char *l_value_str = dap_chain_balance_print(((dap_chain_tx_out_cond_t*)item)->subtype.srv_xchange.buy_value);
                    char *l_coins_str = dap_chain_balance_to_coins(((dap_chain_tx_out_cond_t*)item)->subtype.srv_xchange.buy_value);
                    dap_string_append_printf(a_str_out, "\t\t\t net id: 0x%016"DAP_UINT64_FORMAT_x"\n"
                                                        "\t\t\t buy_token: %s\n"
                                                        "\t\t\t buy_value: %s (%s)\n",
                                             ((dap_chain_tx_out_cond_t*)item)->subtype.srv_xchange.buy_net_id.uint64,
                                             ((dap_chain_tx_out_cond_t*)item)->subtype.srv_xchange.buy_token,
                                             l_coins_str,
                                             l_value_str);
                    DAP_DELETE(l_value_str);
                    DAP_DELETE(l_coins_str);
                } break;
                case DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK: {
                    dap_time_t l_ts_exp = ((dap_chain_tx_out_cond_t*)item)->subtype.srv_stake_lock.time_unlock;
                    dap_string_append_printf(a_str_out, "\t\t\t time_unlock %s\n",
                                             dap_ctime_r(&l_ts_exp, l_tmp_buf));
                } break;
                default: break;
            }
        } break;
        case TX_ITEM_TYPE_OUT_EXT: {
            char *l_value_str = dap_chain_balance_print(((dap_chain_tx_out_ext_t*)item)->header.value);
            char *l_coins_str = dap_chain_balance_to_coins(((dap_chain_tx_out_ext_t*)item)->header.value);
            char *l_addr_str = dap_chain_addr_to_str(&((dap_chain_tx_out_ext_t*)item)->addr);
            dap_string_append_printf(a_str_out, "\t OUT EXT:\n"
                                                "\t\t Addr: %s\n"
                                                "\t\t Token: %s\n"
                                                "\t\t Value: %s (%s)\n",
                                     l_addr_str,
                                     ((dap_chain_tx_out_ext_t*)item)->token,
                                     l_coins_str,
                                     l_value_str);
            DAP_DELETE(l_addr_str);
            DAP_DELETE(l_value_str);
            DAP_DELETE(l_coins_str);
        } break;
        default:
            dap_string_append_printf(a_str_out, " This transaction have unknown item type \n");
            break;
        }
        l_tx_items_count += l_item_tx_size;
        // Freeze protection
        if(!l_item_tx_size)
        {
            break;
        }

    }
    dap_string_append_printf(a_str_out, "\n");
    return true;
}

/**
 * @brief dap_chain_net_dump_datum
 * process datum verification process. Can be:
 * if DAP_CHAIN_DATUM_TX, called dap_chain_ledger_tx_add_check
 * if DAP_CHAIN_DATUM_TOKEN_DECL, called dap_chain_ledger_token_decl_add_check
 * if DAP_CHAIN_DATUM_TOKEN_EMISSION, called dap_chain_ledger_token_emission_add_check
 * @param a_str_out
 * @param a_datum
 */
void dap_chain_datum_dump(dap_string_t *a_str_out, dap_chain_datum_t *a_datum, const char *a_hash_out_type, dap_chain_net_id_t a_net_id)
{
    if( a_datum == NULL){
        dap_string_append_printf(a_str_out,"==Datum is NULL\n");
        return;
    }
    dap_hash_fast_t l_datum_hash;
    dap_hash_fast(a_datum->data, a_datum->header.data_size, &l_datum_hash);
    char *l_hash_str = dap_strcmp(a_hash_out_type, "hex")
            ? dap_enc_base58_encode_hash_to_str(&l_datum_hash)
            : dap_chain_hash_fast_to_str_new(&l_datum_hash);
    switch (a_datum->header.type_id) {
        case DAP_CHAIN_DATUM_TOKEN_DECL: {
            size_t l_token_size = a_datum->header.data_size;
            dap_chain_datum_token_t * l_token = dap_chain_datum_token_read(a_datum->data, &l_token_size);
            if(l_token_size < sizeof(dap_chain_datum_token_t)){
                dap_string_append_printf(a_str_out,"==Datum has incorrect size. Only %zu, while at least %zu is expected\n",
                                         l_token_size, sizeof(dap_chain_datum_token_t));
                DAP_DEL_Z(l_token);
                return;
            }
            dap_string_append_printf(a_str_out,"=== Datum Token Declaration ===\n");
            dap_string_append_printf(a_str_out, "hash: %s\n", l_hash_str);
            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_string_append_printf(a_str_out, "version: %d\n", l_token->version);
            switch (l_token->type) {
                case DAP_CHAIN_DATUM_TOKEN_TYPE_DECL: {
                    dap_string_append(a_str_out,"type: DECL\n");
                    switch (l_token->subtype) {
                        case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PRIVATE:{
                            char *l_value_str = dap_chain_balance_print(l_token->total_supply);
                            dap_string_append(a_str_out,"subtype: PRIVATE\n");
                            dap_string_append_printf(a_str_out, "decimals: %d\n", l_token->header_private_decl.decimals);
                            dap_string_append_printf(a_str_out, "auth signs (valid/total) %u/%u\n", l_token->signs_valid, l_token->signs_total);
                            dap_string_append_printf(a_str_out, "total_supply: %s\n", l_value_str);
                            dap_string_append(a_str_out,"flags: ");
                            dap_chain_datum_token_flags_dump(a_str_out, l_token->header_private_update.flags);
                            s_datum_token_dump_tsd(a_str_out, l_token, l_token_size, a_hash_out_type);
                            size_t l_certs_field_size = l_token_size - sizeof(*l_token) - l_token->header_private_update.tsd_total_size;
                            dap_chain_datum_token_certs_dump(a_str_out, l_token->data_n_tsd + l_token->header_private_update.tsd_total_size,
                                                             l_certs_field_size, a_hash_out_type);
                            DAP_DEL_Z(l_value_str);
                        }break;
                        case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE: {
                            char *l_value_str = dap_chain_balance_print(l_token->total_supply);
                            dap_string_append(a_str_out, "subtype: CF20\n");
                            dap_string_append_printf(a_str_out, "decimals: %d\n", l_token->header_native_decl.decimals);
                            dap_string_append_printf(a_str_out, "auth signs (valid/total) %u/%u\n", l_token->signs_valid, l_token->signs_total);
                            dap_string_append_printf(a_str_out, "total_supply: %s\n", l_value_str);
                            dap_string_append(a_str_out, "flags: ");
                            dap_chain_datum_token_flags_dump(a_str_out, l_token->header_native_decl.flags);
                            s_datum_token_dump_tsd(a_str_out, l_token, l_token_size, a_hash_out_type);
                            size_t l_certs_field_size = l_token_size - sizeof(*l_token) - l_token->header_native_decl.tsd_total_size;
                            dap_chain_datum_token_certs_dump(a_str_out, l_token->data_n_tsd + l_token->header_native_decl.tsd_total_size,
                                                             l_certs_field_size, a_hash_out_type);
                            DAP_DEL_Z(l_value_str);
                        }break;
                        case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PUBLIC: {
                            char *l_premine_supply = dap_chain_balance_print(l_token->header_public.premine_supply);
                            dap_chain_addr_t l_premine_addr = l_token->header_public.premine_address;
                            char *l_premine_addr_str = dap_chain_addr_to_str(&l_premine_addr);
                            dap_string_append(a_str_out, "subtype: PUBLIC\n");
                            dap_string_append_printf(a_str_out, "premine_supply: %s", l_premine_supply);
                            dap_string_append_printf(a_str_out, "premine_address: %s", l_premine_addr_str);
                            dap_string_append(a_str_out, "flags: ");
                            dap_chain_datum_token_flags_dump(a_str_out, l_token->header_public.flags);
                            DAP_DELETE(l_premine_supply);
                            DAP_DELETE(l_premine_addr_str);
                        }break;
                    }
                }break;
                case DAP_CHAIN_DATUM_TOKEN_TYPE_UPDATE: {
                    dap_string_append(a_str_out,"type: UPDATE\n");
                    switch (l_token->subtype) {
                        case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PRIVATE: {
                            char *l_value_str = dap_chain_balance_print(l_token->total_supply);
                            dap_string_append(a_str_out,"subtype: PRIVATE\n");
                            dap_string_append_printf(a_str_out, "decimals: %d\n", l_token->header_private_decl.decimals);
                            dap_string_append_printf(a_str_out, "auth signs (valid/total) %u/%u\n", l_token->signs_valid, l_token->signs_total);
                            dap_string_append_printf(a_str_out, "total_supply: %s\n", l_value_str);
                            dap_string_append(a_str_out,"flags: ");
                            dap_chain_datum_token_flags_dump(a_str_out, l_token->header_private_update.flags);
                            s_datum_token_dump_tsd(a_str_out, l_token, l_token_size, a_hash_out_type);
                            size_t l_certs_field_size = l_token_size - sizeof(*l_token) - l_token->header_private_update.tsd_total_size;
                            dap_chain_datum_token_certs_dump(a_str_out, l_token->data_n_tsd + l_token->header_private_update.tsd_total_size,
                                                             l_certs_field_size, a_hash_out_type);
                            DAP_DEL_Z(l_value_str);
                        }break;
                        case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE: {
                            char *l_value_str = dap_chain_balance_print(l_token->total_supply);
                            dap_string_append_printf(a_str_out,"subtype: CF20\n");
                            dap_string_append_printf(a_str_out, "decimals: %d\n", l_token->header_native_update.decimals);
                            dap_string_append_printf(a_str_out, "auth signs (valid/total) %u/%u\n", l_token->signs_valid, l_token->signs_total);
                            dap_string_append_printf(a_str_out, "total_supply: %s\n", l_value_str);
                            dap_string_append(a_str_out, "flags: ");
                            dap_chain_datum_token_flags_dump(a_str_out, l_token->header_native_update.flags);
                            s_datum_token_dump_tsd(a_str_out, l_token, l_token_size, a_hash_out_type);
                            size_t l_certs_field_size = l_token_size - sizeof(*l_token) - l_token->header_native_update.tsd_total_size;
                            dap_chain_datum_token_certs_dump(a_str_out, l_token->data_n_tsd + l_token->header_native_update.tsd_total_size,
                                                             l_certs_field_size, a_hash_out_type);
                            DAP_DEL_Z(l_value_str);
                        }break;
                    }
                }break;
                default:
                    dap_string_append(a_str_out,"type: UNKNOWN\n");
                    break;
            }
            if (l_token->subtype == DAP_CHAIN_DATUM_TOKEN_SUBTYPE_SIMPLE ) {
                char *l_value_str = dap_chain_balance_print(l_token->total_supply);
                dap_string_append(a_str_out, "subtype: SIMPLE\n");
                dap_string_append_printf(a_str_out, "decimals: %d\n", l_token->header_simple.decimals);
                dap_string_append_printf(a_str_out, "sign_total: %hu\n", l_token->signs_total );
                dap_string_append_printf(a_str_out, "sign_valid: %hu\n", l_token->signs_valid );
                dap_string_append_printf(a_str_out, "total_supply: %s\n", l_value_str);
                size_t l_certs_field_size = l_token_size - sizeof(*l_token);
                dap_chain_datum_token_certs_dump(a_str_out, l_token->data_n_tsd,
                                                 l_certs_field_size, a_hash_out_type);
                DAP_DEL_Z(l_value_str);
            }
            DAP_DELETE(l_token);
        } break;
        case DAP_CHAIN_DATUM_TOKEN_EMISSION: {
            size_t l_emisssion_size = a_datum->header.data_size;
            dap_chain_datum_token_emission_t *l_emission = dap_chain_datum_emission_read(a_datum->data, &l_emisssion_size);
            char *l_value_str = dap_chain_balance_print(l_emission->hdr.value_256);
            char *l_coins_str = dap_chain_balance_to_coins(l_emission->hdr.value_256);
            char *l_addr_str = dap_chain_addr_to_str(&(l_emission->hdr.address));
            dap_string_append_printf(a_str_out, "emission: hash %s\n\t%s(%s) %s, type: %s, version: %d\n",
                                    l_hash_str,
                                    l_coins_str,
                                    l_value_str,
                                    l_emission->hdr.ticker,
                                    c_dap_chain_datum_token_emission_type_str[l_emission->hdr.type],
                                    l_emission->hdr.version);
            dap_string_append_printf(a_str_out, "  to addr: %s\n", l_addr_str);
            DAP_DELETE(l_value_str);
            DAP_DELETE(l_coins_str);
            DAP_DELETE(l_addr_str);
            switch (l_emission->hdr.type) {
                case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_UNDEFINED:
                    break;
                case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_AUTH:
                    dap_string_append_printf(a_str_out, "  signs_count: %d\n", l_emission->data.type_auth.signs_count);
                    dap_string_append_printf(a_str_out, "  tsd_total_size: %"DAP_UINT64_FORMAT_U"\n",
                                             l_emission->data.type_auth.tsd_total_size);

                    if (  ( (void *) l_emission->tsd_n_signs + l_emission->data.type_auth.tsd_total_size) >
                          ((void *) l_emission + l_emisssion_size) )
                    {
                        log_it(L_ERROR, "Illformed DATUM type %d, TSD section is out-of-buffer (%" DAP_UINT64_FORMAT_U " vs %zu)",
                            l_emission->hdr.type, l_emission->data.type_auth.tsd_total_size, l_emisssion_size);

                        dap_string_append_printf(a_str_out, "  Skip incorrect or illformed DATUM");
                        break;
                    }
                    dap_chain_datum_token_certs_dump(a_str_out, l_emission->tsd_n_signs + l_emission->data.type_auth.tsd_total_size,
                                                    l_emission->data.type_auth.size - l_emission->data.type_auth.tsd_total_size, a_hash_out_type);
                    break;
                case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_ALGO:
                    dap_string_append_printf(a_str_out, "  codename: %s\n", l_emission->data.type_algo.codename);
                    break;
                case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_ATOM_OWNER:
                    dap_string_append_printf(a_str_out, " value_start: %.0Lf(%"DAP_UINT64_FORMAT_U"), codename: %s\n",
                        dap_chain_datoshi_to_coins(l_emission->data.type_atom_owner.value_start),
                        l_emission->data.type_atom_owner.value_start,
                        l_emission->data.type_atom_owner.value_change_algo_codename
                    );
                break;
                case DAP_CHAIN_DATUM_TOKEN_EMISSION_TYPE_SMART_CONTRACT: {
                    char l_time_str[70];
                    char *l_addr = dap_chain_addr_to_str(&l_emission->data.type_presale.addr);
                    // get time of create datum
                    if(dap_time_to_str_rfc822(l_time_str, 71, l_emission->data.type_presale.lock_time) < 1)
                            l_time_str[0] = '\0';
                    dap_string_append_printf(a_str_out, "  flags: 0x%x, lock_time: %s\n", l_emission->data.type_presale.flags, l_time_str);
                    dap_string_append_printf(a_str_out, "  addr: %s\n", l_addr);
                    DAP_DELETE(l_addr);
                }
                break;
            }
            DAP_DELETE(l_emission);
        } break;
        case DAP_CHAIN_DATUM_TX: {
            dap_chain_datum_tx_t *l_tx = (dap_chain_datum_tx_t *)a_datum->data;
            dap_chain_datum_dump_tx(l_tx, NULL, a_str_out, a_hash_out_type, &l_datum_hash, a_net_id);
        } break;
        case DAP_CHAIN_DATUM_DECREE:{
            dap_chain_datum_decree_t *l_decree = (dap_chain_datum_decree_t *)a_datum->data;
            size_t l_decree_size = dap_chain_datum_decree_get_size(l_decree);
            dap_string_append_printf(a_str_out,"=== Datum decree ===\n");
            dap_string_append_printf(a_str_out, "hash: %s\n", l_hash_str);
            dap_string_append_printf(a_str_out, "size: %zd\n", l_decree_size);

            dap_chain_datum_decree_dump(a_str_out, l_decree, l_decree_size, a_hash_out_type);
        } break;
        case DAP_CHAIN_DATUM_ANCHOR:{
            dap_chain_datum_anchor_t *l_anchor = (dap_chain_datum_anchor_t *)a_datum->data;
            size_t l_anchor_size = sizeof(dap_chain_datum_anchor_t) + l_anchor->header.data_size + l_anchor->header.signs_size;
            dap_string_append_printf(a_str_out,"=== Datum anchor ===\n");
            dap_string_append_printf(a_str_out, "hash: %s\n", l_hash_str);
            dap_string_append_printf(a_str_out, "size: %zd\n", l_anchor_size);
            dap_hash_fast_t l_decree_hash = { };
            dap_chain_datum_anchor_get_hash_from_data(l_anchor, &l_decree_hash);
            //dap_chain_hash_fast_to_str(&l_decree_hash, l_decree_hash_str, 40);
            char l_decree_hash_str[DAP_CHAIN_HASH_FAST_STR_SIZE];
            dap_chain_hash_fast_to_str(&l_decree_hash, l_decree_hash_str, DAP_CHAIN_HASH_FAST_STR_SIZE);
            dap_string_append_printf(a_str_out, "decree hash: %s\n", l_decree_hash_str);
            dap_chain_datum_anchor_certs_dump(a_str_out, l_anchor->data_n_sign + l_anchor->header.data_size, l_anchor->header.signs_size, a_hash_out_type);
        } break;
    }    
    DAP_DELETE(l_hash_str);
}

json_object * dap_chain_datum_to_json(dap_chain_datum_t* a_datum){
    json_object *l_object = json_object_new_object();
    char *l_hash_data_str;
    dap_get_data_hash_str_static(a_datum->data, a_datum->header.data_size, l_hash_data_str);
    json_object *l_obj_data_hash = json_object_new_string(l_hash_data_str);
    json_object *l_obj_version = json_object_new_int(a_datum->header.version_id);
    json_object *l_obj_size = json_object_new_int(a_datum->header.data_size);
    json_object *l_obj_ts_created = json_object_new_uint64(a_datum->header.ts_create);
    json_object *l_obj_type = json_object_new_string(dap_chain_datum_type_id_to_str(a_datum->header.type_id));
    json_object *l_obj_data;
    switch (a_datum->header.type_id) {
        case DAP_CHAIN_DATUM_TX:
            l_obj_data = dap_chain_datum_tx_to_json((dap_chain_datum_tx_t*)a_datum->data);
            break;
        default:
            l_obj_data = json_object_new_null();
            break;
    }
    json_object_object_add(l_object, "version", l_obj_version);
    json_object_object_add(l_object, "hash", l_obj_data_hash);
    json_object_object_add(l_object, "data_size", l_obj_size);
    json_object_object_add(l_object, "ts_created", l_obj_ts_created);
    json_object_object_add(l_object, "type", l_obj_type);
    json_object_object_add(l_object, "data", l_obj_data);
    return l_object;
}