/*
 * Authors:
 * Frolov Daniil <daniil.frolov@demlabs.net>
 * DeM Labs Inc.   https://demlabs.net
 * Copyright  (c) 2020, 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 <memory.h>
#include <assert.h>
#include "dap_tsd.h"
#include "dap_sign.h"
#include "dap_common.h"
#include "dap_chain_datum_decree.h"
#include "dap_enc_base58.h"
#include "dap_chain_common.h"
#include "dap_chain_policy.h"
#ifdef DAP_OS_UNIX
#include <arpa/inet.h>
#endif
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#endif


#define LOG_TAG "dap_chain_datum_decree"

dap_sign_t *dap_chain_datum_decree_get_signs(dap_chain_datum_decree_t *a_decree, size_t* a_signs_size)
{
    dap_return_val_if_fail(a_decree && a_signs_size, NULL);
    dap_sign_t *l_signs_section = (dap_sign_t*)(a_decree->data_n_signs + a_decree->header.data_size);
    *a_signs_size = a_decree->header.signs_size;
    return l_signs_section;
}

static bool s_find_pkey(dap_chain_datum_decree_t *a_decree, dap_pkey_t *a_pkey)
{
    dap_return_val_if_pass(!a_decree || !a_pkey || !a_pkey->header.size, false);
    dap_sign_t *l_signs_section = (dap_sign_t*)(a_decree->data_n_signs + a_decree->header.data_size);
    size_t l_sign_size = 0;
    bool l_ret = false;
    for (uint64_t l_offset = 0; !l_ret && l_offset + sizeof(dap_sign_t) < a_decree->header.signs_size; l_offset += l_sign_size) {
        dap_sign_t *l_sign = (dap_sign_t *)(a_decree->data_n_signs + a_decree->header.data_size + l_offset);
        l_sign_size = dap_sign_get_size(l_sign);
        if (l_offset + l_sign_size <= l_offset || l_offset + l_sign_size > a_decree->header.signs_size)
            break;
        size_t l_pkey_ser_size = 0;
        const uint8_t *l_pkey_ser = dap_sign_get_pkey(l_sign, &l_pkey_ser_size);
        l_ret = (l_pkey_ser_size == a_pkey->header.size) && !memcmp(l_pkey_ser, a_pkey->pkey, l_pkey_ser_size);
    }
    return l_ret;
}

int dap_chain_datum_decree_get_fee(dap_chain_datum_decree_t *a_decree, uint256_t *a_fee_value)
{
    dap_return_val_if_fail(a_decree && a_fee_value, -1);
    dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_FEE);
    return l_tsd && l_tsd->size == sizeof(uint256_t) ? ( _dap_tsd_get_scalar(l_tsd, a_fee_value), 0 ) : 1;
}

int dap_chain_datum_decree_get_value(dap_chain_datum_decree_t *a_decree, uint256_t *a_value)
{
    dap_return_val_if_fail(a_decree && a_value, -1);
    dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_VALUE);
    return l_tsd && l_tsd->size == sizeof(uint256_t) ? ( _dap_tsd_get_scalar(l_tsd, a_value), 0 ) : 1;
}

int dap_chain_datum_decree_get_fee_addr(dap_chain_datum_decree_t *a_decree, dap_chain_addr_t *a_fee_wallet)
{
    dap_return_val_if_fail(a_decree && a_fee_wallet, -1);
    dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_FEE_WALLET);
    return l_tsd && l_tsd->size == sizeof(dap_chain_addr_t) ? ( _dap_tsd_get_scalar(l_tsd, a_fee_wallet), 0 ) : 1;
}

dap_list_t *dap_chain_datum_decree_get_owners(dap_chain_datum_decree_t *a_decree, uint16_t *a_owners_num)
{
    dap_return_val_if_fail(a_decree && a_owners_num, NULL);
    dap_list_t *l_ret = dap_tsd_find_all(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_OWNER, 0);
    dap_list_t *it, *tmp;
    DL_FOREACH_SAFE(l_ret, it, tmp) {
        dap_tsd_t *l_tsd = it->data;
        if (l_tsd->size < sizeof(dap_pkey_t) || l_tsd->size != sizeof(dap_pkey_t) + ((dap_pkey_t *)l_tsd->data)->header.size) {
            log_it(L_ERROR, "Incorrect size %u of owner pkey", l_tsd->size);
            DL_DELETE(l_ret, it);
            DAP_DEL_MULTY(it->data, it);
        }
    }
    if (a_owners_num)
        *a_owners_num = (uint16_t)dap_list_length(l_ret);
    return l_ret;
}

int dap_chain_datum_decree_get_hardfork_changed_addrs(dap_chain_datum_decree_t *a_decree, json_object **a_json_obj)
{
    dap_return_val_if_fail(a_decree && a_json_obj, -1);
    dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_HARDFORK_CHANGED_ADDRS);
    return l_tsd ? (!dap_strcmp(dap_tsd_get_string_const(l_tsd), DAP_TSD_CORRUPTED_STRING) ? (*a_json_obj = json_tokener_parse(dap_tsd_get_string_const(l_tsd)), 0) :  1)  : 1;
}

int dap_chain_datum_decree_get_min_owners(dap_chain_datum_decree_t *a_decree, uint256_t *a_min_owners_num)
{
    dap_return_val_if_fail(a_decree && a_min_owners_num, -1);
    dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_MIN_OWNER);
    return l_tsd && l_tsd->size == sizeof(uint256_t) ? ( _dap_tsd_get_scalar(l_tsd, a_min_owners_num), 0 ) : 1;
}

int dap_chain_datum_decree_get_hash(dap_chain_datum_decree_t *a_decree, dap_hash_fast_t *a_tx_hash)
{
    dap_return_val_if_fail(a_decree && a_tx_hash, -1);
    dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_HASH);
    return l_tsd && l_tsd->size == sizeof(dap_hash_fast_t) ? ( _dap_tsd_get_scalar(l_tsd, a_tx_hash), 0 ) : 1;
}

int dap_chain_datum_decree_get_stake_value(dap_chain_datum_decree_t *a_decree, uint256_t *a_stake_value)
{
    dap_return_val_if_fail(a_decree && a_stake_value, -1);
    dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_STAKE_VALUE);
    return l_tsd && l_tsd->size == sizeof(uint256_t) ? ( _dap_tsd_get_scalar(l_tsd, a_stake_value), 0 ) : 1;
}

int dap_chain_datum_decree_get_stake_signing_addr(dap_chain_datum_decree_t *a_decree, dap_chain_addr_t *a_signing_addr)
{
    dap_return_val_if_fail(a_decree && a_signing_addr, -1);
    dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_STAKE_SIGNING_ADDR);
    return l_tsd && l_tsd->size == sizeof(dap_chain_addr_t) ? ( _dap_tsd_get_scalar(l_tsd, a_signing_addr), 0 ) : 1;
}

int dap_chain_datum_decree_get_node_addr(dap_chain_datum_decree_t *a_decree, dap_chain_node_addr_t *a_node_addr)
{
    dap_return_val_if_fail(a_decree && a_node_addr, -1);
    dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_NODE_ADDR);
    return l_tsd && l_tsd->size == sizeof(dap_chain_node_addr_t) ? ( _dap_tsd_get_scalar(l_tsd, a_node_addr), 0 ) : 1;
}

int dap_chain_datum_decree_get_stake_min_value(dap_chain_datum_decree_t *a_decree, uint256_t *a_min_value)
{
    dap_return_val_if_fail(a_decree && a_min_value, -1);
    dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_STAKE_MIN_VALUE);
    return l_tsd && l_tsd->size == sizeof(uint256_t) ? ( _dap_tsd_get_scalar(l_tsd, a_min_value), 0 ) : 1;
}

int dap_chain_datum_decree_get_stake_min_signers_count(dap_chain_datum_decree_t *a_decree, uint256_t *a_min_signers_count)
{
    dap_return_val_if_fail(a_decree && a_min_signers_count, -1);
    dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_STAKE_MIN_SIGNERS_COUNT);
    return l_tsd && l_tsd->size == sizeof(uint256_t) ? ( _dap_tsd_get_scalar(l_tsd, a_min_signers_count), 0 ) : 1;
}

int dap_chain_datum_decree_get_action(dap_chain_datum_decree_t *a_decree, uint8_t *a_action)
{
    dap_return_val_if_fail(a_decree && a_action, -1);
    dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_ACTION);
    return l_tsd && l_tsd->size == sizeof(uint8_t) ? ( _dap_tsd_get_scalar(l_tsd, a_action), 0 ) : 1;
}

int dap_chain_datum_decree_get_signature_type(dap_chain_datum_decree_t *a_decree, uint32_t *a_signature_type)
{
    dap_return_val_if_fail(a_decree && a_signature_type, -1);
    dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_SIGNATURE_TYPE);
    return l_tsd && l_tsd->size == sizeof(uint32_t) ? ( _dap_tsd_get_scalar(l_tsd, a_signature_type), 0 ) : 1;
}

int dap_chain_datum_decree_get_ban_addr(dap_chain_datum_decree_t *a_decree, const char **a_addr)
{
    dap_return_val_if_fail(a_decree && a_addr, -1);
    dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_HOST);
    if (!l_tsd)
        l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_STRING);
    return l_tsd ? ( *a_addr = dap_tsd_get_string_const(l_tsd), !dap_strcmp(*a_addr, DAP_TSD_CORRUPTED_STRING) ) : 1;
}

int dap_chain_datum_decree_get_atom_num(dap_chain_datum_decree_t *a_decree, uint64_t *a_atom_num)
{
    dap_return_val_if_fail(a_decree && a_atom_num, -1);
    dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_BLOCK_NUM);
    return l_tsd && l_tsd->size == sizeof(uint64_t) ? ( _dap_tsd_get_scalar(l_tsd, a_atom_num), 0 ) : 1;
}

dap_pkey_t *dap_chain_datum_decree_get_pkey(dap_chain_datum_decree_t *a_decree)
{
    dap_return_val_if_fail(a_decree, NULL);
    dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_STAKE_PKEY);
    return (l_tsd && dap_pkey_get_size((dap_pkey_t *)l_tsd->data) == l_tsd->size) ? (dap_pkey_t *)l_tsd->data : NULL;
}

dap_chain_policy_t *dap_chain_datum_decree_get_policy(dap_chain_datum_decree_t *a_decree)
{
    dap_return_val_if_fail(a_decree, NULL);
    dap_tsd_t *l_tsd = dap_tsd_find(a_decree->data_n_signs, a_decree->header.data_size, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_POLICY_EXECUTE);
    return (l_tsd  && dap_chain_policy_get_size((dap_chain_policy_t *)l_tsd->data) == l_tsd->size) ? (dap_chain_policy_t *)l_tsd->data : NULL;
}

void dap_chain_datum_decree_dump_json(json_object *a_json_out, dap_chain_datum_decree_t *a_decree, size_t a_decree_size, const char *a_hash_out_type)
{
    char *l_type_str;
    switch(a_decree->header.type)
    {
        case DAP_CHAIN_DATUM_DECREE_TYPE_COMMON:
            l_type_str = "DECREE_TYPE_COMMON";
            break;
        case DAP_CHAIN_DATUM_DECREE_TYPE_SERVICE:
            l_type_str = "DECREE_TYPE_SERVICE";
            break;
        default:
            l_type_str = "DECREE_TYPE_UNKNOWN";
    }
    json_object_object_add(a_json_out, "type", json_object_new_string(l_type_str));
    const char *l_subtype_str = dap_chain_datum_decree_subtype_to_str(a_decree->header.sub_type);
    json_object_object_add(a_json_out, "subtype", json_object_new_string(l_subtype_str));
    json_object_object_add(a_json_out, "TSD", json_object_new_string(""));
    dap_tsd_t *l_tsd; size_t l_tsd_size;
    dap_tsd_iter(l_tsd, l_tsd_size, a_decree->data_n_signs, a_decree->header.data_size) {
        switch(l_tsd->type) {
        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_VALUE:
            if (l_tsd->size > sizeof(uint256_t)){
                json_object_object_add(a_json_out, "value", json_object_new_string("WRONG SIZE"));
                break;
            }
            uint256_t l_value = uint256_0;
            _dap_tsd_get_scalar(l_tsd, &l_value);
            const char *l_value_str = dap_uint256_to_char(l_value, NULL);
            json_object_object_add(a_json_out, "value", json_object_new_string(l_value_str));
            break;
        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_SIGN:
        break;
        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_FEE:
            if (l_tsd->size > sizeof(uint256_t)){
                json_object_object_add(a_json_out, "fee", json_object_new_string("WRONG SIZE"));
                break;
            }
            uint256_t l_fee_value = uint256_0;
            _dap_tsd_get_scalar(l_tsd, &l_fee_value);
            const char *l_fee_value_str = dap_uint256_to_char(l_fee_value, NULL);
            json_object_object_add(a_json_out, "fee", json_object_new_string(l_fee_value_str));
            break;
        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_OWNER:
            if (l_tsd->size < sizeof(dap_pkey_t)) {
                json_object_object_add(a_json_out, "owner_fingerprint", json_object_new_string("WRONG SIZE"));
                break;
            }
            dap_pkey_t *l_owner_pkey = /*DAP_NEW_STACK_SIZE(dap_pkey_t, l_tsd->size);
            memcpy(l_owner_pkey, l_tsd->data, l_tsd->size);*/ _dap_tsd_get_object(l_tsd, dap_pkey_t);
            json_object_object_add(a_json_out, "owner_fingerprint", json_object_new_string(dap_get_data_hash_str(l_owner_pkey->pkey, l_owner_pkey->header.size).s));
            break;
        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_MIN_OWNER:
            if (l_tsd->size > sizeof(uint256_t)){
                json_object_object_add(a_json_out, "owner_min", json_object_new_string("WRONG SIZE"));
                break;
            }
            uint256_t l_owner_min = uint256_0;
            _dap_tsd_get_scalar(l_tsd, &l_owner_min);
            const char *l_owner_min_str = dap_uint256_to_char(l_owner_min, NULL);
            json_object_object_add(a_json_out, "owner_min", json_object_new_string(l_owner_min_str));
            break;
        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_FEE_WALLET:
            if (l_tsd->size > sizeof(dap_chain_addr_t)) {
                json_object_object_add(a_json_out, "Wallet for fee", json_object_new_string("WRONG SIZE"));
                break;
            }
            dap_chain_addr_t *l_addr_fee_wallet = /*{ };
            _dap_tsd_get_scalar(l_tsd, &l_addr_fee_wallet);*/ _dap_tsd_get_object(l_tsd, dap_chain_addr_t);
            json_object_object_add(a_json_out, "wallet_for_fee", json_object_new_string(dap_chain_addr_to_str_static(l_addr_fee_wallet)));
        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_HASH:
            if (l_tsd->size > sizeof(dap_hash_fast_t)) {
                json_object_object_add(a_json_out, "stake_tx", json_object_new_string("WRONG SIZE"));
                break;
            }
            dap_hash_fast_t *l_stake_tx = /*{ };
            _dap_tsd_get_scalar(l_tsd, &l_stake_tx);*/ _dap_tsd_get_object(l_tsd, dap_hash_fast_t);
            const char *l_stake_tx_hash = dap_strcmp(a_hash_out_type, "hex")
                    ? dap_enc_base58_encode_hash_to_str_static(l_stake_tx)
                    : dap_chain_hash_fast_to_str_static(l_stake_tx);
            json_object_object_add(a_json_out, "stake_tx", json_object_new_string(l_stake_tx_hash));
            break;
        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_STAKE_VALUE:
            if (l_tsd->size > sizeof(uint256_t)){
                json_object_object_add(a_json_out, "stake_value", json_object_new_string("WRONG SIZE"));
                break;
            }
            uint256_t l_stake_value = uint256_0;
            _dap_tsd_get_scalar(l_tsd, &l_stake_value);
            const char *l_stake_value_str = dap_uint256_to_char(l_stake_value, NULL);
            json_object_object_add(a_json_out, "stake_value", json_object_new_string(l_stake_value_str));
            break;
       case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_STAKE_SIGNING_ADDR:
            if (l_tsd->size > sizeof(dap_chain_addr_t)) {
                json_object_object_add(a_json_out, "signing_addr", json_object_new_string("WRONG SIZE"));
                break;
            }
            dap_chain_addr_t *l_stake_addr_signing = /*{ };
            _dap_tsd_get_scalar(l_tsd, &l_stake_addr_signing);*/ _dap_tsd_get_object(l_tsd, dap_chain_addr_t);
            json_object_object_add(a_json_out, "signing_addr", json_object_new_string(dap_chain_addr_to_str_static(l_stake_addr_signing)));
            dap_chain_hash_fast_t l_pkey_signing = l_stake_addr_signing->data.hash_fast;
            const char *l_pkey_signing_str = dap_strcmp(a_hash_out_type, "hex")
                    ? dap_enc_base58_encode_hash_to_str_static(&l_pkey_signing)
                    : dap_chain_hash_fast_to_str_static(&l_pkey_signing);
            json_object_object_add(a_json_out, "signing_pkey_fingerprint", json_object_new_string(l_pkey_signing_str));
            break;
        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_NODE_ADDR:
            if(l_tsd->size > sizeof(dap_chain_node_addr_t)){
                json_object_object_add(a_json_out, "node_addr", json_object_new_string("WRONG SIZE"));
                break;
            }
            dap_chain_node_addr_t *l_node_addr = _dap_tsd_get_object(l_tsd, dap_chain_node_addr_t);
            char l_buf[24];
            snprintf(l_buf, sizeof(l_buf), NODE_ADDR_FP_STR, NODE_ADDR_FP_ARGS(l_node_addr));
            json_object_object_add(a_json_out, "node_addr", json_object_new_string(l_buf));
            break;
        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_STAKE_MIN_VALUE:
            if (l_tsd->size > sizeof(uint256_t)) {
                json_object_object_add(a_json_out, "min_value", json_object_new_string("WRONG SIZE"));
                break;
            }
            uint256_t l_min_value = uint256_0;
            _dap_tsd_get_scalar(l_tsd, &l_min_value);
            const char *l_min_value_str = dap_uint256_to_char(l_min_value, NULL);
            json_object_object_add(a_json_out, "min_value", json_object_new_string(l_min_value_str));
            break;
        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_STAKE_MIN_SIGNERS_COUNT:
            if (l_tsd->size > sizeof(uint256_t)) {
                json_object_object_add(a_json_out, "min_signers_count", json_object_new_string("WRONG SIZE"));
                break;
            }
            uint256_t l_min_signers_count = uint256_0;
            _dap_tsd_get_scalar(l_tsd, &l_min_signers_count);
            const char *l_min_signers_count_str = dap_uint256_to_char(l_min_signers_count, NULL);
            json_object_object_add(a_json_out, "min_signers_count", json_object_new_string(l_min_signers_count_str));
            break;
        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_HOST:
        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_STRING:
            json_object_object_add(a_json_out, "host_address", json_object_new_string(dap_tsd_get_string(l_tsd)));
            break;
        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_ACTION:
            if (l_tsd->size != sizeof(uint8_t)) {
                json_object_object_add(a_json_out, "action", json_object_new_string("WRONG SIZE"));
                break;
            }
            uint8_t l_action = 0;
            _dap_tsd_get_scalar(l_tsd, &l_action);
            json_object_object_add(a_json_out, "action", l_action ?
                                        json_object_new_string("add (enable)") : json_object_new_string("delete (disable)"));
            break;
        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_SIGNATURE_TYPE:
            if (l_tsd->size != sizeof(uint32_t)) {
                json_object_object_add(a_json_out, "signature_type", json_object_new_string("WRONG SIZE"));
                break;
            }
            uint32_t l_type = 0;
            _dap_tsd_get_scalar(l_tsd, &l_type);
            dap_sign_type_t l_sign_type = { .type = l_type };
            json_object_object_add(a_json_out, "signature_type", json_object_new_string(dap_sign_type_to_str(l_sign_type)));
            break;
        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_STAKE_PKEY:
            if (l_tsd->size != dap_pkey_get_size((dap_pkey_t *)(l_tsd->data))) {
                json_object_object_add(a_json_out, "pkey_type", json_object_new_string("WRONG SIZE"));
                break;
            }
            json_object_object_add(a_json_out, "pkey_type", json_object_new_string( dap_pkey_type_to_str(((dap_pkey_t *)(l_tsd->data))->header.type) ));
            break;
        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_BLOCK_NUM:
            if (l_tsd->size != sizeof(uint64_t)) {
                json_object_object_add(a_json_out, "signature_type", json_object_new_string("WRONG SIZE"));
                break;
            }
            uint64_t l_num = 0;
            _dap_tsd_get_scalar(l_tsd, &l_type);
            json_object_object_add(a_json_out, "signature_type", json_object_new_uint64(l_num));
            break;

        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_HARDFORK_CHANGED_ADDRS:
            if (l_tsd->size != sizeof(uint64_t)) {
                json_object_object_add(a_json_out, "wallet_addr_pair", json_object_new_string("WRONG SIZE"));
                break;
            }
            json_object* l_json_obj = NULL;
            if (!dap_strcmp(dap_tsd_get_string_const(l_tsd), DAP_TSD_CORRUPTED_STRING)) { 
                l_json_obj = json_tokener_parse(dap_tsd_get_string_const(l_tsd));
            } else {
                l_json_obj = json_object_new_string("Can't parse json in Wallet_addr_pair");
            }
            json_object_object_add(a_json_out, "wallet_addr_pair", l_json_obj);

        case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_POLICY_EXECUTE:
            if (l_tsd->size != dap_chain_policy_get_size((dap_chain_policy_t *)(l_tsd->data))) {
                json_object_object_add(a_json_out, "policy_type", json_object_new_string("WRONG SIZE"));
                break;
            }
            json_object_object_add(a_json_out, "policy_type", json_object_new_string( dap_chain_policy_to_str((dap_chain_policy_t *)(l_tsd->data))));
            break;

        default:
            json_object_object_add(a_json_out, "UNKNOWN_TYPE_TSD_SECTION", json_object_new_string(""));
            break;
        }
    }
    dap_chain_datum_decree_certs_dump_json(a_json_out, a_decree->data_n_signs + a_decree->header.data_size,
                                      a_decree->header.signs_size, a_hash_out_type);
}

void dap_chain_datum_decree_certs_dump_json(json_object * a_json_out, byte_t * a_signs, size_t a_certs_size, const char *a_hash_out_type)
{
    if (!a_certs_size) {
        json_object_object_add(a_json_out, "cert_status", json_object_new_string("NONE"));
        return;
    }
    json_object* json_arr_certs_out = json_object_new_array();
    size_t l_offset = 0;
    for (int i = 1; l_offset < (a_certs_size); i++) {
        json_object* json_obj_sign = json_object_new_object();
        dap_sign_t *l_sign = (dap_sign_t *) (a_signs + l_offset);
        l_offset += dap_sign_get_size(l_sign);
        if (l_sign->header.sign_size == 0) {
            json_object_object_add(json_obj_sign, "sign_status", json_object_new_string("CORRUPTED - 0 size signature"));
            json_object_array_add(json_arr_certs_out, json_obj_sign);
            continue;
        }

        dap_chain_hash_fast_t l_pkey_hash = {0};
        if (dap_sign_get_pkey_hash(l_sign, &l_pkey_hash) == false) {
            json_object_object_add(json_obj_sign, "sign_status", json_object_new_string("CORRUPTED - can't calc hash"));
            json_object_array_add(json_arr_certs_out, json_obj_sign);
            continue;
        }

        const char *l_hash_str = dap_strcmp(a_hash_out_type, "hex")
                ? dap_enc_base58_encode_hash_to_str_static(&l_pkey_hash)
                : dap_chain_hash_fast_to_str_static(&l_pkey_hash);
        json_object_object_add(json_obj_sign, "sign_#", json_object_new_uint64(i));
        json_object_object_add(json_obj_sign, "hash", json_object_new_string(l_hash_str));
        json_object_object_add(json_obj_sign, "type", json_object_new_string(dap_sign_type_to_str(l_sign->header.type)));
        json_object_object_add(json_obj_sign, "sign_size", json_object_new_uint64(l_sign->header.sign_size));
        json_object_array_add(json_arr_certs_out, json_obj_sign);        
    }
    json_object_object_add(a_json_out,"SIGNS", json_arr_certs_out);
}

dap_chain_datum_decree_t *dap_chain_datum_decree_new(dap_chain_net_id_t a_net_id, dap_chain_id_t a_chain_id,
                                                     dap_chain_cell_id_t a_cell_id, size_t a_total_tsd_size)
{
    dap_chain_datum_decree_t *l_decree = DAP_NEW_Z_SIZE_RET_VAL_IF_FAIL(dap_chain_datum_decree_t, sizeof(dap_chain_datum_decree_t) + a_total_tsd_size, NULL);

    l_decree->decree_version = DAP_CHAIN_DATUM_DECREE_VERSION;
    l_decree->header.ts_created = dap_time_now();
    l_decree->header.type = DAP_CHAIN_DATUM_DECREE_TYPE_COMMON;
    l_decree->header.common_decree_params.net_id = a_net_id;
    l_decree->header.common_decree_params.chain_id = a_chain_id;
    l_decree->header.common_decree_params.cell_id = a_cell_id;
    l_decree->header.data_size = a_total_tsd_size;
    return l_decree;
}

dap_chain_datum_decree_t *dap_chain_datum_decree_sign_in_cycle(dap_cert_t **a_certs, dap_chain_datum_decree_t *a_datum_decree,
                                                  size_t a_certs_count, size_t *a_total_sign_count)
{
    size_t l_cur_sign_offset = a_datum_decree->header.data_size + a_datum_decree->header.signs_size;
    size_t l_total_signs_size = a_datum_decree->header.signs_size, l_total_sign_count = 0;
    for(size_t i = 0; i < a_certs_count; i++) {
        dap_pkey_t *l_cur_pkey = dap_cert_to_pkey(a_certs[i]);
        if (s_find_pkey(a_datum_decree, l_cur_pkey)) {
            dap_chain_hash_fast_t l_pkey_hash = { };
            dap_pkey_get_hash(l_cur_pkey, &l_pkey_hash);
            log_it(L_ERROR, "Sign with %s pkey already exist in decree", dap_hash_fast_to_str_static(&l_pkey_hash));
            DAP_DELETE(l_cur_pkey);
            continue;;
        }
        DAP_DELETE(l_cur_pkey);
        dap_sign_t *l_sign = dap_cert_sign(a_certs[i], a_datum_decree,
                                            sizeof(dap_chain_datum_decree_t) + a_datum_decree->header.data_size);
        if (!l_sign) {
            log_it(L_ERROR, "Decree signing failed");
            DAP_DELETE(a_datum_decree);
            return NULL;
        }
        size_t l_sign_size = dap_sign_get_size(l_sign);
        dap_chain_datum_decree_t *l_datum_decree = DAP_REALLOC_RET_VAL_IF_FAIL(
            a_datum_decree, sizeof(dap_chain_datum_decree_t) + l_cur_sign_offset + l_sign_size, NULL, l_sign);
        a_datum_decree = l_datum_decree;
        memcpy(a_datum_decree->data_n_signs + l_cur_sign_offset, l_sign, l_sign_size);
        DAP_DELETE(l_sign);
        l_total_signs_size += l_sign_size;
        l_cur_sign_offset += l_sign_size;
        a_datum_decree->header.signs_size = l_total_signs_size;
        log_it(L_DEBUG,"<-- Signed with '%s'", a_certs[i]->name);
        l_total_sign_count++;
    }
    if (a_total_sign_count)
        *a_total_sign_count = l_total_sign_count;
    return a_datum_decree;
}