diff --git a/dap-sdk b/dap-sdk index a7b6da64ee24a3aba64dcbbd5f3f356069d853ad..ff8478aa04fa94540016fee0038db4b63965d0d1 160000 --- a/dap-sdk +++ b/dap-sdk @@ -1 +1 @@ -Subproject commit a7b6da64ee24a3aba64dcbbd5f3f356069d853ad +Subproject commit ff8478aa04fa94540016fee0038db4b63965d0d1 diff --git a/modules/chain/dap_chain.c b/modules/chain/dap_chain.c index 6a31319fb60d1ca52080ae5d5ee4d98351f0f6dd..3981dd2b116ddc8b109e97ae8730915bbe8191d4 100644 --- a/modules/chain/dap_chain.c +++ b/modules/chain/dap_chain.c @@ -982,4 +982,4 @@ const char *dap_chain_type_to_str(const dap_chain_type_t a_default_chain_type) default: return "unknown"; } -} +} \ No newline at end of file diff --git a/modules/chain/dap_chain_policy.c b/modules/chain/dap_chain_policy.c new file mode 100644 index 0000000000000000000000000000000000000000..2b6e06c41c0b9d65cef64779d6c62e798e16941a --- /dev/null +++ b/modules/chain/dap_chain_policy.c @@ -0,0 +1,375 @@ +/* +* Authors: +* Pavel Uhanov <pavel.uhanov@demlabs.net> +* Cellframe https://cellframe.net +* DeM Labs Inc. https://demlabs.net +* Copyright (c) 2017-2025 +* All rights reserved. + +This file is part of CellFrame SDK the open source project + +CellFrame SDK is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +CellFrame SDK is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with any CellFrame SDK based project. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "dap_chain_policy.h" +#include "dap_chain_datum_decree.h" +#include "dap_list.h" +#include "uthash.h" + +#define LOG_TAG "dap_chain_policy" + +typedef struct dap_chain_net dap_chain_net_t; + +struct net_policy_item { + uint64_t net_id; + uint32_t last_num_policy; + dap_list_t *exception_list; + dap_list_t *policies; + UT_hash_handle hh; +}; + +static struct net_policy_item *s_net_policy_items = NULL; + +/** + * @brief search net element in list by id + * @param a_net_id + * @return pointer if find, NULL if not + */ +DAP_STATIC_INLINE struct net_policy_item *s_net_item_find(uint64_t a_net_id) +{ + struct net_policy_item *l_item = NULL; + HASH_FIND_BYHASHVALUE(hh, s_net_policy_items, &a_net_id, sizeof(a_net_id), a_net_id, l_item); + return l_item; +} + +DAP_STATIC_INLINE int s_policy_num_compare(dap_list_t *a_list1, dap_list_t *a_list2) +{ + dap_chain_policy_t + *l_policy1 = a_list1->data, + *l_policy2 = a_list2->data; + if (l_policy1->type != DAP_CHAIN_POLICY_ACTIVATE || l_policy2->type != DAP_CHAIN_POLICY_ACTIVATE) { + log_it(L_WARNING, "Compare wrong policy type"); + return 0; + } + return ((dap_chain_policy_activate_t *)(l_policy1->data))->num == ((dap_chain_policy_activate_t *)(l_policy2->data))->num ? 0 : + ((dap_chain_policy_activate_t *)(l_policy1->data))->num > ((dap_chain_policy_activate_t *)(l_policy2->data))->num ? 1 : -1; +} + +DAP_STATIC_INLINE bool s_policy_is_cond(dap_chain_policy_t *a_policy) +{ + return a_policy->type == DAP_CHAIN_POLICY_ACTIVATE && + (DAP_FLAG_CHECK(a_policy->flags, DAP_CHAIN_POLICY_FLAG_ACTIVATE_BY_TS) || + DAP_FLAG_CHECK(a_policy->flags, DAP_CHAIN_POLICY_FLAG_ACTIVATE_BY_BLOCK_NUM)); +} + +static bool s_policy_cond_activated(dap_chain_policy_activate_t *a_policy_activate, uint64_t a_flags) +{ + bool l_ret = false; + if (DAP_FLAG_CHECK(a_flags, DAP_CHAIN_POLICY_FLAG_ACTIVATE_BY_TS)) { + time_t l_current_time = dap_time_now(); + l_ret |= l_current_time >= a_policy_activate->ts_start; + } + if (DAP_FLAG_CHECK(a_flags, DAP_CHAIN_POLICY_FLAG_ACTIVATE_BY_BLOCK_NUM)) { + if (!a_policy_activate->chain_union.chain) { + log_it(L_ERROR, "Chain is null in policy item with upped DAP_CHAIN_POLICY_FLAG_ACTIVATE_BY_BLOCK_NUM flag"); + return l_ret; + } + l_ret |= a_policy_activate->chain_union.chain->atom_num_last >= a_policy_activate->block_start; + } + return l_ret; +} + +/** + * @brief init policy commands + * @return 0 if pass, other if error + */ +int dap_chain_policy_init() +{ + return 0; +} + +/** + * @brief deinit policy commands + */ +void dap_chain_policy_deinit() +{ + struct net_policy_item + *l_temp = NULL, + *l_current = NULL; + HASH_ITER(hh, s_net_policy_items, l_current, l_temp) { + HASH_DEL(s_net_policy_items, l_current); + dap_list_free_full(l_current->policies, NULL); + dap_list_free(l_current->exception_list); + DAP_DELETE(l_current); + } +} + +/** + * @brief add new net to policies list + * @param a_net_id + * @return 0 if pass, other if error + */ +int dap_chain_policy_net_add(uint64_t a_net_id) +{ + dap_return_val_if_pass(!a_net_id, -1); + if(s_net_item_find(a_net_id)) { + log_it(L_ERROR, "Net with id %"DAP_UINT64_FORMAT_X" already added", a_net_id); + return -2; + } + struct net_policy_item *l_new_item = DAP_NEW_Z_RET_VAL_IF_FAIL(struct net_policy_item, -3); + l_new_item->net_id = a_net_id; + HASH_ADD_BYHASHVALUE(hh, s_net_policy_items, net_id, sizeof(l_new_item->net_id), l_new_item->net_id, l_new_item); + return 0; +} + +/** + * @brief remove net from policies list + * @param a_net_id + * @return 0 if pass, other if error + */ +int dap_chain_policy_net_remove(uint64_t a_net_id) +{ + dap_return_val_if_pass(!a_net_id, -1); + struct net_policy_item *l_net_item = s_net_item_find(a_net_id); + if (!l_net_item) { + log_it(L_ERROR, "Can't find net %"DAP_UINT64_FORMAT_X" in policy list", a_net_id); + return -2; + } + HASH_DEL(s_net_policy_items, l_net_item); + dap_list_free_full(l_net_item->policies, NULL); + dap_list_free(l_net_item->exception_list); + DAP_DELETE(l_net_item); + return 0; +} + +/** + * @brief add new policy + * @param a_policy_num + * @param a_net_id net id + * @return 0 if pass, other if error + */ +int dap_chain_policy_add(dap_chain_policy_t *a_policy, uint64_t a_net_id) +{ + dap_return_val_if_pass(!a_policy, -1); + struct net_policy_item *l_net_item = s_net_item_find(a_net_id); + if (!l_net_item) { + log_it(L_ERROR, "Can't find net %"DAP_UINT64_FORMAT_X" in policy list", a_net_id); + return -2; + } + switch (a_policy->type) { + case DAP_CHAIN_POLICY_ACTIVATE: + if (dap_list_find(l_net_item->policies, a_policy, s_policy_num_compare)) { + log_it(L_ERROR, "CN-%u already added to net %"DAP_UINT64_FORMAT_X, ((dap_chain_policy_activate_t *)(a_policy->data))->num, a_net_id); + return -3; + } + l_net_item->policies = dap_list_insert_sorted(l_net_item->policies, a_policy, s_policy_num_compare); + if (!s_policy_is_cond(a_policy)) + l_net_item->last_num_policy = dap_max(((dap_chain_policy_activate_t *)(a_policy->data))->num, l_net_item->last_num_policy); + break; + case DAP_CHAIN_POLICY_DEACTIVATE: + for (size_t i = 0; i < ((dap_chain_policy_deactivate_t *)(a_policy->data))->count; ++i) { + uint32_t l_policy_num = ((dap_chain_policy_deactivate_t *)(a_policy->data))->nums[i]; + if (dap_list_find(l_net_item->exception_list, (const void *)(uintptr_t)l_policy_num, NULL)) { + log_it(L_ERROR, "CN-%u already added to exception list net %"DAP_UINT64_FORMAT_X, l_policy_num, a_net_id); + continue; + } + l_net_item->exception_list = dap_list_append(l_net_item->exception_list, (void *)(uintptr_t)l_policy_num); + } + break; + default: + log_it(L_ERROR, "Unknow policy type %u", a_policy->type); + break; + } + return 0; +} + +/** + * @brief add policy num to exception list + * @param a_policy_num + * @param a_net_id net id + * @return 0 if pass, other if error + */ +int dap_chain_policy_add_to_exception_list(uint32_t a_policy_num, uint64_t a_net_id) +{ + dap_return_val_if_pass(!a_policy_num, -1); + struct net_policy_item *l_net_item = s_net_item_find(a_net_id); + if (!l_net_item) { + log_it(L_ERROR, "Can't find net %"DAP_UINT64_FORMAT_X" in policy list", a_net_id); + return -2; + } + if (dap_list_find(l_net_item->exception_list, (const void *)(uintptr_t)a_policy_num, NULL)) { + log_it(L_ERROR, "CN-%u already added to exception list net %"DAP_UINT64_FORMAT_X, a_policy_num, a_net_id); + return -3; + } + l_net_item->exception_list = dap_list_append(l_net_item->exception_list, (void *)(uintptr_t)a_policy_num); + return 0; +} + +/** + * @brief check policy activation + * @param a_policy_num + * @param a_net_id net id + * @return true if yes, false if no + */ +bool dap_chain_policy_activated(uint32_t a_policy_num, uint64_t a_net_id) +{ + const bool l_ret_false = false; + struct net_policy_item *l_net_item = s_net_item_find(a_net_id); + dap_return_val_if_pass(!l_net_item, l_ret_false); + // exception list check + if (dap_list_find(l_net_item->exception_list, (const void *)(uintptr_t)a_policy_num, NULL)) + return l_ret_false; + // seach politics to condition check + dap_chain_policy_t *l_to_search = DAP_NEW_Z_SIZE_RET_VAL_IF_FAIL(dap_chain_policy_t, sizeof(dap_chain_policy_t) + sizeof(dap_chain_policy_activate_t), false); + l_to_search->type = DAP_CHAIN_POLICY_ACTIVATE; + ((dap_chain_policy_activate_t *)(l_to_search->data))->num = a_policy_num; + dap_list_t *l_list_item = dap_list_find(l_net_item->policies, l_to_search, s_policy_num_compare); + DAP_DELETE(l_to_search); + if (l_list_item && s_policy_is_cond((dap_chain_policy_t *)l_list_item->data)) { + dap_chain_policy_activate_t *l_activate = (dap_chain_policy_activate_t *)((dap_chain_policy_t *)(l_list_item->data))->data; + bool l_ret = s_policy_cond_activated(l_activate, ((dap_chain_policy_t *)l_list_item->data)->flags); + if (l_ret) + l_net_item->last_num_policy = dap_max(l_activate->num, l_net_item->last_num_policy); + return l_ret; + } + // cumulative return + return a_policy_num <= l_net_item->last_num_policy; +} + +/** + * @brief find policy + * @param a_policy_num + * @param a_net_id net id + * @return true if yes, false if no + */ +dap_chain_policy_t *dap_chain_policy_find(uint32_t a_policy_num, uint64_t a_net_id) +{ + dap_return_val_if_pass(!a_policy_num, NULL); + struct net_policy_item *l_net_item = s_net_item_find(a_net_id); + dap_return_val_if_pass(!l_net_item, NULL); + + dap_chain_policy_t *l_to_search = DAP_NEW_Z_SIZE_RET_VAL_IF_FAIL(dap_chain_policy_t, sizeof(dap_chain_policy_t) + sizeof(dap_chain_policy_activate_t), false); + l_to_search->type = DAP_CHAIN_POLICY_ACTIVATE; + ((dap_chain_policy_activate_t *)(l_to_search->data))->num = a_policy_num; + dap_list_t *l_find = dap_list_find(l_net_item->policies, l_to_search, s_policy_num_compare); + DAP_DELETE(l_to_search); + if (!l_find) { + log_it(L_DEBUG, "Can't find CN-%u in net %"DAP_UINT64_FORMAT_X, a_policy_num, a_net_id); + return NULL; + } + return (dap_chain_policy_t *)l_find->data; +} + +/** + * @brief return last policy num in enet + * @param a_net_id net id to search + * @return last num + */ +DAP_INLINE uint32_t dap_chain_policy_get_last_num(uint64_t a_net_id) +{ + struct net_policy_item *l_net_item = s_net_item_find(a_net_id); + dap_return_val_if_pass(!l_net_item, 0); + return l_net_item->last_num_policy; +} + + +json_object *dap_chain_policy_list(uint64_t a_net_id) +{ + struct net_policy_item *l_net_item = s_net_item_find(a_net_id); + dap_return_val_if_pass(!l_net_item, NULL); + json_object *l_ret = json_object_new_object(); + + dap_string_t *l_active_str = dap_string_new(""); + dap_string_t *l_inactive_str = dap_string_new(""); + if (l_net_item->last_num_policy) + dap_string_append_printf(l_active_str, "%s CN-%u ", dap_list_find(l_net_item->exception_list, (const void *)(uintptr_t)l_net_item->last_num_policy, NULL) ? "<" : "<=", l_net_item->last_num_policy); + json_object_object_add(l_ret, "cumulative active", json_object_new_string(l_active_str->str)); + dap_string_erase(l_active_str, 0, -1); + for (dap_list_t *l_iter = dap_list_first(l_net_item->policies); l_iter; l_iter = l_iter->next) { + dap_chain_policy_activate_t *l_policy_activate = (dap_chain_policy_activate_t *)((dap_chain_policy_t *)(l_iter->data))->data; + if (dap_list_find(l_net_item->exception_list, (const void *)(uintptr_t)l_policy_activate->num, NULL)) + continue; + if (s_policy_is_cond((dap_chain_policy_t *)(l_iter->data))) { + if (s_policy_cond_activated(l_policy_activate, ((dap_chain_policy_t *)(l_iter->data))->flags)) + dap_string_append_printf(l_active_str, "CN-%u ", l_policy_activate->num); + else + dap_string_append_printf(l_inactive_str, "CN-%u ", l_policy_activate->num); + } + } + json_object_object_add(l_ret, "conditional active", json_object_new_string(l_active_str->str)); + json_object_object_add(l_ret, "conditional inactive", json_object_new_string(l_inactive_str->str)); + + dap_string_free(l_active_str, true); + dap_string_erase(l_inactive_str, 0, -1); + for (dap_list_t *l_iter = dap_list_first(l_net_item->exception_list); l_iter; l_iter = l_iter->next) { + dap_string_append_printf(l_inactive_str, "CN-%u ", (uint32_t)(uintptr_t)l_iter->data); + } + json_object_object_add(l_ret, "exception list", json_object_new_string(l_inactive_str->str)); + dap_string_free(l_inactive_str, true); + return l_ret; +} + +json_object *dap_chain_policy_json_collect(dap_chain_policy_t *a_policy) +{ + dap_return_val_if_pass(!a_policy, NULL); + json_object *l_ret = json_object_new_object(); + + json_object_object_add(l_ret, "version", json_object_new_uint64(a_policy->version)); + json_object_object_add(l_ret, "type", json_object_new_string(dap_chain_policy_to_str(a_policy))); + switch (a_policy->type) { + case DAP_CHAIN_POLICY_ACTIVATE: { + dap_chain_policy_activate_t *l_policy_activate = (dap_chain_policy_activate_t *)a_policy->data; + json_object_object_add(l_ret, "num", json_object_new_uint64(l_policy_activate->num)); + if (l_policy_activate->ts_start) { + char l_time[DAP_TIME_STR_SIZE] = {}; + dap_time_to_str_rfc822(l_time, DAP_TIME_STR_SIZE - 1, l_policy_activate->ts_start); + json_object_object_add(l_ret, "ts_start", json_object_new_string(l_time)); + } else { + json_object_object_add(l_ret, "ts_start", json_object_new_int(0)); + } + json_object_object_add(l_ret, "block_start", json_object_new_uint64(l_policy_activate->block_start)); + if (l_policy_activate->block_start) { + if (!l_policy_activate->chain_union.chain) { + json_object_object_add(l_ret, "chain", json_object_new_string("ERROR pointer chain is NULL")); + } else { + char l_chain_id[32] = { }; + snprintf(l_chain_id, sizeof(l_chain_id) - 1, "0x%016"DAP_UINT64_FORMAT_x, l_policy_activate->chain_union.chain->id.uint64); + json_object_object_add(l_ret, "chain", json_object_new_string(l_chain_id)); + } + } else { + json_object_object_add(l_ret, "chain", json_object_new_string("")); + } + json_object_object_add(l_ret, "description", json_object_new_string("WIKI")); + } + break; + + case DAP_CHAIN_POLICY_DEACTIVATE: { + dap_chain_policy_deactivate_t *l_policy_deactivate = (dap_chain_policy_deactivate_t *)a_policy->data; + if (l_policy_deactivate->count) { + dap_string_t *l_nums_list = dap_string_sized_new(l_policy_deactivate->count * (sizeof(uint32_t) + 4)); + for (size_t i = 0; i < l_policy_deactivate->count; ++i) { + dap_string_append_printf(l_nums_list, "CN-%u ", l_policy_deactivate->nums[i]); + } + json_object_object_add(l_ret, "deactivate", json_object_new_string(l_nums_list->str)); + dap_string_free(l_nums_list, true); + } else { + json_object_object_add(l_ret, "deactivate", json_object_new_string("")); + } + } + break; + default: + break; + } + return l_ret; +} \ No newline at end of file diff --git a/modules/chain/include/dap_chain_policy.h b/modules/chain/include/dap_chain_policy.h new file mode 100644 index 0000000000000000000000000000000000000000..883e8469c5a942e044b34c65710af201c12e097c --- /dev/null +++ b/modules/chain/include/dap_chain_policy.h @@ -0,0 +1,109 @@ +/* +* Authors: +* Pavel Uhanov <pavel.uhanov@demlabs.net> +* Cellframe https://cellframe.net +* DeM Labs Inc. https://demlabs.net +* Copyright (c) 2017-2025 +* 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/>. +*/ + +#pragma once + +#include "dap_common.h" +#include "dap_chain.h" + +#define DAP_CHAIN_POLICY_VERSION 1 + +#define DAP_CHAIN_POLICY_FLAG_ACTIVATE_BY_TS BIT(0) +#define DAP_CHAIN_POLICY_FLAG_ACTIVATE_BY_BLOCK_NUM BIT(1) +#define DAP_CHAIN_POLICY_FLAG_ACTIVATE_BY_CONFIG BIT(2) + + +typedef enum { + DAP_CHAIN_POLICY_DEACTIVATE = 0, + DAP_CHAIN_POLICY_ACTIVATE +} dap_chain_policy_type_t; + +typedef struct dap_chain_policy_deactivate { + uint32_t count; + uint32_t nums[]; +} DAP_ALIGN_PACKED dap_chain_policy_deactivate_t; + + +typedef struct dap_chain_policy_activate { + uint32_t num; + int64_t ts_start; + uint64_t block_start; + union { + dap_chain_id_t chain_id; + dap_chain_t *chain; + } chain_union; + uint16_t generation; +} DAP_ALIGN_PACKED dap_chain_policy_activate_t; + +typedef struct dap_chain_policy { + uint16_t version; + uint16_t type; + uint64_t flags; + uint64_t data_size; + uint8_t data[]; +} DAP_ALIGN_PACKED dap_chain_policy_t; + +int dap_chain_policy_init(); +void dap_chain_policy_deinit(); +int dap_chain_policy_net_add(uint64_t a_net_id); +int dap_chain_policy_net_remove(uint64_t a_net_id); +int dap_chain_policy_add(dap_chain_policy_t *a_policy, uint64_t a_net_id); +int dap_chain_policy_add_to_exception_list(uint32_t a_policy_num, uint64_t a_net_id); +uint32_t dap_chain_policy_get_last_num(uint64_t a_net_id); +dap_chain_policy_t *dap_chain_policy_find(uint32_t a_policy_num, uint64_t a_net_id); +json_object *dap_chain_policy_json_collect(dap_chain_policy_t *a_policy); +json_object *dap_chain_policy_list(uint64_t a_net_id); +bool dap_chain_policy_activated(uint32_t a_policy_num, uint64_t a_net_id); + +DAP_STATIC_INLINE size_t dap_chain_policy_deactivate_calc_size(size_t a_deactivate_count) +{ + return sizeof(dap_chain_policy_t) + sizeof(dap_chain_policy_deactivate_t) + sizeof(uint32_t) * a_deactivate_count; +} + +DAP_STATIC_INLINE size_t dap_chain_policy_get_size(dap_chain_policy_t *a_policy) +{ + return a_policy ? sizeof(dap_chain_policy_t) + a_policy->data_size : 0; +} + +DAP_STATIC_INLINE const char *dap_chain_policy_to_str(dap_chain_policy_t *a_policy) +{ + if(!a_policy) + return "NULL"; + switch (a_policy->type) { + case DAP_CHAIN_POLICY_ACTIVATE: return ("DAP_CHAIN_POLICY_ACTIVATE"); + case DAP_CHAIN_POLICY_DEACTIVATE: return ("DAP_CHAIN_POLICY_DEACTIVATE"); + default: return ("UNKNOWN"); + } +} + +/** + * @brief check policy num + * @param a_num + * @return true if valid, fail if not + */ +DAP_STATIC_INLINE bool dap_chain_policy_num_is_valid(uint64_t a_num) +{ + uint32_t l_num = dap_maxval(l_num); + return (a_num && a_num <= l_num); +} \ No newline at end of file diff --git a/modules/common/dap_chain_common.c b/modules/common/dap_chain_common.c index c611a4ddf75f81c38a50ddd29a313fad3efdbea7..298cbdd1498cd240e8894446a3b38d27cb35885c 100644 --- a/modules/common/dap_chain_common.c +++ b/modules/common/dap_chain_common.c @@ -75,25 +75,73 @@ dap_chain_addr_str_t dap_chain_addr_to_str_static_(const dap_chain_addr_t *a_add return res; } +/** + * @brief s_addr_from_str + * @param [out] a_addr - pointer to addr fill + * @param [in] a_str - string with one addr + * @return 0 if pass, other if error + */ +static int s_addr_from_str(dap_chain_addr_t *a_addr, const char *a_str) +{ + dap_return_val_if_pass(!a_addr || !a_str || !a_str[0], -1); + if (!dap_strcmp(a_str, "null") || !dap_strcmp(a_str, "0")) { + memset(a_addr, 0, sizeof(dap_chain_addr_t)); + return 0; + } + int l_ret = 0; + size_t l_ret_size = DAP_ENC_BASE58_DECODE_SIZE(strlen(a_str)); + dap_chain_addr_t *l_addr_cur = DAP_NEW_Z_SIZE_RET_VAL_IF_FAIL(dap_chain_addr_t, l_ret_size, -2); + if (dap_enc_base58_decode(a_str, l_addr_cur) == sizeof(dap_chain_addr_t) && !dap_chain_addr_check_sum(l_addr_cur)) { + memcpy(a_addr, l_addr_cur, sizeof(dap_chain_addr_t)); + } else { + l_ret = -3; + } + DAP_DELETE(l_addr_cur); + return l_ret; +} + /** * @brief dap_chain_str_to_addr - * @param a_addr - * @return + * @param a_str - string with one addr + * @return pointer to dap_chain_addr_t if pass, if not - NULL */ -dap_chain_addr_t* dap_chain_addr_from_str(const char *a_str) +dap_chain_addr_t *dap_chain_addr_from_str(const char *a_str) { - size_t l_str_len = (a_str) ? strlen(a_str) : 0; - if(l_str_len <= 0) + dap_chain_addr_t *l_ret = DAP_NEW_Z_RET_VAL_IF_FAIL(dap_chain_addr_t, NULL); + if (s_addr_from_str(l_ret, a_str)) { + DAP_DELETE(l_ret); return NULL; - if (!dap_strcmp(a_str, "null") || !dap_strcmp(a_str, "0")) { - return DAP_NEW_Z(dap_chain_addr_t); } - size_t l_ret_size = DAP_ENC_BASE58_DECODE_SIZE(l_str_len); - dap_chain_addr_t *l_addr = DAP_NEW_Z_SIZE(dap_chain_addr_t, l_ret_size); - return ( dap_enc_base58_decode(a_str, l_addr) == sizeof(dap_chain_addr_t) ) - && !dap_chain_addr_check_sum(l_addr) - ? l_addr - : ( DAP_DELETE(l_addr), NULL ); + return l_ret; +} + + +/** + * @brief parce addrs string div by ',', alloc memory to addrs + * @param a_addr_str - ddrs string div by ',' + * @param a_addr - pointer to memory alloc + * @return addr count + */ +size_t dap_chain_addr_from_str_array(const char *a_addr_str, dap_chain_addr_t **a_addr) +{ + dap_return_val_if_pass(!a_addr_str || !a_addr, 0); + size_t l_count = dap_str_symbol_count(a_addr_str, ',') + 1; + dap_chain_addr_t *l_addr = DAP_NEW_Z_COUNT_RET_VAL_IF_FAIL(dap_chain_addr_t, l_count, 0); + char **l_addr_str_array = dap_strsplit(a_addr_str, ",", l_count); + if (!l_addr_str_array) { + DAP_DELETE(l_addr); + return 0; + } + for (size_t i = 0; i < l_count; ++i) { + if (s_addr_from_str(l_addr + i, l_addr_str_array[i])) { + DAP_DELETE(l_addr); + dap_strfreev(l_addr_str_array); + return 0; + } + } + dap_strfreev(l_addr_str_array); + *a_addr = l_addr; + return l_count; } bool dap_chain_addr_is_blank(const dap_chain_addr_t *a_addr) diff --git a/modules/common/dap_chain_datum_decree.c b/modules/common/dap_chain_datum_decree.c index 2f61eab67173449aec7130caafa0e0fa8789f4c7..be040aaeb615f2c41983bb88dd58a70b317b3dbd 100644 --- a/modules/common/dap_chain_datum_decree.c +++ b/modules/common/dap_chain_datum_decree.c @@ -28,6 +28,7 @@ #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 @@ -177,9 +178,14 @@ 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); - if (!l_tsd) - return NULL; - return dap_pkey_get_size((dap_pkey_t *)l_tsd->data) == l_tsd->size ? (dap_pkey_t *)l_tsd->data : NULL; + 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) @@ -348,7 +354,14 @@ void dap_chain_datum_decree_dump_json(json_object *a_json_out, dap_chain_datum_d 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; + break; + 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; diff --git a/modules/common/dap_chain_datum_token.c b/modules/common/dap_chain_datum_token.c index 423e44a0ffe6d66df877c9cc63a2901364dc5e3d..f413be3ff7fc6e4c7e5df2f0c147b4fe0ba1945f 100644 --- a/modules/common/dap_chain_datum_token.c +++ b/modules/common/dap_chain_datum_token.c @@ -491,7 +491,7 @@ dap_sign_t *dap_chain_datum_emission_get_signs(dap_chain_datum_token_emission_t size_t l_count, l_sign_size; for (l_count = 0, l_sign_size = 0; l_count < a_emission->data.type_auth.signs_count; ++l_count) { l_sign_size = dap_sign_get_size(l_sign); - if (!dap_sign_verify_size(l_sign, l_sign_size)) { + if (dap_sign_verify_size(l_sign, l_sign_size)) { break; } l_actual_size += l_sign_size; diff --git a/modules/common/dap_chain_datum_tx_items.c b/modules/common/dap_chain_datum_tx_items.c index b694961e23c5e5a2bc4ef48342abf7e588f68770..6384a2573f3e918ddf47166e4e0373b7ce272ebc 100644 --- a/modules/common/dap_chain_datum_tx_items.c +++ b/modules/common/dap_chain_datum_tx_items.c @@ -104,7 +104,7 @@ dap_chain_tx_out_cond_subtype_t dap_chain_tx_out_cond_subtype_from_str(const cha */ size_t dap_chain_datum_item_tx_get_size(const byte_t *a_item, size_t a_max_size) { - dap_return_val_if_fail(a_item, TX_ITEM_TYPE_UNKNOWN); + dap_return_val_if_fail(a_item, 0); size_t l_ret = 0; #define m_tx_item_size(t) ( !a_max_size || sizeof(t) <= a_max_size ? sizeof(t) : 0 ) #define m_tx_item_size_ext(t, size_field) \ @@ -366,11 +366,11 @@ dap_chain_tx_out_cond_t *dap_chain_datum_tx_item_out_cond_create_srv_stake_lock( dap_chain_tx_out_cond_t *dap_chain_datum_tx_item_out_cond_create_srv_emit_delegate(dap_chain_net_srv_uid_t a_srv_uid, uint256_t a_value, uint32_t a_signs_min, dap_hash_fast_t *a_pkey_hashes, - size_t a_pkey_hashes_count) + size_t a_pkey_hashes_count, const char *a_tag_str) { if (IS_ZERO_256(a_value)) return NULL; - size_t l_tsd_total_size = a_pkey_hashes_count * (sizeof(dap_hash_fast_t) + sizeof(dap_tsd_t)); + size_t l_tsd_total_size = a_pkey_hashes_count * (sizeof(dap_hash_fast_t) + sizeof(dap_tsd_t)) + (a_tag_str ? sizeof(dap_tsd_t) + strlen(a_tag_str) + 1 : 0); dap_chain_tx_out_cond_t *l_item = DAP_NEW_Z_SIZE_RET_VAL_IF_FAIL(dap_chain_tx_out_cond_t, sizeof(dap_chain_tx_out_cond_t) + l_tsd_total_size, NULL); l_item->header.item_type = TX_ITEM_TYPE_OUT_COND; l_item->header.value = a_value; @@ -381,6 +381,8 @@ dap_chain_tx_out_cond_t *dap_chain_datum_tx_item_out_cond_create_srv_emit_delega byte_t *l_next_tsd_ptr = l_item->tsd; for (size_t i = 0; i < a_pkey_hashes_count; i++) l_next_tsd_ptr = dap_tsd_write(l_next_tsd_ptr, DAP_CHAIN_TX_OUT_COND_TSD_HASH, a_pkey_hashes + i, sizeof(dap_hash_fast_t)); + if (a_tag_str) + l_next_tsd_ptr = dap_tsd_write(l_next_tsd_ptr, DAP_CHAIN_TX_OUT_COND_TSD_STR, (const void*)a_tag_str, strlen(a_tag_str) + 1); return l_item; } diff --git a/modules/common/dap_chain_datum_tx_voting.c b/modules/common/dap_chain_datum_tx_voting.c index 2a161dd3a93ce110042cf4c8c30030a536e9ec28..eb2ab0320c2da6b324b7dcf002790f0b6644c9f7 100644 --- a/modules/common/dap_chain_datum_tx_voting.c +++ b/modules/common/dap_chain_datum_tx_voting.c @@ -124,6 +124,18 @@ dap_chain_tx_tsd_t* dap_chain_datum_voting_vote_changing_allowed_tsd_create(bool return l_tsd; } +dap_chain_tx_tsd_t *dap_chain_datum_voting_token_tsd_create(const char *a_token_ticker) +{ + dap_return_val_if_fail(a_token_ticker && *a_token_ticker, NULL); + size_t l_ticker_len = strlen(a_token_ticker); + if (l_ticker_len >= DAP_CHAIN_TICKER_SIZE_MAX) { + log_it(L_ERROR, "Ticker len %zu is too big", l_ticker_len); + return NULL; + } + dap_chain_tx_tsd_t *l_tsd = dap_chain_datum_tx_item_tsd_create((char *)a_token_ticker, VOTING_TSD_TYPE_TOKEN, l_ticker_len); + return l_tsd; +} + dap_chain_tx_tsd_t* dap_chain_datum_voting_vote_tx_cond_tsd_create(dap_chain_hash_fast_t a_tx_hash, int a_out_idx) { dap_chain_tx_voting_tx_cond_t l_temp = { @@ -176,6 +188,9 @@ json_object *dap_chain_datum_tx_item_voting_tsd_to_json(dap_chain_datum_tx_t* a_ case VOTING_TSD_TYPE_ANSWER: json_object_array_add(l_answer_array_object, json_object_new_string_len((char*)l_tsd->data, l_tsd->size)); break; + case VOTING_TSD_TYPE_TOKEN: + json_object_object_add(l_object, "token", json_object_new_string_len((char*)l_tsd->data, l_tsd->size)); + break; case VOTING_TSD_TYPE_EXPIRE: json_object_object_add(l_object, "exired", json_object_new_uint64(*(uint64_t*)l_tsd->data)); break; diff --git a/modules/common/include/dap_chain_common.h b/modules/common/include/dap_chain_common.h index 69bc2c00fc68f50e450738672c1e607d3df5152d..0b930d2925c0122e26181a3aecf272f9f850c70d 100644 --- a/modules/common/include/dap_chain_common.h +++ b/modules/common/include/dap_chain_common.h @@ -225,6 +225,7 @@ dap_chain_addr_str_t dap_chain_addr_to_str_static_(const dap_chain_addr_t *a_add #define dap_chain_addr_to_str dap_chain_addr_to_str_static dap_chain_addr_t* dap_chain_addr_from_str(const char *str); +size_t dap_chain_addr_from_str_array(const char *a_addr_str, dap_chain_addr_t **a_addr); bool dap_chain_addr_is_blank(const dap_chain_addr_t *a_addr); dap_chain_net_srv_uid_t dap_chain_net_srv_uid_from_str(const char* a_str); diff --git a/modules/common/include/dap_chain_datum_decree.h b/modules/common/include/dap_chain_datum_decree.h index f8a49f0a39b5cb20247504ee040204c056a6d9ac..fceff87616ecf26f12f2576bda89a06fbdfa8d59 100644 --- a/modules/common/include/dap_chain_datum_decree.h +++ b/modules/common/include/dap_chain_datum_decree.h @@ -27,6 +27,7 @@ #include "dap_time.h" #include "dap_list.h" #include "dap_cert.h" +#include "dap_chain_policy.h" #include <stdint.h> #define DAP_CHAIN_DATUM_DECREE_VERSION 0 @@ -75,7 +76,8 @@ DAP_STATIC_INLINE size_t dap_chain_datum_decree_get_size(dap_chain_datum_decree_ #define DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_MAX_WEIGHT 0x000C #define DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_EMERGENCY_VALIDATORS 0x000D #define DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_CHECK_SIGNS_STRUCTURE 0x000E -#define DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_STAKE_PKEY_UPDATE 0x0010 +#define DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_STAKE_PKEY_UPDATE 0x0010 +#define DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_POLICY 0x0012 // DECREE TSD types #define DAP_CHAIN_DATUM_DECREE_TSD_TYPE_VALUE 0x0100 @@ -94,7 +96,8 @@ DAP_STATIC_INLINE size_t dap_chain_datum_decree_get_size(dap_chain_datum_decree_ #define DAP_CHAIN_DATUM_DECREE_TSD_TYPE_STRING 0x0115 #define DAP_CHAIN_DATUM_DECREE_TSD_TYPE_ACTION 0x010A #define DAP_CHAIN_DATUM_DECREE_TSD_TYPE_SIGNATURE_TYPE 0x010B -#define DAP_CHAIN_DATUM_DECREE_TSD_TYPE_STAKE_PKEY 0x010D +#define DAP_CHAIN_DATUM_DECREE_TSD_TYPE_STAKE_PKEY 0x010D +#define DAP_CHAIN_DATUM_DECREE_TSD_TYPE_POLICY_EXECUTE 0x010E #ifdef __cplusplus @@ -132,6 +135,8 @@ DAP_STATIC_INLINE const char *dap_chain_datum_decree_subtype_to_str(uint16_t a_d return "DECREE_COMMON_SUBTYPE_CHECK_SIGNS_STRUCTURE"; case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_STAKE_PKEY_UPDATE: return "DECREE_COMMON_SUBTYPE_STAKE_UPDATE"; + case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_POLICY: + return "DECREE_COMMON_SUBTYPE_POLICY"; default: return "DECREE_SUBTYPE_UNKNOWN"; } @@ -166,6 +171,8 @@ DAP_STATIC_INLINE uint16_t dap_chain_datum_decree_type_from_str(const char *a_de return DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_CHECK_SIGNS_STRUCTURE; } else if (!dap_strcmp(a_decree_type, "stake_update")) { return DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_STAKE_PKEY_UPDATE; + } else if (!dap_strcmp(a_decree_type, "policy")) { + return DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_POLICY; } else { return 0; } @@ -207,6 +214,8 @@ DAP_STATIC_INLINE const char *dap_chain_datum_decree_tsd_type_to_str(uint16_t a_ return "DAP_CHAIN_DATUM_DECREE_TSD_TYPE_SIGNATURE_TYPE"; case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_STAKE_PKEY: return "DAP_CHAIN_DATUM_DECREE_TSD_TYPE_STAKE_PKEY"; + case DAP_CHAIN_DATUM_DECREE_TSD_TYPE_POLICY_EXECUTE: + return "DAP_CHAIN_DATUM_DECREE_TSD_TYPE_POLICY_EXECUTE"; default: return "DECREE_TSD_TYPE_UNKNOWN"; } @@ -315,6 +324,12 @@ int dap_chain_datum_decree_get_action(dap_chain_datum_decree_t *a_decree, uint8_ int dap_chain_datum_decree_get_signature_type(dap_chain_datum_decree_t *a_decree, uint32_t *a_signature_type); int dap_chain_datum_decree_get_ban_addr(dap_chain_datum_decree_t *a_decree, const char **a_addr); dap_pkey_t *dap_chain_datum_decree_get_pkey(dap_chain_datum_decree_t *a_decree); +/** + * @brief get policy from decree tsd + * @param a_decree + * @return pointer to dap_chain_policy_t if find, if not or error - NULL + */ +dap_chain_policy_t *dap_chain_datum_decree_get_policy(dap_chain_datum_decree_t *a_decree); /** * @breif dap_chain_datum_decree_dump Dump information about decree @@ -358,7 +373,6 @@ void dap_chain_datum_decree_certs_dump_json(json_object * a_json_out, byte_t * a 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); - #ifdef __cplusplus } #endif diff --git a/modules/common/include/dap_chain_datum_tx_items.h b/modules/common/include/dap_chain_datum_tx_items.h index 30394948908f3ea921fa68bc6f614869c5ecc28a..e864aef95f5ddb967bb1c3be3bdb3e6e14676796 100644 --- a/modules/common/include/dap_chain_datum_tx_items.h +++ b/modules/common/include/dap_chain_datum_tx_items.h @@ -212,7 +212,7 @@ dap_chain_tx_out_cond_t *dap_chain_datum_tx_item_out_cond_create_srv_stake_lock( dap_chain_tx_out_cond_t *dap_chain_datum_tx_item_out_cond_create_srv_emit_delegate(dap_chain_net_srv_uid_t a_srv_uid, uint256_t a_value, uint32_t a_signs_min, dap_hash_fast_t *a_pkey_hashes, - size_t a_pkey_hashes_count); + size_t a_pkey_hashes_count, const char *a_tag_str); /** * Create item dap_chain_tx_sig_t diff --git a/modules/common/include/dap_chain_datum_tx_out_cond.h b/modules/common/include/dap_chain_datum_tx_out_cond.h index 1014316a432d0caf3b3756da7ab321ee3e09f88d..ddc1dc6a87d08c9fb8252c40faf097fe96c3d26d 100644 --- a/modules/common/include/dap_chain_datum_tx_out_cond.h +++ b/modules/common/include/dap_chain_datum_tx_out_cond.h @@ -56,6 +56,8 @@ typedef byte_t dap_chain_tx_out_cond_subtype_t; #define DAP_CHAIN_TX_OUT_COND_TSD_ADDR 0xf001 // Chain hash #define DAP_CHAIN_TX_OUT_COND_TSD_HASH 0xf002 +// Custom str +#define DAP_CHAIN_TX_OUT_COND_TSD_STR 0xf003 /** * @struct dap_chain_tx_out diff --git a/modules/common/include/dap_chain_datum_tx_voting.h b/modules/common/include/dap_chain_datum_tx_voting.h index a87c67824ce16362b1e40c0822a3b47737e94fe9..add632997a948cee77c4aeb2d464f4ff9979ed03 100644 --- a/modules/common/include/dap_chain_datum_tx_voting.h +++ b/modules/common/include/dap_chain_datum_tx_voting.h @@ -43,7 +43,8 @@ typedef enum dap_chain_datum_voting_tsd_type { VOTING_TSD_TYPE_MAX_VOTES_COUNT, VOTING_TSD_TYPE_DELEGATED_KEY_REQUIRED, VOTING_TSD_TYPE_VOTE_CHANGING_ALLOWED, - VOTING_TSD_TYPE_VOTE_TX_COND + VOTING_TSD_TYPE_VOTE_TX_COND, + VOTING_TSD_TYPE_TOKEN } dap_chain_datum_voting_tsd_type_t; typedef struct dap_chain_tx_voting { @@ -63,13 +64,14 @@ typedef struct dap_chain_tx_vote { typedef struct dap_chain_datum_tx_voting_params { - char *voting_question; + char *voting_question; dap_list_t *answers_list; uint8_t answers_count; dap_time_t voting_expire; uint64_t votes_max_count; bool delegate_key_required; bool vote_changing_allowed; + char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX]; } dap_chain_datum_tx_voting_params_t; #ifdef __cplusplus @@ -86,6 +88,7 @@ dap_chain_tx_tsd_t* dap_chain_datum_voting_max_votes_count_tsd_create(uint64_t a dap_chain_tx_tsd_t *dap_chain_datum_voting_delegated_key_required_tsd_create(bool a_delegated_key_required); dap_chain_tx_tsd_t* dap_chain_datum_voting_vote_changing_allowed_tsd_create(bool a_vote_changing_allowed); dap_chain_tx_tsd_t* dap_chain_datum_voting_vote_tx_cond_tsd_create(dap_chain_hash_fast_t a_tx_hash, int a_out_idx); +dap_chain_tx_tsd_t *dap_chain_datum_voting_token_tsd_create(const char *a_token_ticker); dap_chain_tx_voting_t *dap_chain_datum_tx_item_voting_create(void); json_object *dap_chain_datum_tx_item_voting_tsd_to_json(dap_chain_datum_tx_t* a_tx); @@ -96,4 +99,4 @@ json_object *dap_chain_datum_tx_item_vote_to_json(dap_chain_tx_vote_t *a_vote, d #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/modules/consensus/esbocs/dap_chain_cs_esbocs.c b/modules/consensus/esbocs/dap_chain_cs_esbocs.c index 88546b2272cf201be28153caa0099fd8a14a22dc..29c1c0100181c26d9ca10b094636cd8280f67336 100644 --- a/modules/consensus/esbocs/dap_chain_cs_esbocs.c +++ b/modules/consensus/esbocs/dap_chain_cs_esbocs.c @@ -38,6 +38,7 @@ along with any CellFrame SDK based project. If not, see <http://www.gnu.org/lic #include "dap_chain_net_srv_stake_pos_delegate.h" #include "dap_chain_ledger.h" #include "dap_chain_node_cli_cmd.h" +#include "dap_sign.h" #define LOG_TAG "dap_chain_cs_esbocs" @@ -124,6 +125,16 @@ DAP_STATIC_INLINE size_t s_get_esbocs_message_size(dap_chain_esbocs_message_t *a return sizeof(*a_message) + a_message->hdr.sign_size + a_message->hdr.message_size; } +static dap_pkey_t *s_get_pkey(dap_sign_t *a_sign, dap_chain_net_id_t a_net_id) +{ + if (dap_sign_is_use_pkey_hash(a_sign)) { + dap_hash_fast_t l_pkey_hash = {}; + dap_sign_get_pkey_hash(a_sign, &l_pkey_hash); + return dap_chain_net_srv_stake_get_pkey_by_hash(a_net_id, &l_pkey_hash); + } + return NULL; +} + static dap_chain_esbocs_session_t *s_session_items; struct precached_key { @@ -2584,8 +2595,8 @@ static void s_session_packet_in(dap_chain_esbocs_session_t *a_session, dap_chain } // check candidate's sign size_t l_offset = dap_chain_block_get_sign_offset(l_store->candidate, l_store->candidate_size); - int l_sign_verified = dap_sign_verify(l_candidate_sign, l_store->candidate, - l_offset + sizeof(l_store->candidate->hdr)); + int l_sign_verified = dap_sign_verify_by_pkey(l_candidate_sign, l_store->candidate, + l_offset + sizeof(l_store->candidate->hdr), s_get_pkey(l_candidate_sign, l_session->chain->net_id)); if (l_sign_verified != 0) { if (!l_candidate_hash_str) l_candidate_hash_str = dap_chain_hash_fast_to_str_static(l_candidate_hash); @@ -2774,6 +2785,9 @@ static size_t s_callback_block_sign(dap_chain_cs_blocks_t *a_blocks, dap_chain_b static uint64_t s_get_precached_key_hash(dap_list_t **a_precached_keys_list, dap_sign_t *a_source_sign, dap_hash_fast_t *a_result) { + if (DAP_SIGN_GET_PKEY_HASHING_FLAG(a_source_sign->header.hash_type)) + return !dap_sign_get_pkey_hash(a_source_sign, a_result); + bool l_found = false; struct precached_key *l_key = NULL; dap_list_t *l_cur; @@ -2807,7 +2821,11 @@ static uint64_t s_get_precached_key_hash(dap_list_t **a_precached_keys_list, dap l_key_new->pkey_size = a_source_sign->header.sign_pkey_size; l_key_new->frequency = 0; memcpy(l_key_new->sign_pkey, dap_sign_get_pkey(a_source_sign, NULL), l_key_new->pkey_size); - dap_sign_get_pkey_hash(a_source_sign, &l_key_new->pkey_hash); + if (DAP_SIGN_GET_PKEY_HASHING_FLAG(a_source_sign->header.hash_type)) { + memcpy(&l_key_new->pkey_hash, l_key_new->sign_pkey, sizeof(dap_hash_fast_t)); + } else { + dap_sign_get_pkey_hash(a_source_sign, &l_key_new->pkey_hash); + } *a_precached_keys_list = dap_list_append(*a_precached_keys_list, l_key_new); if (a_result) *a_result = l_key_new->pkey_hash; @@ -2918,7 +2936,7 @@ static int s_callback_block_verify(dap_chain_cs_blocks_t *a_blocks, dap_chain_bl break; } } - if (!dap_sign_verify(l_sign, l_block, l_block_excl_sign_size)) + if (!dap_sign_verify_by_pkey(l_sign, l_block, l_block_excl_sign_size, s_get_pkey(l_sign, l_esbocs->chain->net_id))) l_signs_verified_count++; } DAP_DELETE(l_signs); diff --git a/modules/net/dap_chain_ledger.c b/modules/net/dap_chain_ledger.c index 1a883dd8dd234777d93cd68e95586b140b31d11d..f5859e334399a6f45f3b9263500c9ded1b788b64 100644 --- a/modules/net/dap_chain_ledger.c +++ b/modules/net/dap_chain_ledger.c @@ -288,8 +288,6 @@ typedef struct dap_ledger_private { #define PVT(a) ( (dap_ledger_private_t *) a->_internal ) -static dap_ledger_tx_item_t* tx_item_find_by_addr(dap_ledger_t *a_ledger, - const dap_chain_addr_t *a_addr, const char * a_token, dap_chain_hash_fast_t *a_tx_first_hash); static void s_threshold_emissions_proc( dap_ledger_t * a_ledger); static void s_threshold_txs_proc( dap_ledger_t * a_ledger); static void s_threshold_txs_free(dap_ledger_t *a_ledger); @@ -352,7 +350,7 @@ static bool s_tag_check_transfer(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a //crosschain transfer //regular transfer //comission transfer - + // fee transfer: in_cond item linked to out_cond_fee if (a_items_grp->items_in_cond) { @@ -3268,6 +3266,9 @@ const char *dap_ledger_tx_action_str(dap_chain_tx_tag_action_type_t a_tag) if (a_tag == DAP_CHAIN_TX_TAG_ACTION_CHANGE) return "change"; if (a_tag == DAP_CHAIN_TX_TAG_ACTION_VOTING) return "voting"; if (a_tag == DAP_CHAIN_TX_TAG_ACTION_VOTE) return "vote"; + if (a_tag == DAP_CHAIN_TX_TAG_ACTION_EMIT_DELEGATE_HOLD) return "hold"; + if (a_tag == DAP_CHAIN_TX_TAG_ACTION_EMIT_DELEGATE_TAKE) return "take"; + if (a_tag == DAP_CHAIN_TX_TAG_ACTION_EMIT_DELEGATE_REFILL) return "refill"; return "WTFSUBTAG"; @@ -3288,7 +3289,9 @@ dap_chain_tx_tag_action_type_t dap_ledger_tx_action_str_to_action_t(const char * if (strcmp("extend", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_EXTEND; if (strcmp("close", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_CLOSE; if (strcmp("change", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_CHANGE; - + if (strcmp("hold", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_EMIT_DELEGATE_HOLD; + if (strcmp("take", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_EMIT_DELEGATE_TAKE; + if (strcmp("refill", a_str) == 0) return DAP_CHAIN_TX_TAG_ACTION_EMIT_DELEGATE_REFILL; return DAP_CHAIN_TX_TAG_ACTION_UNKNOWN; } @@ -4190,13 +4193,14 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger, debug_if(s_debug_more, L_WARNING, "Verificator check error for voting item"); l_err_num = DAP_LEDGER_TX_CHECK_NO_VERIFICATOR_SET; } - // if (a_tag) + // if (a_tag) // a_tag->uint64 = DAP_CHAIN_TX_TAG_ACTION_VOTING; - if (a_action) + if (a_action) *a_action = DAP_CHAIN_TX_TAG_ACTION_VOTING; } else if ( dap_chain_datum_tx_item_get(a_tx, NULL, NULL, TX_ITEM_TYPE_VOTE, NULL) ) { if (s_voting_callbacks.voting_callback) { - if ((l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTE, a_tx, a_tx_hash, false))) { + if (!s_check_hal(a_ledger, a_tx_hash) && + (l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTE, a_tx, a_tx_hash, false))) { debug_if(s_debug_more, L_WARNING, "Verificator check error %d for vote", l_err_num); l_err_num = DAP_LEDGER_TX_CHECK_VERIFICATOR_CHECK_FAILURE; } @@ -4204,8 +4208,6 @@ static int s_tx_cache_check(dap_ledger_t *a_ledger, debug_if(s_debug_more, L_WARNING, "Verificator check error for vote item"); l_err_num = DAP_LEDGER_TX_CHECK_NO_VERIFICATOR_SET; } - // if (a_tag) - // a_tag->uint64 = DAP_CHAIN_TX_TAG_ACTION_VOTE; if (a_action) *a_action = DAP_CHAIN_TX_TAG_ACTION_VOTE; } @@ -4616,7 +4618,8 @@ int dap_ledger_tx_add(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_ha else if (l_action == DAP_CHAIN_TX_TAG_ACTION_VOTE) l_err_num = s_voting_callbacks.voting_callback(a_ledger, TX_ITEM_TYPE_VOTE, a_tx, a_tx_hash, true); } - assert(!l_err_num); + if (!s_check_hal(a_ledger, a_tx_hash)) + assert(!l_err_num); // add transaction to the cache list dap_ledger_tx_item_t *l_tx_item = DAP_NEW_Z_SIZE(dap_ledger_tx_item_t, sizeof(dap_ledger_tx_item_t) + l_outs_count * sizeof(dap_chain_hash_fast_t)); @@ -5273,8 +5276,8 @@ uint256_t dap_ledger_calc_balance_full(dap_ledger_t *a_ledger, const dap_chain_a * a_public_key_size[in] public key size * a_tx_first_hash [in/out] hash of the initial transaction/ found transaction, if 0 start from the beginning */ -static dap_ledger_tx_item_t *tx_item_find_by_addr(dap_ledger_t *a_ledger, const dap_chain_addr_t *a_addr, - const char * a_token, dap_chain_hash_fast_t *a_tx_first_hash) +dap_chain_datum_tx_t *dap_ledger_tx_find_by_addr(dap_ledger_t *a_ledger, const char *a_token, + const dap_chain_addr_t *a_addr, dap_chain_hash_fast_t *a_tx_first_hash) { if(!a_addr || !a_tx_first_hash) return NULL; @@ -5331,20 +5334,7 @@ static dap_ledger_tx_item_t *tx_item_find_by_addr(dap_ledger_t *a_ledger, const break; } pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); - return is_tx_found ? l_iter_current : NULL; -} - -/** - * @brief dap_ledger_tx_find_by_addr - * @param a_addr - * @param a_tx_first_hash - * @return - */ - dap_chain_datum_tx_t* dap_ledger_tx_find_by_addr(dap_ledger_t *a_ledger , const char * a_token , - const dap_chain_addr_t *a_addr, dap_chain_hash_fast_t *a_tx_first_hash) -{ - dap_ledger_tx_item_t* l_tx_item = tx_item_find_by_addr(a_ledger, a_addr, a_token, a_tx_first_hash); - return (l_tx_item) ? l_tx_item->tx : NULL; + return is_tx_found ? l_iter_current->tx : NULL; } bool dap_ledger_tx_check_recipient(dap_ledger_t* a_ledger, dap_chain_hash_fast_t* a_tx_prev_hash, dap_chain_addr_t *a_addr) @@ -5374,52 +5364,6 @@ static dap_ledger_tx_item_t *tx_item_find_by_addr(dap_ledger_t *a_ledger, const return false; } -/** - * Get the transaction in the cache by the public key that signed the transaction, - * starting from the next hash after a_tx_first_hash - * - * a_public_key[in] public key that signed the transaction - * a_public_key_size[in] public key size - * a_tx_first_hash [in/out] hash of the initial transaction/ found transaction, if 0 start from the beginning - */ -const dap_chain_datum_tx_t* dap_ledger_tx_find_by_pkey(dap_ledger_t *a_ledger, - char *a_public_key, size_t a_public_key_size, dap_chain_hash_fast_t *a_tx_first_hash) -{ - if(!a_public_key || !a_tx_first_hash) - return NULL; - dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); - dap_chain_datum_tx_t *l_cur_tx = NULL; - bool is_null_hash = dap_hash_fast_is_blank(a_tx_first_hash); - bool is_search_enable = is_null_hash; - dap_ledger_tx_item_t *l_iter_current, *l_item_tmp; - pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); - HASH_ITER(hh, l_ledger_pvt->ledger_items , l_iter_current, l_item_tmp) { - dap_chain_datum_tx_t *l_tx_tmp = l_iter_current->tx; - dap_chain_hash_fast_t *l_tx_hash_tmp = &l_iter_current->tx_hash_fast; - // start searching from the next hash after a_tx_first_hash - if (!is_search_enable) { - is_search_enable = dap_hash_fast_compare(l_tx_hash_tmp, a_tx_first_hash); - continue; - } - // Get sign item from transaction - dap_chain_tx_sig_t *l_tx_sig = (dap_chain_tx_sig_t*) dap_chain_datum_tx_item_get(l_tx_tmp, NULL, - NULL, TX_ITEM_TYPE_SIG, NULL); - // Get dap_sign_t from item - dap_sign_t *l_sig = dap_chain_datum_tx_item_sign_get_sig(l_tx_sig); - if(l_sig) { - // compare public key in transaction with a_public_key - if(a_public_key_size == l_sig->header.sign_pkey_size && - !memcmp(a_public_key, l_sig->pkey_n_sign, a_public_key_size)) { - l_cur_tx = l_tx_tmp; - memcpy(a_tx_first_hash, l_tx_hash_tmp, sizeof(dap_chain_hash_fast_t)); - break; - } - } - } - pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); - return l_cur_tx; -} - /** * @brief Get all transactions from the cache with the out_cond item * @param a_ledger @@ -5442,93 +5386,6 @@ dap_list_t* dap_ledger_tx_cache_find_out_cond_all(dap_ledger_t *a_ledger, dap_ch return l_ret; } - -/** - * Get the transaction in the cache with the out_cond item - * - * a_addr[in] wallet address, whose owner can use the service - */ -dap_chain_datum_tx_t* dap_ledger_tx_cache_find_out_cond(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_cond_type, - dap_chain_hash_fast_t *a_tx_first_hash, dap_chain_tx_out_cond_t **a_out_cond, int *a_out_cond_idx, char *a_token_ticker) -{ - if (!a_tx_first_hash) - return NULL; - dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); - dap_chain_datum_tx_t *l_cur_tx = NULL; - bool is_null_hash = dap_hash_fast_is_blank(a_tx_first_hash); - bool is_search_enable = is_null_hash; - dap_ledger_tx_item_t *l_iter_current = NULL, *l_item_tmp = NULL; - dap_chain_tx_out_cond_t *l_tx_out_cond = NULL; - int l_tx_out_cond_idx = 0; - pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); - HASH_ITER(hh, l_ledger_pvt->ledger_items, l_iter_current, l_item_tmp) { - dap_chain_datum_tx_t *l_tx_tmp = l_iter_current->tx; - dap_chain_hash_fast_t *l_tx_hash_tmp = &l_iter_current->tx_hash_fast; - // start searching from the next hash after a_tx_first_hash - if (!is_search_enable) { - is_search_enable = dap_hash_fast_compare(l_tx_hash_tmp, a_tx_first_hash); - continue; - } - // Get out_cond item from transaction - l_tx_out_cond = dap_chain_datum_tx_out_cond_get(l_tx_tmp, a_cond_type, &l_tx_out_cond_idx); - - if(l_tx_out_cond) { - l_cur_tx = l_tx_tmp; - memcpy(a_tx_first_hash, l_tx_hash_tmp, sizeof(dap_chain_hash_fast_t)); - if (a_token_ticker) { - strcpy(a_token_ticker, l_iter_current->cache_data.token_ticker); - } - break; - } - } - pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); - if (a_out_cond) { - *a_out_cond = l_tx_out_cond; - } - if (a_out_cond_idx) { - *a_out_cond_idx = l_tx_out_cond_idx; - } - return l_cur_tx; -} - -/** - * Get the value from all transactions in the cache with out_cond item - * - * a_addr[in] wallet address, whose owner can use the service - * a_sign [in] signature of a_addr hash for check valid key - * a_sign_size [in] signature size - * - * a_public_key[in] public key that signed the transaction - * a_public_key_size[in] public key size - */ -uint256_t dap_ledger_tx_cache_get_out_cond_value(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_cond_type, - dap_chain_addr_t *a_addr, dap_chain_tx_out_cond_t **tx_out_cond) - -{ - uint256_t l_ret_value = {}; - - dap_chain_datum_tx_t *l_tx_tmp; - dap_chain_hash_fast_t l_tx_first_hash = { 0 }; // start hash - /* size_t l_pub_key_size = a_key_from->pub_key_data_size; - uint8_t *l_pub_key = dap_enc_key_serialize_pub_key(a_key_from, &l_pub_key_size);*/ - dap_chain_tx_out_cond_t *l_tx_out_cond; - // Find all transactions - do { - l_tx_tmp = dap_ledger_tx_cache_find_out_cond(a_ledger, a_cond_type, &l_tx_first_hash, &l_tx_out_cond, NULL, NULL); - // Get out_cond item from transaction - if(l_tx_tmp) { - UNUSED(a_addr); - // TODO check relations a_addr with cond_data and public key - if(l_tx_out_cond) { - l_ret_value = l_tx_out_cond->header.value; - if(tx_out_cond) - *tx_out_cond = l_tx_out_cond; - } - } - } while(l_tx_tmp); - return l_ret_value; -} - /** * @brief dap_ledger_get_list_tx_outs_with_val * @param a_ledger @@ -5784,7 +5641,7 @@ dap_chain_datum_tx_t *dap_ledger_datum_iter_get_last(dap_ledger_datum_iter_t *a_ { dap_ledger_private_t *l_ledger_pvt = PVT(a_iter->net->pub.ledger); pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); - a_iter->cur_ledger_tx_item = l_ledger_pvt->ledger_items->hh.prev; + a_iter->cur_ledger_tx_item = HASH_LAST(l_ledger_pvt->ledger_items); a_iter->cur = ((dap_ledger_tx_item_t *)(a_iter->cur_ledger_tx_item))->tx; a_iter->cur_hash = ((dap_ledger_tx_item_t *)(a_iter->cur_ledger_tx_item))->tx_hash_fast; a_iter->is_unspent = ((dap_ledger_tx_item_t *)(a_iter->cur_ledger_tx_item))->cache_data.ts_spent ? false : true; @@ -5793,72 +5650,120 @@ dap_chain_datum_tx_t *dap_ledger_datum_iter_get_last(dap_ledger_datum_iter_t *a_ return a_iter->cur; } - /** - * @brief dap_ledger_get_list_tx_cond_outs_with_val - * @param a_ledger - * @param a_token_ticker - * @param a_addr_from - * @param a_subtype - * @param a_value_need - * @param a_value_transfer - * @return + * Get the transaction in the cache by the addr in sig item + * + * a_addr[in] public key that signed the transaction + * a_tx_first_hash [in/out] hash of the initial transaction/ found transaction, if 0 start from the beginning */ -dap_list_t *dap_ledger_get_list_tx_cond_outs_with_val(dap_ledger_t *a_ledger, const char *a_token_ticker, const dap_chain_addr_t *a_addr_from, - dap_chain_tx_out_cond_subtype_t a_subtype, uint256_t a_value_need, uint256_t *a_value_transfer) +dap_chain_tx_out_cond_t *dap_ledger_out_cond_unspent_find_by_addr(dap_ledger_t *a_ledger, const char *a_token, dap_chain_tx_out_cond_subtype_t a_subtype, + const dap_chain_addr_t *a_addr, dap_chain_hash_fast_t *a_tx_first_hash, int *a_out_idx) { - dap_list_t *l_list_used_out = NULL; // list of transaction with 'out' items - dap_chain_hash_fast_t l_tx_cur_hash = { 0 }; - uint256_t l_value_transfer = { }; - dap_chain_datum_tx_t *l_tx; - while( compare256(l_value_transfer, a_value_need) == -1 - && ( l_tx = dap_ledger_tx_find_by_addr(a_ledger, a_token_ticker, a_addr_from, &l_tx_cur_hash)) ) - { - byte_t *it; size_t l_size; int i, l_out_idx_tmp = -1; - TX_ITEM_ITER_TX_TYPE(it, TX_ITEM_TYPE_OUT_COND, l_size, i, l_tx) { - ++l_out_idx_tmp; - dap_chain_tx_out_cond_t *l_out_cond = (dap_chain_tx_out_cond_t*) it; - if (a_subtype != l_out_cond->header.subtype || IS_ZERO_256(l_out_cond->header.value) ) - continue; - dap_chain_tx_used_out_item_t *l_item = DAP_NEW_Z(dap_chain_tx_used_out_item_t); - *l_item = (dap_chain_tx_used_out_item_t) { l_tx_cur_hash, (uint32_t)l_out_idx_tmp, l_out_cond->header.value }; - l_list_used_out = dap_list_append(l_list_used_out, l_item); - SUM_256_256(l_value_transfer, l_item->value, &l_value_transfer); - // already accumulated the required value, finish the search for 'out' items - if ( compare256(l_value_transfer, a_value_need) != -1 ) { - break; + if (!a_addr || !a_token) + return NULL; + dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); + dap_chain_tx_out_cond_t *ret = NULL; + dap_ledger_tx_item_t *l_iter_start = NULL, *it; + pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); + if (a_tx_first_hash && !dap_hash_fast_is_blank(a_tx_first_hash)) { + HASH_FIND(hh, l_ledger_pvt->ledger_items, a_tx_first_hash, sizeof(dap_hash_t), l_iter_start); + if (!l_iter_start || !l_iter_start->hh.next) { + pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); + return NULL; + } + // start searching from the next hash after a_tx_first_hash + l_iter_start = l_iter_start->hh.next; + } else + l_iter_start = l_ledger_pvt->ledger_items; + for (it = l_iter_start; it; it = it->hh.next, ret = NULL) { + // If a_token is setup we check if its not our token - miss it + if (*it->cache_data.token_ticker && dap_strcmp(it->cache_data.token_ticker, a_token)) + continue; + // Get 'out_cond' item from transaction + byte_t *l_item; size_t l_size; int i, l_out_idx = 0; + TX_ITEM_ITER_TX_TYPE(l_item, TX_ITEM_TYPE_OUT_ALL, l_size, i, it->tx) { + if (*l_item == TX_ITEM_TYPE_OUT_COND) { + dap_chain_tx_out_cond_subtype_t l_subtype = ((dap_chain_tx_out_cond_t *)l_item)->header.subtype; + if (l_subtype == a_subtype || + (a_subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_ALL && l_subtype != DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE)) { + ret = (dap_chain_tx_out_cond_t *)l_item; + break; + } } + l_out_idx++; + } + // Don't return regular tx or spent conditions + if (!ret || !dap_hash_fast_is_blank(&it->cache_data.tx_hash_spent_fast[l_out_idx])) + continue; + dap_hash_fast_t l_owner_tx_hash = dap_ledger_get_first_chain_tx_hash(a_ledger, it->tx, a_subtype); + dap_chain_datum_tx_t *l_tx = dap_hash_fast_is_blank(&l_owner_tx_hash) ? it->tx + : dap_ledger_tx_find_by_hash(a_ledger, &l_owner_tx_hash); + if (!l_tx) { + log_it(L_ERROR, "Can't find owner for tx %s", dap_hash_fast_to_str_static(&it->tx_hash_fast)); + continue; + } + // Get sign item from transaction + dap_chain_tx_sig_t *l_tx_sig = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get(l_tx, NULL, NULL, TX_ITEM_TYPE_SIG, NULL); + // Get dap_sign_t from item + dap_sign_t *l_sign = dap_chain_datum_tx_item_sign_get_sig(l_tx_sig); + // compare public key in transaction with a_public_key + dap_chain_hash_fast_t l_sign_hash = {}; + dap_sign_get_pkey_hash(l_sign, &l_sign_hash); + if (dap_hash_fast_compare(&l_sign_hash, &a_addr->data.hash_fast)) { + if (a_tx_first_hash) + *a_tx_first_hash = it->tx_hash_fast; + if (a_out_idx) + *a_out_idx = l_out_idx; + break; } } - return compare256(l_value_transfer, a_value_need) > -1 && l_list_used_out - ? ({ if (a_value_transfer) *a_value_transfer = l_value_transfer; l_list_used_out; }) - : ( dap_list_free_full(l_list_used_out, NULL), NULL ); + pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); + return ret; } -dap_list_t *dap_ledger_get_list_tx_cond_outs(dap_ledger_t *a_ledger, const char *a_token_ticker, const dap_chain_addr_t *a_addr_from, - dap_chain_tx_out_cond_subtype_t a_subtype, uint256_t *a_value_transfer) +dap_list_t *dap_ledger_get_list_tx_cond_outs(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_subtype, + const char *a_token_ticker, const dap_chain_addr_t *a_addr_from) { dap_list_t *l_list_used_out = NULL; // list of transaction with 'out' items dap_chain_hash_fast_t l_tx_cur_hash = { }; - uint256_t l_value_transfer = { }; - dap_chain_datum_tx_t *l_tx; - while(( l_tx = dap_ledger_tx_find_by_addr(a_ledger, a_token_ticker, a_addr_from, &l_tx_cur_hash) )) { - byte_t *it; size_t l_size; int i, l_out_idx_tmp = -1; - TX_ITEM_ITER_TX_TYPE(it, TX_ITEM_TYPE_OUT_COND, l_size, i, l_tx) { - ++l_out_idx_tmp; - dap_chain_tx_out_cond_t *l_out_cond = (dap_chain_tx_out_cond_t*)it; - if ( a_subtype != l_out_cond->header.subtype || IS_ZERO_256(l_out_cond->header.value) ) - continue; - dap_chain_tx_used_out_item_t *l_item = DAP_NEW_Z(dap_chain_tx_used_out_item_t); - *l_item = (dap_chain_tx_used_out_item_t) { l_tx_cur_hash, (uint32_t)l_out_idx_tmp, l_out_cond->header.value }; - l_list_used_out = dap_list_append(l_list_used_out, l_item); - SUM_256_256(l_value_transfer, l_item->value, &l_value_transfer); - } + int l_out_idx; + dap_chain_tx_out_cond_t *l_cond; + while ( (l_cond = dap_ledger_out_cond_unspent_find_by_addr(a_ledger, a_token_ticker, a_subtype, a_addr_from, &l_tx_cur_hash, &l_out_idx)) ) { + dap_chain_tx_used_out_item_t *l_item = DAP_NEW_Z(dap_chain_tx_used_out_item_t); + *l_item = (dap_chain_tx_used_out_item_t) { l_tx_cur_hash, (uint32_t)l_out_idx, l_cond->header.value }; + l_list_used_out = dap_list_append(l_list_used_out, l_item); } + return l_list_used_out; +} - return l_list_used_out - ? ({ if (a_value_transfer) *a_value_transfer = l_value_transfer; l_list_used_out; }) - : ( dap_list_free_full(l_list_used_out, NULL), NULL ); +bool dap_ledger_check_condition_owner(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_hash, dap_chain_tx_out_cond_subtype_t a_cond_subtype, + int a_out_idx, dap_sign_t *a_owner_sign) +{ + dap_return_val_if_fail(a_ledger && a_tx_hash && a_owner_sign, false); + // Get first tx + dap_chain_datum_tx_t *l_check_tx = dap_ledger_tx_find_by_hash(a_ledger, a_tx_hash); + if (!l_check_tx) { + log_it(L_ERROR, "Can't find tx %s", dap_hash_fast_to_str_static(a_tx_hash)); + return false; + } + if (!dap_chain_datum_tx_out_cond_get(l_check_tx, a_cond_subtype, NULL)) { + log_it(L_ERROR, "Can't find owner for tx %s", dap_hash_fast_to_str_static(a_tx_hash)); + return false; + } + dap_hash_fast_t l_first_tx_hash = dap_ledger_get_first_chain_tx_hash(a_ledger, l_check_tx, a_cond_subtype); + dap_chain_datum_tx_t *l_first_tx = dap_hash_fast_is_blank(&l_first_tx_hash) ? l_check_tx + : dap_ledger_tx_find_by_hash(a_ledger, &l_first_tx_hash); + if (!l_first_tx) { + log_it(L_ERROR, "Can't find owner tx %s", dap_hash_fast_to_str_static(&l_first_tx_hash)); + return false; + } + dap_chain_tx_sig_t *l_first_tx_sig = (dap_chain_tx_sig_t *)dap_chain_datum_tx_item_get(l_first_tx, NULL, NULL, TX_ITEM_TYPE_SIG, NULL); + dap_sign_t *l_sign = dap_chain_datum_tx_item_sign_get_sig((dap_chain_tx_sig_t *)l_first_tx_sig); + if (!l_sign) { + log_it(L_ERROR, "Can't find signature for tx %s", dap_hash_fast_to_str_static(&l_first_tx_hash)); + return false; + } + return dap_sign_compare_pkeys(a_owner_sign, l_sign); } void dap_ledger_tx_add_notify(dap_ledger_t *a_ledger, dap_ledger_tx_add_notify_t a_callback, void *a_arg) { @@ -5915,39 +5820,3 @@ dap_chain_token_ticker_str_t dap_ledger_tx_calculate_main_ticker_(dap_ledger_t * *a_ledger_rc = l_rc; return l_ret; } - -/** - * @brief dap_ledger_find_pkey_by_hash - * @param a_ledger to search - * @param a_pkey_hash - pkey hash - * @return pointer to dap_pkey_t if finded, other - NULL - */ -dap_pkey_t *dap_ledger_find_pkey_by_hash(dap_ledger_t *a_ledger, dap_chain_hash_fast_t *a_pkey_hash) -{ - dap_return_val_if_pass(!a_pkey_hash || dap_hash_fast_is_blank(a_pkey_hash), NULL); - - dap_ledger_private_t *l_ledger_pvt = PVT(a_ledger); - dap_ledger_tx_item_t *l_iter_current, *l_item_tmp; - dap_pkey_t *l_ret = NULL; - pthread_rwlock_rdlock(&l_ledger_pvt->ledger_rwlock); - HASH_ITER(hh, l_ledger_pvt->ledger_items , l_iter_current, l_item_tmp) { - dap_chain_datum_tx_t *l_tx_tmp = l_iter_current->tx; - dap_chain_hash_fast_t *l_tx_hash_tmp = &l_iter_current->tx_hash_fast; - // Get sign item from transaction - dap_chain_tx_sig_t *l_tx_sig = (dap_chain_tx_sig_t*) dap_chain_datum_tx_item_get(l_tx_tmp, NULL, - NULL, TX_ITEM_TYPE_SIG, NULL); - // Get dap_sign_t from item - dap_sign_t *l_sig = dap_chain_datum_tx_item_sign_get_sig(l_tx_sig); - if(l_sig) { - // compare public key in transaction with a_public_key - dap_chain_hash_fast_t l_sign_hash = {}; - dap_sign_get_pkey_hash(l_sig, &l_sign_hash); - if(!memcmp(&l_sign_hash, a_pkey_hash, sizeof(dap_chain_hash_fast_t))) { - l_ret = dap_pkey_get_from_sign(l_sig); - break; - } - } - } - pthread_rwlock_unlock(&l_ledger_pvt->ledger_rwlock); - return l_ret; -} diff --git a/modules/net/dap_chain_net.c b/modules/net/dap_chain_net.c index 6b6d8b2c7e09e0c3100ec656a62e17f1b32ececf..a1950dc3422c605aa006732f37d2464b8f395829 100644 --- a/modules/net/dap_chain_net.c +++ b/modules/net/dap_chain_net.c @@ -108,6 +108,8 @@ #include "dap_http_ban_list_client.h" #include "dap_net.h" #include "dap_context.h" +#include "dap_chain_cs_esbocs.h" +#include "dap_chain_policy.h" #include <stdio.h> #include <sys/types.h> @@ -775,6 +777,39 @@ static dap_chain_net_t *s_net_new(const char *a_net_name, dap_config_t *a_cfg) l_net_name_str), NULL; log_it (L_NOTICE, "Node role \"%s\" selected for network '%s'", a_node_role, l_net_name_str); + + if (dap_chain_policy_net_add(l_ret->pub.id.uint64)) { + log_it(L_ERROR, "Can't add net %s to policy module", l_ret->pub.name); + DAP_DEL_MULTY(l_ret->pub.name, l_ret); + return NULL; + } + // activate policy + uint64_t l_policy_num = dap_config_get_item_uint64(a_cfg, "policy", "activate"); + dap_chain_policy_t *l_new_policy = NULL; + if (l_policy_num) { + if (!dap_chain_policy_num_is_valid(l_policy_num)) { + log_it(L_ERROR, "Can't add policy CN-%"DAP_UINT64_FORMAT_U, l_policy_num); + } else { + dap_chain_policy_t *l_new_policy = DAP_NEW_Z_SIZE_RET_VAL_IF_FAIL(dap_chain_policy_t, sizeof(dap_chain_policy_activate_t), NULL, l_ret->pub.name, l_ret); + l_new_policy->type = DAP_CHAIN_POLICY_ACTIVATE; + l_new_policy->version = DAP_CHAIN_POLICY_VERSION; + ((dap_chain_policy_activate_t *)(l_new_policy->data))->num = l_policy_num; + l_new_policy->flags = DAP_FLAG_ADD(l_new_policy->flags, DAP_CHAIN_POLICY_FLAG_ACTIVATE_BY_CONFIG); + dap_chain_policy_add(l_new_policy, l_ret->pub.id.uint64); + } + } + // deactivate policy + uint16_t l_policy_count = 0; + const char **l_policy_str = dap_config_get_array_str(a_cfg, "policy", "deactivate", &l_policy_count); + for (uint16_t i = 0; i < l_policy_count; ++i) { + l_policy_num = strtoull(l_policy_str[i], NULL, 10); + if (!dap_chain_policy_num_is_valid(l_policy_num)) { + log_it(L_ERROR, "Can't add policy CN-%"DAP_UINT64_FORMAT_U" to exception list", l_policy_num); + } else { + dap_chain_policy_add_to_exception_list(l_policy_num, l_ret->pub.id.uint64); + } + } + l_ret->pub.config = a_cfg; l_ret->pub.gdb_groups_prefix = dap_config_get_item_str_default( a_cfg, "general", "gdb_groups_prefix", dap_config_get_item_str(a_cfg, "general", "name") ); @@ -1783,6 +1818,7 @@ void dap_chain_net_deinit() dap_chain_net_delete(l_net); } dap_http_ban_list_client_deinit(); + dap_chain_policy_deinit(); } /** @@ -1820,6 +1856,7 @@ void dap_chain_net_delete(dap_chain_net_t *a_net) dap_chain_delete(l_cur); } } + dap_chain_policy_net_remove(a_net->pub.id.uint64); HASH_DEL(s_nets_by_name, a_net); HASH_DELETE(hh2, s_nets_by_id, a_net); DAP_DELETE(a_net); @@ -2511,7 +2548,7 @@ DAP_INLINE dap_chain_net_state_t dap_chain_net_get_state (dap_chain_net_t *a_net return PVT(a_net)->state; } -dap_chain_cell_id_t * dap_chain_net_get_cur_cell( dap_chain_net_t *a_net) +dap_chain_cell_id_t *dap_chain_net_get_cur_cell( dap_chain_net_t *a_net) { return PVT(a_net)->node_info ? &PVT(a_net)->node_info->cell_id: 0; } @@ -2865,7 +2902,7 @@ int dap_chain_datum_remove(dap_chain_t *a_chain, dap_chain_datum_t *a_datum, siz return 0; } -bool dap_chain_net_get_load_mode(dap_chain_net_t * a_net) +DAP_INLINE bool dap_chain_net_get_load_mode(dap_chain_net_t * a_net) { return PVT(a_net)->state == NET_STATE_LOADING; } @@ -3111,7 +3148,6 @@ static void s_ch_out_pkt_callback(dap_stream_ch_t *a_ch, uint8_t a_type, const v default: break; } - l_net_pvt->sync_context.stage_last_activity = dap_time_now(); debug_if(s_debug_more, L_DEBUG, "Sent OUT sync packet type %hhu size %zu to addr " NODE_ADDR_FP_STR, a_type, a_data_size, NODE_ADDR_FP_ARGS_S(a_ch->stream->node)); } diff --git a/modules/net/dap_chain_net_decree.c b/modules/net/dap_chain_net_decree.c index 509e4e6af5089c1392e43ce6c0fdbbb03924540c..cc93696978515291da98da74b80986254bec10f0 100644 --- a/modules/net/dap_chain_net_decree.c +++ b/modules/net/dap_chain_net_decree.c @@ -33,6 +33,7 @@ #include "dap_chain_net_tx.h" #include "dap_chain_net_srv_stake_pos_delegate.h" #include "dap_http_ban_list_client.h" +#include "dap_chain_policy.h" @@ -632,6 +633,22 @@ static int s_common_decree_handler(dap_chain_datum_decree_t *a_decree, dap_chain break; dap_chain_esbocs_set_emergency_validator(l_chain, l_action, l_sign_type, &l_hash); } break; + case DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_POLICY: { + if (!a_apply) + break; + dap_chain_policy_t *l_policy = NULL; + if ( !(l_policy = dap_chain_datum_decree_get_policy(a_decree)) ){ + log_it(L_WARNING,"Can't get policy from decree."); + return -105; + } + l_policy = DAP_DUP_SIZE_RET_VAL_IF_FAIL(l_policy, dap_chain_policy_get_size(l_policy), -106); + if (l_policy->type == DAP_CHAIN_POLICY_ACTIVATE) { + dap_chain_policy_activate_t *l_policy_activate = (dap_chain_policy_activate_t *)l_policy->data; + if(DAP_FLAG_CHECK(l_policy->flags, DAP_CHAIN_POLICY_FLAG_ACTIVATE_BY_BLOCK_NUM)) + l_policy_activate->chain_union.chain = dap_chain_find_by_id(a_net->pub.id, l_policy_activate->chain_union.chain_id); + } + return dap_chain_policy_add(l_policy, a_net->pub.id.uint64); + } default: return -1; } diff --git a/modules/net/dap_chain_net_tx.c b/modules/net/dap_chain_net_tx.c index addcb27c56eff00a0018ff62fc0785ebbc1eba8a..937c0464f50f9b54297dfce0366f9d6e62749d47 100644 --- a/modules/net/dap_chain_net_tx.c +++ b/modules/net/dap_chain_net_tx.c @@ -1633,7 +1633,7 @@ int dap_chain_net_tx_to_json(dap_chain_datum_tx_t *a_tx, json_object *a_out_json json_object_object_add(json_obj_item,"type", json_object_new_string("voting")); json_object_object_add(json_obj_item,"voting_question", json_object_new_string(l_voting_params->voting_question)); json_object_object_add(json_obj_item,"answer_options", json_object_new_string("")); - + json_object_object_add(json_obj_item, "token", json_object_new_string(l_voting_params->token_ticker)); dap_list_t *l_temp = l_voting_params->answers_list; uint8_t l_index = 0; while (l_temp) { @@ -1643,7 +1643,7 @@ int dap_chain_net_tx_to_json(dap_chain_datum_tx_t *a_tx, json_object *a_out_json } if (l_voting_params->voting_expire) { dap_time_to_str_rfc822(l_tmp_buf, DAP_TIME_STR_SIZE, l_voting_params->voting_expire); - json_object_object_add(json_obj_item,"Voting expire", json_object_new_string(l_tmp_buf)); + json_object_object_add(json_obj_item, "Voting expire", json_object_new_string(l_tmp_buf)); } if (l_voting_params->votes_max_count) { json_object_object_add(json_obj_item, "Votes max count", json_object_new_uint64(l_voting_params->votes_max_count)); diff --git a/modules/net/dap_chain_node_cli.c b/modules/net/dap_chain_node_cli.c index 02064b27334c990e575b24a2b3be6277aedd9204..5e9a92d98a602600e6c861d440c6f8355480f6d6 100644 --- a/modules/net/dap_chain_node_cli.c +++ b/modules/net/dap_chain_node_cli.c @@ -164,8 +164,9 @@ int dap_chain_node_cli_init(dap_config_t * g_config) "wallet info {-addr <addr> | -w <wallet_name>} -net <net_name>\n" "wallet activate -w <wallet_name> -password <password> [-ttl <password_ttl_in_minutes>]\n" "wallet deactivate -w <wallet_name>>\n" - "wallet outputs {-addr <addr> | -w <wallet_name>} -net <net_name> -token <token_tiker> [-value <uint256_value>]\n" - "wallet convert -w <wallet_name> {-password <password> | -remove_password }\n"); + "wallet outputs {-addr <addr> | -w <wallet_name>} -net <net_name> -token <token_tiker> [{-cond | -value <uint256_value>}]\n" + "wallet convert -w <wallet_name> {-password <password> | -remove_password }\n" + "wallet find -addr <addr> {-file <file path>}\n"); // Token commands @@ -235,7 +236,7 @@ int dap_chain_node_cli_init(dap_config_t * g_config) "token_emit { sign -emission <hash> | -token <mempool_token_ticker> -emission_value <value> -addr <addr> } " "[-chain_emission <chain_name>] -net <net_name> -certs <cert_list>\n"); - dap_cli_server_cmd_add("mempool", com_mempool, "Command for working with mempool", + dap_cli_cmd_t *l_cmd_mempool = dap_cli_server_cmd_add("mempool", com_mempool, "Command for working with mempool", "mempool list -net <net_name> [-chain <chain_name>] [-addr <addr>] [-brief] [-limit] [-offset]\n" "\tList mempool (entries or transaction) for (selected chain network or wallet)\n" "mempool check -net <net_name> [-chain <chain_name>] -datum <datum_hash>\n" @@ -253,15 +254,13 @@ int dap_chain_node_cli_init(dap_config_t * g_config) "\tAdd pubic certificate into the mempool to prepare its way to chains\n" "mempool count -net <net_name> [-chain <chain_name>]\n" "\tDisplays the number of elements in the mempool of a given network."); - dap_cli_cmd_t *l_cmd_mempool = dap_cli_server_cmd_find("mempool"); - dap_cli_server_alias_add("mempool_list", "list", l_cmd_mempool); - dap_cli_server_alias_add("mempool_check", "check", l_cmd_mempool); - dap_cli_server_alias_add("mempool_proc", "proc", l_cmd_mempool); - dap_cli_server_alias_add("mempool_proc_all", "proc_all", l_cmd_mempool); - dap_cli_server_alias_add("mempool_delete", "delete", l_cmd_mempool); - dap_cli_server_alias_add("mempool_add_ca", "add_ca", l_cmd_mempool); - dap_cli_server_alias_add("chain_ca_copy", "add_ca", l_cmd_mempool); - + dap_cli_server_alias_add(l_cmd_mempool, "list", "mempool_list"); + dap_cli_server_alias_add(l_cmd_mempool, "check", "mempool_check"); + dap_cli_server_alias_add(l_cmd_mempool, "proc", "mempool_proc"); + dap_cli_server_alias_add(l_cmd_mempool, "proc_all", "mempool_proc_all"); + dap_cli_server_alias_add(l_cmd_mempool, "delete", "mempool_delete"); + dap_cli_server_alias_add(l_cmd_mempool, "add_ca", "mempool_add_ca"); + dap_cli_server_alias_add(l_cmd_mempool, "add_ca", "chain_ca_copy"); dap_cli_server_cmd_add ("chain_ca_pub", com_chain_ca_pub, "Add pubic certificate into the mempool to prepare its way to chains", @@ -358,6 +357,26 @@ int dap_chain_node_cli_init(dap_config_t * g_config) "\tTypes decree: fee, owners, owners_min, stake_approve, stake_invalidate, min_value, " "min_validators_count, ban, unban, reward, validator_max_weight, emergency_validators, check_signs_structure\n"); + + dap_cli_server_cmd_add ("policy", com_policy, "Policy commands", + "policy activate - prepare policy activate decree\n" + "\t[execute] - used to create policy decree, otherwise show policy decree draft\n" + "\t-net <net_name>\n" + "\t-num <policy_num>\n" + "\t[-ts_start <dd/mm/yy-H:M:S>] - date to start policy\n" + "\t[{\n\t\t-block_start <block_num> - block num to start policy\n" + "\t\t-chain <chain_name> - chain name to check blocks num\n\t}]\n" + "\t-certs <cert1[,cert2,...,certN]> - list signing certs\n" + "policy deactivate - prepare policy deactivate decree\n" + "\t[execute] - used to create policy decree, otherwise show policy decree draft\n" + "\t-net <net_name>\n" + "\t-num <num1[,num2,...,numN]> - deactivated policy list\n" + "\t-certs <cert1[,cert2,...,certN]> - list signing certs\n" + "policy find - find info about policy in net\n" + "\t-net <net_name>\n" + "\t-num <policy_num>\n" + "policy list - show all policies from table in net\n" + "\t-net <net_name>\n"); // Exit - always last! dap_cli_server_cmd_add ("exit", com_exit, "Stop application and exit", "exit\n" ); diff --git a/modules/net/dap_chain_node_cli_cmd.c b/modules/net/dap_chain_node_cli_cmd.c index 5faab630d1c898bc275d9a905ee4ab73f882a7cf..4413358c8a27fee8f029c46117283fa8476563da 100644 --- a/modules/net/dap_chain_node_cli_cmd.c +++ b/modules/net/dap_chain_node_cli_cmd.c @@ -106,6 +106,11 @@ #include "dap_client_pvt.h" #include "dap_enc.h" #include "dap_notify_srv.h" +#include "dap_chain_wallet_cache.h" +#include "dap_chain_net_srv_stake_pos_delegate.h" +#include "dap_chain_policy.h" +#include "dap_time.h" + #include "dap_chain_net_tx.h" @@ -1547,9 +1552,11 @@ int com_help(int a_argc, char **a_argv, void **a_str_reply) } -void s_wallet_list(const char *a_wallet_path, json_object *a_json_arr_out){ +void s_wallet_list(const char *a_wallet_path, json_object *a_json_arr_out, dap_chain_addr_t *a_addr){ if (!a_wallet_path || !a_json_arr_out) return; + const char *l_addr_str = NULL; + dap_chain_addr_t * l_addr = NULL; DIR * l_dir = opendir(a_wallet_path); if(l_dir) { struct dirent * l_dir_entry = NULL; @@ -1560,7 +1567,7 @@ void s_wallet_list(const char *a_wallet_path, json_object *a_json_arr_out){ size_t l_file_name_len = (l_file_name) ? strlen(l_file_name) : 0; unsigned int res = 0; json_object * json_obj_wall = json_object_new_object(); - if (!json_obj_wall) + if (!json_obj_wall) return; if ( (l_file_name_len > 8) && (!strcmp(l_file_name + l_file_name_len - 8, ".dwallet")) ) { char l_file_path_tmp[MAX_PATH] = {0}; @@ -1568,6 +1575,28 @@ void s_wallet_list(const char *a_wallet_path, json_object *a_json_arr_out){ dap_chain_wallet_t *l_wallet = dap_chain_wallet_open(l_file_name, a_wallet_path, &res); if (l_wallet) { + if (a_addr) { + l_addr = dap_chain_wallet_get_addr(l_wallet, a_addr->net_id); + if (l_addr && dap_chain_addr_compare(l_addr, a_addr)) { + json_object_object_add(json_obj_wall, "wallet", json_object_new_string(l_file_name)); + if(l_wallet->flags & DAP_WALLET$M_FL_ACTIVE) + json_object_object_add(json_obj_wall, "status", json_object_new_string("protected-active")); + else + json_object_object_add(json_obj_wall, "status", json_object_new_string("unprotected")); + json_object_object_add(json_obj_wall, "deprecated", json_object_new_string( + strlen(dap_chain_wallet_check_sign(l_wallet))!=0 ? "true" : "false")); + } + else { + json_object_put(json_obj_wall); + dap_chain_wallet_close(l_wallet); + DAP_DEL_Z(l_addr); + continue; + } + DAP_DEL_Z(l_addr); + dap_chain_wallet_close(l_wallet); + json_object_array_add(a_json_arr_out, json_obj_wall); + break; + } //l_addr = l_net ? dap_chain_wallet_get_addr(l_wallet, l_net->pub.id) : NULL; // const char *l_addr_str = dap_chain_addr_to_str_static(l_addr); json_object_object_add(json_obj_wall, "Wallet", json_object_new_string(l_file_name)); @@ -1581,16 +1610,28 @@ void s_wallet_list(const char *a_wallet_path, json_object *a_json_arr_out){ // json_object_object_add(json_obj_wall, "addr", json_object_new_string(l_addr_str)); // } dap_chain_wallet_close(l_wallet); - } else{ + } else if (!a_addr){ json_object_object_add(json_obj_wall, "Wallet", json_object_new_string(l_file_name)); if(res==4)json_object_object_add(json_obj_wall, "status", json_object_new_string("protected-inactive")); else if(res != 0)json_object_object_add(json_obj_wall, "status", json_object_new_string("invalid")); } + } else if (a_addr) { + json_object_put(json_obj_wall); + continue; } else if ((l_file_name_len > 7) && (!strcmp(l_file_name + l_file_name_len - 7, ".backup"))) { json_object_object_add(json_obj_wall, "Wallet", json_object_new_string(l_file_name)); json_object_object_add(json_obj_wall, "status", json_object_new_string("Backup")); } - json_object_array_add(a_json_arr_out, json_obj_wall); + if (json_object_object_length(json_obj_wall)) + json_object_array_add(a_json_arr_out, json_obj_wall); + else + json_object_put(json_obj_wall); + } + if (a_addr && (json_object_array_length(a_json_arr_out) == 0)) { + json_object * json_obj_out = json_object_new_object(); + if (!json_obj_out) return; + json_object_object_add(json_obj_out, "status", json_object_new_string("not found")); + json_object_array_add(a_json_arr_out, json_obj_out); } closedir(l_dir); } @@ -1610,7 +1651,8 @@ int com_tx_wallet(int a_argc, char **a_argv, void **a_str_reply) { json_object ** a_json_arr_reply = (json_object **) a_str_reply; const char *c_wallets_path = dap_chain_wallet_get_path(g_config); -enum { CMD_NONE, CMD_WALLET_NEW, CMD_WALLET_LIST, CMD_WALLET_INFO, CMD_WALLET_ACTIVATE, CMD_WALLET_DEACTIVATE, CMD_WALLET_CONVERT, CMD_WALLET_OUTPUTS }; +enum { CMD_NONE, CMD_WALLET_NEW, CMD_WALLET_LIST, CMD_WALLET_INFO, CMD_WALLET_ACTIVATE, + CMD_WALLET_DEACTIVATE, CMD_WALLET_CONVERT, CMD_WALLET_OUTPUTS, CMD_WALLET_FIND}; int l_arg_index = 1, l_rc, cmd_num = CMD_NONE; // find add parameter ('alias' or 'handshake') @@ -1628,6 +1670,8 @@ int l_arg_index = 1, l_rc, cmd_num = CMD_NONE; cmd_num = CMD_WALLET_CONVERT; else if(dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, dap_min(a_argc, l_arg_index + 1), "outputs", NULL)) cmd_num = CMD_WALLET_OUTPUTS; + else if(dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, dap_min(a_argc, l_arg_index + 1), "find", NULL)) + cmd_num = CMD_WALLET_FIND; l_arg_index++; @@ -1638,7 +1682,7 @@ int l_arg_index = 1, l_rc, cmd_num = CMD_NONE; } const char *l_addr_str = NULL, *l_wallet_name = NULL, *l_net_name = NULL, *l_sign_type_str = NULL, *l_restore_str = NULL, - *l_pass_str = NULL, *l_ttl_str = NULL; + *l_pass_str = NULL, *l_ttl_str = NULL, *l_file_path = NULL; // find wallet addr dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-addr", &l_addr_str); @@ -1646,6 +1690,7 @@ int l_arg_index = 1, l_rc, cmd_num = CMD_NONE; dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-net", &l_net_name); dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-password", &l_pass_str); dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-sign", &l_sign_type_str); + dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-file", &l_file_path); // Check if wallet name has only digits and English letter if (l_wallet_name && !dap_isstralnum(l_wallet_name)){ @@ -1671,7 +1716,7 @@ int l_arg_index = 1, l_rc, cmd_num = CMD_NONE; switch (cmd_num) { // wallet list case CMD_WALLET_LIST: - s_wallet_list(c_wallets_path, json_arr_out); + s_wallet_list(c_wallets_path, json_arr_out, NULL); break; // wallet info case CMD_WALLET_INFO: { @@ -1841,48 +1886,78 @@ int l_arg_index = 1, l_rc, cmd_num = CMD_NONE; return DAP_CHAIN_NODE_CLI_COM_TX_WALLET_PARAM_ERR; } json_object * json_obj_wall = json_object_new_object(); - const char* l_value_str = NULL; - dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-value", &l_value_str); + const char *l_value_str = NULL; + uint256_t l_value_datoshi = uint256_0, l_value_sum = uint256_0; + bool l_cond_outs = dap_cli_server_cmd_check_option(a_argv, l_arg_index, a_argc, "-cond") != -1; + if (!l_cond_outs) { + dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-value", &l_value_str); + if (l_value_str) { + l_value_datoshi = dap_chain_balance_scan(l_value_str); + if (IS_ZERO_256(l_value_datoshi)) { + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_TX_WALLET_PARAM_ERR, + "Can't convert -value param to 256bit integer"); + json_object_put(json_arr_out); + return DAP_CHAIN_NODE_CLI_COM_TX_WALLET_PARAM_ERR; + } + } + } dap_list_t *l_outs_list = NULL; - uint256_t l_value_sum = uint256_0; - - - - if (l_value_str){ - uint256_t l_value_datoshi = dap_chain_balance_scan(l_value_str); + if (l_cond_outs) + l_outs_list = dap_ledger_get_list_tx_cond_outs(l_net->pub.ledger, DAP_CHAIN_TX_OUT_COND_SUBTYPE_ALL, l_token_tiker, l_addr); + else if (l_value_str) { if (dap_chain_wallet_cache_tx_find_outs_with_val(l_net, l_token_tiker, l_addr, &l_outs_list, l_value_datoshi, &l_value_sum)) - l_outs_list = dap_ledger_get_list_tx_outs_with_val(l_net->pub.ledger, l_token_tiker, l_addr, l_value_datoshi, &l_value_sum); + l_outs_list = dap_ledger_get_list_tx_outs_with_val(l_net->pub.ledger, l_token_tiker, l_addr, l_value_datoshi, &l_value_sum); } else { if (dap_chain_wallet_cache_tx_find_outs(l_net, l_token_tiker, l_addr, &l_outs_list, &l_value_sum)) l_outs_list = dap_ledger_get_list_tx_outs(l_net->pub.ledger, l_token_tiker, l_addr, &l_value_sum); } json_object_object_add(json_obj_wall, "wallet_addr", json_object_new_string(dap_chain_addr_to_str_static(l_addr))); - const char *l_out_total_value_str = dap_chain_balance_print(l_value_sum); - const char *l_out_total_value_coins_str = dap_chain_balance_to_coins(l_value_sum); - json_object_object_add(json_obj_wall, "total_value_coins", json_object_new_string(l_out_total_value_coins_str)); - json_object_object_add(json_obj_wall, "total_value_datoshi", json_object_new_string(l_out_total_value_str)); - DAP_DEL_Z(l_out_total_value_str); - DAP_DEL_Z(l_out_total_value_coins_str); struct json_object *l_json_outs_arr = json_object_new_array(); - for (dap_list_t *l_temp = l_outs_list; l_temp; l_temp = l_temp->next){ + if (!l_json_outs_arr) + return json_object_put(json_arr_out), DAP_CHAIN_NODE_CLI_COM_TX_WALLET_MEMORY_ERR; + for (dap_list_t *l_temp = l_outs_list; l_temp; l_temp = l_temp->next) { + json_object *json_obj_item = json_object_new_object(); + if (!json_obj_item) + return json_object_put(json_arr_out), DAP_CHAIN_NODE_CLI_COM_TX_WALLET_MEMORY_ERR; dap_chain_tx_used_out_item_t *l_item = l_temp->data; - json_object* json_obj_item = json_object_new_object(); - const char *l_out_value_str = dap_chain_balance_print(l_item->value); - const char *l_out_value_coins_str = dap_chain_balance_to_coins(l_item->value); - json_object_object_add(json_obj_item,"item_type", json_object_new_string("unspent_out")); + const char *l_out_value_coins_str, *l_out_value_str = dap_uint256_to_char(l_item->value, &l_out_value_coins_str); + json_object_object_add(json_obj_item,"item_type", json_object_new_string(l_cond_outs ? "unspent_cond_out" : "unspent_out")); json_object_object_add(json_obj_item,"value_coins", json_object_new_string(l_out_value_coins_str)); json_object_object_add(json_obj_item,"value_datosi", json_object_new_string(l_out_value_str)); - json_object_object_add(json_obj_item,"prev_hash", json_object_new_string(dap_hash_fast_to_str_static(&l_item->tx_hash_fast))); - json_object_object_add(json_obj_item,"out_prev_idx", json_object_new_int64(l_item->num_idx_out)); + json_object_object_add(json_obj_item,"prev_hash", json_object_new_string(dap_hash_fast_to_str_static(&l_item->tx_hash_fast))); + json_object_object_add(json_obj_item,"out_prev_idx", json_object_new_int64(l_item->num_idx_out)); json_object_array_add(l_json_outs_arr, json_obj_item); - DAP_DEL_Z(l_out_value_str); - DAP_DEL_Z(l_out_value_coins_str); + if (l_cond_outs) + SUM_256_256(l_value_sum, l_item->value, &l_value_sum); } dap_list_free_full(l_outs_list, NULL); + const char * l_out_total_value_coins_str, *l_out_total_value_str = dap_uint256_to_char(l_value_sum, &l_out_total_value_coins_str); + json_object_object_add(json_obj_wall, "total_value_coins", json_object_new_string(l_out_total_value_coins_str)); + json_object_object_add(json_obj_wall, "total_value_datoshi", json_object_new_string(l_out_total_value_str)); json_object_object_add(json_obj_wall, "outs", l_json_outs_arr); json_object_array_add(json_arr_out, json_obj_wall); } break; + case CMD_WALLET_FIND: { + if (l_addr_str) { + l_addr = dap_chain_addr_from_str(l_addr_str); + if (l_addr) { + if (l_file_path) + s_wallet_list(l_file_path, json_arr_out, l_addr); + else + s_wallet_list(c_wallets_path, json_arr_out, l_addr); + } + else { + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_TX_WALLET_ADDR_ERR, + "addr not recognized"); + return DAP_CHAIN_NODE_CLI_COM_TX_WALLET_ADDR_ERR; + } + } else { + dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_TX_WALLET_ADDR_ERR, + "You should use -addr option for the wallet find command."); + return DAP_CHAIN_NODE_CLI_COM_TX_WALLET_ADDR_ERR; + } + } break; default: { if( !l_wallet_name ) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_TX_WALLET_NAME_ERR, @@ -8023,7 +8098,7 @@ struct json_object *wallet_list_json_collect(){ struct json_object *l_json = json_object_new_object(); json_object_object_add(l_json, "class", json_object_new_string("WalletList")); struct json_object *l_j_wallets = json_object_new_array(); - s_wallet_list(dap_chain_wallet_get_path(g_config), l_j_wallets); + s_wallet_list(dap_chain_wallet_get_path(g_config), l_j_wallets, NULL); json_object_object_add(l_json, "wallets", l_j_wallets); return l_json; } @@ -8183,3 +8258,274 @@ int com_exec_cmd(int argc, char **argv, void **reply) { dap_json_rpc_request_free(l_request); return 0; } + +static dap_chain_datum_decree_t *s_decree_policy_execute(dap_chain_net_t *a_net, dap_chain_policy_t *a_policy) +{ + dap_return_val_if_pass(!a_net || !a_policy, NULL); + // create updating decree + size_t l_total_tsd_size = sizeof(dap_tsd_t) + dap_chain_policy_get_size(a_policy); + + dap_chain_t *l_chain = dap_chain_net_get_chain_by_chain_type(a_net, CHAIN_TYPE_DECREE); + if (!l_chain) { + log_it(L_ERROR, "No chain supported decree datum type"); + return NULL; + } + + dap_chain_datum_decree_t *l_decree = dap_chain_datum_decree_new(a_net->pub.id, l_chain->id, *dap_chain_net_get_cur_cell(a_net), l_total_tsd_size); + if (!l_decree) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return NULL; + } + l_decree->header.sub_type = DAP_CHAIN_DATUM_DECREE_COMMON_SUBTYPE_POLICY; + dap_tsd_write((byte_t*)l_decree->data_n_signs, DAP_CHAIN_DATUM_DECREE_TSD_TYPE_POLICY_EXECUTE, a_policy, dap_chain_policy_get_size(a_policy)); + + return l_decree; +} + +// Put the decree to mempool +static char *s_decree_policy_put(dap_chain_datum_decree_t *a_decree, dap_chain_net_t *a_net) +{ + size_t l_decree_size = dap_chain_datum_decree_get_size(a_decree); + dap_chain_datum_t *l_datum = dap_chain_datum_create(DAP_CHAIN_DATUM_DECREE, a_decree, l_decree_size); + dap_chain_t *l_chain = dap_chain_net_get_default_chain_by_chain_type(a_net, CHAIN_TYPE_DECREE); + if (!l_chain) + l_chain = dap_chain_net_get_chain_by_chain_type(a_net, CHAIN_TYPE_DECREE); + if (!l_chain) { + log_it(L_ERROR, "No chain supported decree datum type"); + return NULL; + } + // Processing will be made according to autoprocess policy + char *l_ret = dap_chain_mempool_datum_add(l_datum, l_chain, "hex"); + DAP_DELETE(l_datum); + return l_ret; +} + +int com_policy(int argc, char **argv, void **reply) { + json_object ** a_json_arr_reply = (json_object **) reply; + char **l_deactivate_array = NULL; + const char + *l_num_str = NULL, + *l_net_str = NULL, + *l_deactivate_str = NULL, + *l_chain_str = NULL, + *l_ts_start_str = NULL, + *l_block_start_str = NULL, + *l_certs_str = NULL; + size_t + l_deactivate_count = 0, + l_certs_count = 0; + dap_cert_t **l_certs = NULL; + uint64_t l_flags = 0; + bool l_execute = false; + + enum { CMD_NONE = 0, CMD_ACTIVATE, CMD_DEACTIVATE, CMD_FIND, CMD_LIST }; + int l_arg_index = 1; + + int l_cmd = CMD_NONE; + if (dap_cli_server_cmd_find_option_val(argv, 1, 2, "activate", NULL)) + l_cmd = CMD_ACTIVATE; + else if (dap_cli_server_cmd_find_option_val(argv, 1, 2, "deactivate", NULL)) + l_cmd = CMD_DEACTIVATE; + else if (dap_cli_server_cmd_find_option_val(argv, 1, 2, "find", NULL)) + l_cmd = CMD_FIND; + else if (dap_cli_server_cmd_find_option_val(argv, 1, 2, "list", NULL)) + l_cmd = CMD_LIST; + + if (l_cmd == CMD_NONE) { + dap_json_rpc_error_add(*a_json_arr_reply, -4, "Unknown subcommand"); + return -4; + } + + dap_cli_server_cmd_find_option_val(argv, l_arg_index, argc, "-net", &l_net_str); + + if (!l_net_str) { + dap_json_rpc_error_add(*a_json_arr_reply, -3, "Command policy require args -net"); + return -4; + } + dap_chain_net_t *l_net = dap_chain_net_by_name(l_net_str); + if (!l_net){ + dap_json_rpc_error_add(*a_json_arr_reply, -3, "Can't find net %s", l_net_str); + return -4; + } + + if (l_cmd == CMD_LIST) { + json_object *l_answer = dap_chain_policy_list(l_net->pub.id.uint64); + json_object_array_add(*a_json_arr_reply, l_answer); + return 0; + } + + dap_cli_server_cmd_find_option_val(argv, l_arg_index, argc, "-num", &l_num_str); + if (!l_num_str) { + dap_json_rpc_error_add(*a_json_arr_reply, -7, "Command policy require args -num"); + return -7; + } + + void *l_policy_data = NULL; + size_t l_data_size = 0; + uint64_t l_policy_num = 0; + int l_policy_type = -1; + if (l_cmd == CMD_DEACTIVATE) { + l_policy_type = DAP_CHAIN_POLICY_DEACTIVATE; + l_deactivate_count = dap_str_symbol_count(l_num_str, ',') + 1; + l_deactivate_array = dap_strsplit(l_num_str, ",", l_deactivate_count); + l_data_size = sizeof(dap_chain_policy_deactivate_t) + l_deactivate_count * sizeof(uint32_t); + l_policy_data = DAP_NEW_Z_SIZE(void, l_data_size); + if (!l_policy_data) { + dap_json_rpc_error_add(*a_json_arr_reply, -16, "%s", c_error_memory_alloc); + dap_strfreev(l_deactivate_array); + return -16; + } + ((dap_chain_policy_deactivate_t *)l_policy_data)->count = l_deactivate_count; + for (size_t i = 0; i < l_deactivate_count; ++i) { + l_policy_num = strtoull(l_deactivate_array[i], NULL, 10); + if (!dap_chain_policy_num_is_valid(l_policy_num)) { + dap_json_rpc_error_add(*a_json_arr_reply, -16, "Policy nums sould be less or equal than %u and not equal 0", dap_maxval((uint32_t)l_policy_num)); + dap_strfreev(l_deactivate_array); + DAP_DELETE(l_policy_data); + return -16; + } + ((dap_chain_policy_deactivate_t *)l_policy_data)->nums[i] = l_policy_num; + } + dap_strfreev(l_deactivate_array); + } else { + l_policy_num = strtoull(l_num_str, NULL, 10); + if (!dap_chain_policy_num_is_valid(l_policy_num)) { + dap_json_rpc_error_add(*a_json_arr_reply, -16, "Policy num sould be less or equal than %u and not equal 0", dap_maxval((uint32_t)l_policy_num)); + return -16; + } + } + + uint32_t l_last_num = dap_chain_policy_get_last_num(l_net->pub.id.uint64); + + if (l_cmd == CMD_FIND) { + dap_chain_policy_t *l_policy = dap_chain_policy_find(l_policy_num, l_net->pub.id.uint64); + json_object *l_answer = dap_chain_policy_json_collect(l_policy); + if (l_answer) { + json_object_object_add(l_answer, "active", json_object_new_string(dap_chain_policy_activated(((dap_chain_policy_activate_t *)(l_policy->data))->num, l_net->pub.id.uint64) ? "true" : "false")); + json_object_array_add(*a_json_arr_reply, l_answer); + } else { + json_object_array_add(*a_json_arr_reply, json_object_new_string("Detailed information not exist")); + } + return 0; + } + + dap_cli_server_cmd_find_option_val(argv, l_arg_index, argc, "-chain", &l_chain_str); + dap_cli_server_cmd_find_option_val(argv, l_arg_index, argc, "-ts_start", &l_ts_start_str); + dap_cli_server_cmd_find_option_val(argv, l_arg_index, argc, "-block_start", &l_block_start_str); + dap_cli_server_cmd_find_option_val(argv, l_arg_index, argc, "-deactivate", &l_deactivate_str); + dap_cli_server_cmd_find_option_val(argv, l_arg_index, argc, "-certs", &l_certs_str); + l_execute = dap_cli_server_cmd_find_option_val(argv, l_arg_index, argc, "execute", NULL); + + if (l_execute) { + if (!l_certs_str) { + dap_json_rpc_error_add(*a_json_arr_reply, -4, "Command 'execute' requires parameter -certs"); + DAP_DELETE(l_policy_data); + return -4; + } + dap_cert_parse_str_list(l_certs_str, &l_certs, &l_certs_count); + if (!l_certs || !l_certs_count) { + dap_json_rpc_error_add(*a_json_arr_reply, -5, "Specified certificates not found"); + DAP_DELETE(l_policy_data); + return -5; + } + } + + if (l_cmd == CMD_ACTIVATE) { + if (l_policy_num == l_last_num) { + dap_json_rpc_error_add(*a_json_arr_reply, -15, "Specified policy num already existed"); + return -15; + } + l_policy_type = DAP_CHAIN_POLICY_ACTIVATE; + l_data_size = sizeof(dap_chain_policy_activate_t); + l_policy_data = DAP_NEW_Z_SIZE_RET_VAL_IF_FAIL(void, l_data_size, -5); + dap_chain_policy_activate_t *l_policy_activate = (dap_chain_policy_activate_t *)l_policy_data; + + l_policy_activate->num = l_policy_num; + if (l_ts_start_str) { + l_policy_activate->ts_start = dap_time_from_str_custom(l_ts_start_str, "%d/%m/%y-%H:%M:%S"); + if (!l_policy_activate->ts_start) { + dap_json_rpc_error_add(*a_json_arr_reply, -13, "Can't read ts_start \"%s\"", l_ts_start_str); + DAP_DELETE(l_policy_activate); + return -13; + } + l_flags = DAP_FLAG_ADD(l_flags, DAP_CHAIN_POLICY_FLAG_ACTIVATE_BY_TS); + } + + if (l_block_start_str) + l_policy_activate->block_start = strtoull(l_block_start_str, NULL, 10); + + if (l_policy_activate->block_start) { + if (!l_chain_str) { + dap_json_rpc_error_add(*a_json_arr_reply, -8, "Command policy create with -block_start require args -chain"); + DAP_DELETE(l_policy_activate); + return -8; + } + dap_chain_t *l_chain = dap_chain_net_get_chain_by_name(l_net, l_chain_str); + if (!l_chain) { + dap_json_rpc_error_add(*a_json_arr_reply, -9, "%s Chain not found", l_chain_str); + DAP_DELETE(l_policy_activate); + return -9; + } + l_policy_activate->chain_union.chain = l_chain; + l_flags = DAP_FLAG_ADD(l_flags, DAP_CHAIN_POLICY_FLAG_ACTIVATE_BY_BLOCK_NUM); + } + if (!l_flags && l_policy_activate->num < l_last_num) { + dap_json_rpc_error_add(*a_json_arr_reply, -16, "Specified policy already activated by CN-%u", l_last_num); + DAP_DELETE(l_policy_activate); + return -16; + } + } + + dap_chain_policy_t *l_policy = DAP_NEW_Z_SIZE_RET_VAL_IF_FAIL(dap_chain_policy_t, l_data_size + sizeof(dap_chain_policy_t), -5, l_policy_data); + l_policy->data_size = l_data_size; + l_policy->version = DAP_CHAIN_POLICY_VERSION; + l_policy->type = l_policy_type; + l_policy->flags = l_flags; + memcpy(l_policy->data, l_policy_data, l_policy->data_size); + DAP_DELETE(l_policy_data); + // if cmd none - only print preaparing result + if (!l_execute) { + json_object *l_answer = dap_chain_policy_json_collect(l_policy); + char l_time[DAP_TIME_STR_SIZE] = {}; + dap_time_to_str_rfc822(l_time, DAP_TIME_STR_SIZE - 1, dap_time_now()); + json_object_object_add(l_answer, "Current time", json_object_new_string(l_time)); + json_object_object_add(l_answer, "Notification", json_object_new_string("It's policy draft, check and use 'execute' command to apply")); + if (l_answer) { + json_object_array_add(*a_json_arr_reply, l_answer); + } else { + dap_json_rpc_error_add(*a_json_arr_reply, -11, "Policy draft creation failed"); + DAP_DELETE(l_policy); + return -11; + } + DAP_DELETE(l_policy); + return 0; + } + // change pointer to id to decree + if (l_policy->type == DAP_CHAIN_POLICY_ACTIVATE && ((dap_chain_policy_activate_t *)(l_policy->data))->chain_union.chain) { + ((dap_chain_policy_activate_t *)(l_policy->data))->chain_union.chain_id = ((dap_chain_policy_activate_t *)(l_policy->data))->chain_union.chain->id; + } + + dap_chain_datum_decree_t *l_decree = s_decree_policy_execute(l_net, l_policy); + DAP_DELETE(l_policy); + size_t l_total_signs_success = 0; + l_decree = dap_chain_datum_decree_sign_in_cycle(l_certs, l_decree, l_certs_count, &l_total_signs_success); + + if (!l_decree || l_total_signs_success == 0){ + dap_json_rpc_error_add(*a_json_arr_reply, -11, "Decree creation failed. Successful count of certificate signing is 0"); + return -11; + } + + char *l_decree_hash_str = NULL;; + if (!(l_decree_hash_str = s_decree_policy_put(l_decree, l_net))) { + dap_json_rpc_error_add(*a_json_arr_reply, -12, "Policy decree error"); + return -12; + } + DAP_DELETE(l_decree); + + char l_approve_str[128]; + snprintf(l_approve_str, sizeof(l_approve_str), "Policy decree %s successfully created", l_decree_hash_str); + json_object_array_add(*a_json_arr_reply, json_object_new_string(l_approve_str)); + DAP_DELETE(l_decree_hash_str); + + return 0; +} diff --git a/modules/net/dap_chain_node_cli_cmd_tx.c b/modules/net/dap_chain_node_cli_cmd_tx.c index 0748ba83ce64ac7bdffe97f91b6f8549df941509..48e89da82a7ce68e0aae8d8569d662478b1e76ef 100644 --- a/modules/net/dap_chain_node_cli_cmd_tx.c +++ b/modules/net/dap_chain_node_cli_cmd_tx.c @@ -429,7 +429,7 @@ json_object* dap_db_history_addr(json_object* a_json_arr_reply, dap_chain_addr_t int l_src_subtype = DAP_CHAIN_TX_OUT_COND_SUBTYPE_UNDEFINED; uint8_t *l_tx_item = NULL; size_t l_size; int i, q = 0; - // Проход по входам + // Inputs iteration TX_ITEM_ITER_TX_TYPE(l_tx_item, TX_ITEM_TYPE_IN_ALL, l_size, i, l_tx) { dap_chain_hash_fast_t *l_tx_prev_hash = NULL; int l_tx_prev_out_idx; @@ -552,7 +552,7 @@ json_object* dap_db_history_addr(json_object* a_json_arr_reply, dap_chain_addr_t case TX_ITEM_TYPE_OUT_COND: l_value = ((dap_chain_tx_out_cond_t *)it->data)->header.value; if (((dap_chain_tx_out_cond_t *)it->data)->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE) { - SUM_256_256(l_fee_sum, ((dap_chain_tx_out_cond_t *)it->data)->header.value, &l_fee_sum); + SUM_256_256(l_fee_sum, l_value, &l_fee_sum); l_dst_token = l_native_ticker; } else l_dst_token = l_src_token; @@ -562,7 +562,7 @@ json_object* dap_db_history_addr(json_object* a_json_arr_reply, dap_chain_addr_t if (l_src_addr && l_dst_addr && dap_chain_addr_compare(l_dst_addr, l_src_addr) && - dap_strcmp(l_noaddr_token, l_dst_token)) + l_noaddr_token && dap_strcmp(l_noaddr_token, l_dst_token)) continue; // sent to self (coinback) if (l_dst_addr && l_net_fee_used && dap_chain_addr_compare(&l_net_fee_addr, l_dst_addr)) @@ -698,13 +698,10 @@ json_object* dap_db_history_addr(json_object* a_json_arr_reply, dap_chain_addr_t json_object_object_add(l_corr_object, "recv_datoshi", json_object_new_string(l_value_str)); } if (l_send_to_same_cond) { - json_object *l_cond_recv_value_obj = json_object_object_get(l_cond_recv_object, "recv_datoshi"); - const char *l_cond_recv_value_str = json_object_get_string(l_cond_recv_value_obj); - uint256_t l_cond_recv_value = dap_uint256_scan_uninteger(l_cond_recv_value_str); + uint256_t l_cond_recv_value = l_cond_value; json_object *l_cond_send_value_obj = json_object_object_get(l_cond_send_object, "send_datoshi"); const char *l_cond_send_value_str = json_object_get_string(l_cond_send_value_obj); uint256_t l_cond_send_value = dap_uint256_scan_uninteger(l_cond_send_value_str); - assert(!IS_ZERO_256(l_cond_recv_value) && !IS_ZERO_256(l_cond_send_value)); int l_direction = compare256(l_cond_recv_value, l_cond_send_value); if (l_direction > 0) { SUBTRACT_256_256(l_cond_recv_value, l_cond_send_value, &l_cond_recv_value); diff --git a/modules/net/include/dap_chain_ledger.h b/modules/net/include/dap_chain_ledger.h index f2e43d962d7f7617a4a8457eada2479fa56218a2..a3608c605911e76d277223925f4be24c9d46eb6b 100644 --- a/modules/net/include/dap_chain_ledger.h +++ b/modules/net/include/dap_chain_ledger.h @@ -127,6 +127,10 @@ typedef enum dap_chain_tx_tag_action_type { DAP_CHAIN_TX_TAG_ACTION_VOTING = 1 << 11, DAP_CHAIN_TX_TAG_ACTION_VOTE = 1 << 12, + + DAP_CHAIN_TX_TAG_ACTION_EMIT_DELEGATE_HOLD = 1 << 13, + DAP_CHAIN_TX_TAG_ACTION_EMIT_DELEGATE_TAKE = 1 << 14, + DAP_CHAIN_TX_TAG_ACTION_EMIT_DELEGATE_REFILL = 1 << 15, DAP_CHAIN_TX_TAG_ACTION_ALL = ~0, } dap_chain_tx_tag_action_type_t; @@ -418,21 +422,11 @@ dap_chain_datum_tx_t* dap_ledger_tx_find_by_addr(dap_ledger_t *a_ledger, const c bool dap_ledger_tx_check_recipient(dap_ledger_t* a_ledger, dap_chain_hash_fast_t* a_tx_prev_hash, dap_chain_addr_t *a_addr); -// Get the transaction in the cache by the public key that signed the transaction, starting with a_tx_first_hash -const dap_chain_datum_tx_t* dap_ledger_tx_find_by_pkey(dap_ledger_t *a_ledger, - char *a_public_key, size_t a_public_key_size, dap_chain_hash_fast_t *a_tx_first_hash); - -// Get the transaction in the cache with the out_cond item -dap_chain_datum_tx_t* dap_ledger_tx_cache_find_out_cond(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_cond_type, - dap_chain_hash_fast_t *a_tx_first_hash, dap_chain_tx_out_cond_t **a_out_cond, - int *a_out_cond_idx, char *a_token_ticker); - // Get all transactions from the cache with the specified out_cond items dap_list_t* dap_ledger_tx_cache_find_out_cond_all(dap_ledger_t *a_ledger, dap_chain_net_srv_uid_t a_srv_uid); -// Get the value from all transactions in the cache with out_cond item -uint256_t dap_ledger_tx_cache_get_out_cond_value(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_cond_type, dap_chain_addr_t *a_addr, - dap_chain_tx_out_cond_t **tx_out_cond); +dap_chain_tx_out_cond_t *dap_ledger_out_cond_unspent_find_by_addr(dap_ledger_t *a_ledger, const char *a_token, dap_chain_tx_out_cond_subtype_t a_subtype, + const dap_chain_addr_t *a_addr, dap_chain_hash_fast_t *a_tx_first_hash, int *a_out_idx); // Get the list of 'out' items from previous transactions with summary value >= than a_value_need // Put this summary value to a_value_transfer @@ -440,12 +434,8 @@ dap_list_t *dap_ledger_get_list_tx_outs_with_val(dap_ledger_t *a_ledger, const c uint256_t a_value_need, uint256_t *a_value_transfer); dap_list_t *dap_ledger_get_list_tx_outs(dap_ledger_t *a_ledger, const char *a_token_ticker, const dap_chain_addr_t *a_addr_from, uint256_t *a_value_transfer); -// Get the list of 'out_cond' items with summary value >= than a_value_need -dap_list_t *dap_ledger_get_list_tx_cond_outs_with_val(dap_ledger_t *a_ledger, const char *a_token_ticker, const dap_chain_addr_t *a_addr_from, - dap_chain_tx_out_cond_subtype_t a_subtype, uint256_t a_value_need, uint256_t *a_value_transfer); - -dap_list_t *dap_ledger_get_list_tx_cond_outs(dap_ledger_t *a_ledger, const char *a_token_ticker, const dap_chain_addr_t *a_addr_from, - dap_chain_tx_out_cond_subtype_t a_subtype, uint256_t *a_value_transfer); +dap_list_t *dap_ledger_get_list_tx_cond_outs(dap_ledger_t *a_ledger, dap_chain_tx_out_cond_subtype_t a_subtype, const char *a_token_ticker, const dap_chain_addr_t *a_addr_from); +bool dap_ledger_check_condition_owner(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_hash, dap_chain_tx_out_cond_subtype_t a_cond_subtype, int a_out_idx, dap_sign_t *a_owner_sign); // Add new verificator callback with associated subtype. Returns 1 if callback replaced, overwise returns 0 int dap_ledger_verificator_add(dap_chain_tx_out_cond_subtype_t a_subtype, dap_ledger_verificator_callback_t a_callback, dap_ledger_updater_callback_t a_callback_added, dap_ledger_delete_callback_t a_callback_deleted); @@ -471,7 +461,6 @@ bool dap_ledger_cache_enabled(dap_ledger_t *a_ledger); void dap_ledger_set_cache_tx_check_callback(dap_ledger_t *a_ledger, dap_ledger_cache_tx_check_callback_t a_callback); dap_chain_tx_out_cond_t* dap_chain_ledger_get_tx_out_cond_linked_to_tx_in_cond(dap_ledger_t *a_ledger, dap_chain_tx_in_cond_t *a_in_cond); void dap_ledger_load_end(dap_ledger_t *a_ledger); -dap_pkey_t *dap_ledger_find_pkey_by_hash(dap_ledger_t *a_ledger, dap_chain_hash_fast_t *a_pkey_hash); #ifdef __cplusplus } diff --git a/modules/net/include/dap_chain_node_cli_cmd.h b/modules/net/include/dap_chain_node_cli_cmd.h index 7f8a39ea7d0adca1c4474306422d2688aa833d2c..fba416a4c88fb6da4595c5c4e74d2befc43e43f6 100644 --- a/modules/net/include/dap_chain_node_cli_cmd.h +++ b/modules/net/include/dap_chain_node_cli_cmd.h @@ -474,6 +474,11 @@ void dap_notify_new_client_send_info(dap_events_socket_t *a_es, void *a_arg); int com_exec_cmd(int argc, char **argv, void **reply); +/** + * Policy management + */ +int com_policy(int a_argc, char **a_argv, void **a_str_reply); + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/modules/service/emit-delegate/dap_chain_net_srv_emit_delegate.c b/modules/service/emit-delegate/dap_chain_net_srv_emit_delegate.c index 3df74f13687bcbaa4f17a068c3d1809ae68154db..4a16024466a78f580d011f0bd0402d8d3556ccdb 100644 --- a/modules/service/emit-delegate/dap_chain_net_srv_emit_delegate.c +++ b/modules/service/emit-delegate/dap_chain_net_srv_emit_delegate.c @@ -47,7 +47,8 @@ enum emit_delegation_error { ERROR_COMPOSE, ERROR_CREATE, ERROR_PLACE, - ERROR_SUBCOMMAND + ERROR_SUBCOMMAND, + ERROR_NETWORK }; #define LOG_TAG "dap_chain_net_srv_emit_delegate" @@ -60,6 +61,7 @@ static int s_emit_delegate_verificator(dap_ledger_t *a_ledger, dap_chain_tx_out_ uint256_t l_writeoff_value = uint256_0; dap_chain_tx_out_cond_t *l_cond_out = NULL; dap_chain_addr_t l_net_fee_addr; + uint16_t l_change_type = 0; bool l_net_fee_used = dap_chain_net_tx_get_fee(a_ledger->net->pub.id, NULL, &l_net_fee_addr); byte_t *l_item; size_t l_tx_item_size; TX_ITEM_ITER_TX(l_item, l_tx_item_size, a_tx_in) { @@ -76,7 +78,7 @@ static int s_emit_delegate_verificator(dap_ledger_t *a_ledger, dap_chain_tx_out_ break; case TX_ITEM_TYPE_TSD: { dap_tsd_t *l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t *)l_item)->tsd; - if (l_tsd->type != DAP_CHAIN_NET_SRV_EMIT_DELEGATE_TSD_WRITEOFF) + if (l_tsd->type != DAP_CHAIN_NET_SRV_EMIT_DELEGATE_TSD_WRITEOFF && l_tsd->type != DAP_CHAIN_NET_SRV_EMIT_DELEGATE_TSD_REFILL) break; // Skip it if (l_tsd->size != sizeof(uint256_t)) { log_it(L_ERROR, "TSD section size control error"); @@ -87,6 +89,7 @@ static int s_emit_delegate_verificator(dap_ledger_t *a_ledger, dap_chain_tx_out_ return -5; } l_writeoff_value = dap_tsd_get_scalar(l_tsd, uint256_t); + l_change_type = l_tsd->type; break; } // Verify signs @@ -125,13 +128,22 @@ static int s_emit_delegate_verificator(dap_ledger_t *a_ledger, dap_chain_tx_out_ } uint256_t l_change_value; - if (SUBTRACT_256_256(a_cond->header.value, l_writeoff_value, &l_change_value)) { + if (l_change_type == DAP_CHAIN_NET_SRV_EMIT_DELEGATE_TSD_WRITEOFF && SUBTRACT_256_256(a_cond->header.value, l_writeoff_value, &l_change_value)) { char *l_balance = dap_uint256_decimal_to_char(a_cond->header.value); - const char *l_writeoff; dap_uint256_to_char(l_writeoff_value, &l_writeoff); + const char *l_writeoff = NULL; + dap_uint256_to_char(l_change_value, &l_writeoff); log_it(L_ERROR, "Write-off value %s is greater than account balance %s", l_writeoff, l_balance); DAP_DELETE(l_balance); return -7; } + if (l_change_type == DAP_CHAIN_NET_SRV_EMIT_DELEGATE_TSD_REFILL && SUM_256_256(a_cond->header.value, l_writeoff_value, &l_change_value)) { + char *l_balance = dap_uint256_decimal_to_char(a_cond->header.value); + const char *l_refill = NULL; + dap_uint256_to_char(l_change_value, &l_refill); + log_it(L_ERROR, "Sum of re-fill value %s and account balance %s is owerflow 256 bit num", l_refill, l_balance); + DAP_DELETE(l_balance); + return -9; + } if (!IS_ZERO_256(l_change_value)) { if (!l_cond_out) { log_it(L_ERROR, "Changeback on conditional output is need but not found"); @@ -148,12 +160,9 @@ static int s_emit_delegate_verificator(dap_ledger_t *a_ledger, dap_chain_tx_out_ log_it(L_ERROR, "Condtional output in current TX have different TSD sections vs previous TX's one"); return -11; } - } else if (l_cond_out) { - log_it(L_ERROR, "Changeback on conditional output is not need but found"); - return -10; } - if (l_signs_verified < a_cond->subtype.srv_emit_delegate.signers_minimum) { + if (l_change_type == DAP_CHAIN_NET_SRV_EMIT_DELEGATE_TSD_WRITEOFF && l_signs_verified < a_cond->subtype.srv_emit_delegate.signers_minimum) { log_it(L_WARNING, "Not enough valid signs (%u from %u) for delegated emission", l_signs_verified, a_cond->subtype.srv_emit_delegate.signers_minimum); return DAP_CHAIN_CS_VERIFY_CODE_NOT_ENOUGH_SIGNS; @@ -163,7 +172,17 @@ static int s_emit_delegate_verificator(dap_ledger_t *a_ledger, dap_chain_tx_out_ static bool s_tag_check(dap_ledger_t *a_ledger, dap_chain_datum_tx_t *a_tx, dap_chain_datum_tx_item_groups_t *a_items_grp, dap_chain_tx_tag_action_type_t *a_action) { - return a_items_grp->items_out_cond_srv_emit_delegate; + if (!a_items_grp->items_out_cond_srv_emit_delegate) + return false; + if (a_action) { + if (dap_chain_datum_tx_item_get_tsd_by_type(a_tx, DAP_CHAIN_NET_SRV_EMIT_DELEGATE_TSD_WRITEOFF)) + *a_action = DAP_CHAIN_TX_TAG_ACTION_EMIT_DELEGATE_TAKE; + else if (dap_chain_datum_tx_item_get_tsd_by_type(a_tx, DAP_CHAIN_NET_SRV_EMIT_DELEGATE_TSD_REFILL)) + *a_action = DAP_CHAIN_TX_TAG_ACTION_EMIT_DELEGATE_REFILL; + else + *a_action = DAP_CHAIN_TX_TAG_ACTION_EMIT_DELEGATE_HOLD; + } + return true; } // Put a transaction to the mempool @@ -183,11 +202,11 @@ static char *s_tx_put(dap_chain_datum_tx_t *a_tx, dap_chain_t *a_chain, const ch #define m_sign_fail(e,s) { dap_json_rpc_error_add(a_json_arr_reply, e, s); log_it(L_ERROR, "%s", s); return NULL; } -#define m_tx_fail(e,s) { DAP_DELETE(l_tx); m_sign_fail(e,s) } +#define m_tx_fail(e,s) { DAP_DELETE(l_tx); m_sign_fail(e,s); log_it(L_ERROR, "%s", s); } static dap_chain_datum_tx_t *s_emitting_tx_create(json_object *a_json_arr_reply, dap_chain_net_t *a_net, dap_enc_key_t *a_enc_key, const char *a_token_ticker, uint256_t a_value, uint256_t a_fee, - uint32_t a_signs_min, dap_hash_fast_t *a_pkey_hashes, size_t a_pkey_hashes_count) + uint32_t a_signs_min, dap_hash_fast_t *a_pkey_hashes, size_t a_pkey_hashes_count, const char *a_tag_str) { const char *l_native_ticker = a_net->pub.native_ticker; bool l_delegate_native = !dap_strcmp(l_native_ticker, a_token_ticker); @@ -233,7 +252,7 @@ static dap_chain_datum_tx_t *s_emitting_tx_create(json_object *a_json_arr_reply, // add 'out_cond' & 'out_ext' items dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_EMIT_DELEGATE_ID }; dap_chain_tx_out_cond_t *l_tx_out = dap_chain_datum_tx_item_out_cond_create_srv_emit_delegate( - l_uid, a_value, a_signs_min, a_pkey_hashes, a_pkey_hashes_count); + l_uid, a_value, a_signs_min, a_pkey_hashes, a_pkey_hashes_count, a_tag_str); if (!l_tx_out) m_tx_fail(ERROR_COMPOSE, "Can't compose the transaction conditional output"); dap_chain_datum_tx_add_item(&l_tx, (const uint8_t *)l_tx_out); @@ -266,6 +285,134 @@ static dap_chain_datum_tx_t *s_emitting_tx_create(json_object *a_json_arr_reply, if (!IS_ZERO_256(l_fee_back) && dap_chain_datum_tx_add_out_ext_item(&l_tx, &l_owner_addr, l_fee_back, l_native_ticker) != 1) m_tx_fail(ERROR_COMPOSE, "Cant add fee back output"); } + // add 'sign' item + if (dap_chain_datum_tx_add_sign_item(&l_tx, a_enc_key) != 1) + m_tx_fail(ERROR_COMPOSE, "Can't add sign output"); + return l_tx; +} + + +dap_chain_datum_tx_t *dap_chain_net_srv_emit_delegate_refilling_tx_create(json_object *a_json_arr_reply, dap_chain_net_t *a_net, dap_enc_key_t *a_enc_key, + uint256_t a_value, uint256_t a_fee, dap_hash_fast_t *a_tx_in_hash, dap_list_t* tsd_items) +{ + dap_return_val_if_pass(!a_net || IS_ZERO_256(a_value) || IS_ZERO_256(a_fee), NULL); + dap_ledger_t *l_ledger = a_net->pub.ledger; + const char *l_tx_ticker = dap_ledger_tx_get_token_ticker_by_hash(a_net->pub.ledger, a_tx_in_hash); + bool l_refill_native = !dap_strcmp(a_net->pub.native_ticker, l_tx_ticker); + uint256_t l_value = a_value, l_value_transfer = {}, l_fee_transfer = {}; // how many coins to transfer + uint256_t l_net_fee, l_fee_total = a_fee; + dap_chain_addr_t l_net_fee_addr; + // create empty transaction + dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create(); + + bool l_net_fee_used = dap_chain_net_tx_get_fee(a_net->pub.id, &l_net_fee, &l_net_fee_addr); + if (l_net_fee_used && SUM_256_256(l_fee_total, l_net_fee, &l_fee_total)) + m_tx_fail(ERROR_OVERFLOW, "Integer overflow in TX composer"); + if (l_refill_native && SUM_256_256(l_value, l_fee_total, &l_value)) + m_tx_fail(ERROR_OVERFLOW, "Integer overflow in TX composer"); + + // list of transaction with 'out' items to sell + dap_chain_addr_t l_owner_addr; + dap_chain_addr_fill_from_key(&l_owner_addr, a_enc_key, a_net->pub.id); + dap_list_t *l_list_used_out = dap_ledger_get_list_tx_outs_with_val(l_ledger, l_tx_ticker, + &l_owner_addr, l_value, &l_value_transfer); + if (!l_list_used_out) + m_tx_fail(ERROR_FUNDS, "Nothing to pay for refill (not enough funds)"); + + // add 'in' items to pay for delegate + uint256_t l_value_to_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out); + dap_list_free_full(l_list_used_out, NULL); + if (!EQUAL_256(l_value_to_items, l_value_transfer)) + m_tx_fail(ERROR_COMPOSE, "Can't compose the transaction input"); + + if (!l_refill_native) { + dap_list_t *l_list_fee_out = dap_ledger_get_list_tx_outs_with_val(l_ledger, a_net->pub.native_ticker, + &l_owner_addr, l_fee_total, &l_fee_transfer); + if (!l_list_fee_out) + m_tx_fail(ERROR_FUNDS, "Nothing to pay for fee (not enough funds)"); + // add 'in' items to pay fee + uint256_t l_value_fee_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_fee_out); + dap_list_free_full(l_list_fee_out, NULL); + if (!EQUAL_256(l_value_fee_items, l_fee_transfer)) + m_tx_fail(ERROR_COMPOSE, "Can't compose the fee transaction input"); + } + + dap_hash_fast_t l_final_tx_hash = dap_ledger_get_final_chain_tx_hash(l_ledger, DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_EMIT_DELEGATE, a_tx_in_hash, false); + if (dap_hash_fast_is_blank(&l_final_tx_hash)) + m_tx_fail(ERROR_FUNDS, "Nothing to refill, can't find tx"); + + log_it(L_NOTICE, "Actual TX hash %s will be used for refill TX composing", dap_hash_fast_to_str_static(&l_final_tx_hash)); + dap_chain_datum_tx_t *l_tx_in = dap_ledger_tx_find_by_hash(l_ledger, &l_final_tx_hash); + assert(l_tx_in); + int l_prev_cond_idx = 0; + dap_chain_tx_out_cond_t *l_cond_prev = dap_chain_datum_tx_out_cond_get(l_tx_in, DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_EMIT_DELEGATE, &l_prev_cond_idx); + if (!l_cond_prev) + m_tx_fail(ERROR_TX_MISMATCH, "Requested conditional transaction requires conditional output"); + + if (dap_ledger_tx_hash_is_used_out_item(l_ledger, &l_final_tx_hash, l_prev_cond_idx, NULL)) + m_tx_fail(ERROR_TX_MISMATCH, "Requested conditional transaction is already used out"); + + // add 'in_cond' item + if (dap_chain_datum_tx_add_in_cond_item(&l_tx, &l_final_tx_hash, l_prev_cond_idx, -1) != 1) { + log_it(L_ERROR, "Can't compose the transaction conditional input"); + m_tx_fail(ERROR_COMPOSE, "Cant add conditionsl input"); + } + + uint256_t l_value_back = {}; + if(SUM_256_256(l_cond_prev->header.value, a_value, &l_value_back)) { + m_tx_fail(ERROR_OVERFLOW, "Integer overflow in TX composer"); + } + + dap_chain_tx_out_cond_t *l_out_cond = DAP_DUP_SIZE(l_cond_prev, sizeof(dap_chain_tx_out_cond_t) + l_cond_prev->tsd_size); + if (!l_out_cond) + m_tx_fail(ERROR_MEMORY, c_error_memory_alloc); + l_out_cond->header.value = l_value_back; + if (dap_chain_datum_tx_add_item(&l_tx, (const uint8_t *)l_out_cond) < 0) { + m_tx_fail(ERROR_COMPOSE, "Cant add refill cond output"); + DAP_DELETE(l_out_cond); + } + DAP_DELETE(l_out_cond); + + // add track for refill from conditional value + dap_chain_tx_tsd_t *l_refill_tsd = dap_chain_datum_tx_item_tsd_create(&a_value, DAP_CHAIN_NET_SRV_EMIT_DELEGATE_TSD_REFILL, sizeof(uint256_t)); + if (dap_chain_datum_tx_add_item(&l_tx, l_refill_tsd) != 1) { + DAP_DELETE(l_refill_tsd); + m_tx_fail(ERROR_COMPOSE, "Can't add TSD section item with withdraw value"); + } + DAP_DELETE(l_refill_tsd); + + //add other tsd if available + for ( dap_list_t *l_tsd = tsd_items; l_tsd; l_tsd = l_tsd->next ) { + if ( dap_chain_datum_tx_add_item(&l_tx, l_tsd->data) != 1 ) + m_tx_fail(ERROR_COMPOSE, "Can't add custom TSD section item "); + } + + // coin back + SUBTRACT_256_256(l_value_transfer, l_value, &l_value_back); + if (!IS_ZERO_256(l_value_back)) { + int rc = l_refill_native ? dap_chain_datum_tx_add_out_item(&l_tx, &l_owner_addr, l_value_back) + : dap_chain_datum_tx_add_out_ext_item(&l_tx, &l_owner_addr, l_value_back, l_tx_ticker); + if (rc != 1) + m_tx_fail(ERROR_COMPOSE, "Cant add coin back output"); + } + + // add fee items + if (l_net_fee_used) { + int rc = l_refill_native ? dap_chain_datum_tx_add_out_item(&l_tx, &l_net_fee_addr, l_net_fee) + : dap_chain_datum_tx_add_out_ext_item(&l_tx, &l_net_fee_addr, l_net_fee, a_net->pub.native_ticker); + if (rc != 1) + m_tx_fail(ERROR_COMPOSE, "Cant add net fee output"); + } + if (!IS_ZERO_256(a_fee) && dap_chain_datum_tx_add_fee_item(&l_tx, a_fee) != 1) + m_tx_fail(ERROR_COMPOSE, "Cant add validator fee output"); + + if (!l_refill_native) { + uint256_t l_fee_back = {}; + // fee coin back + SUBTRACT_256_256(l_fee_transfer, l_fee_total, &l_fee_back); + if (!IS_ZERO_256(l_fee_back) && dap_chain_datum_tx_add_out_ext_item(&l_tx, &l_owner_addr, l_fee_back, a_net->pub.native_ticker) != 1) + m_tx_fail(ERROR_COMPOSE, "Cant add fee back output"); + } // add 'sign' item if (dap_chain_datum_tx_add_sign_item(&l_tx, a_enc_key) != 1) @@ -290,8 +437,13 @@ static bool s_is_key_present(dap_chain_tx_out_cond_t *a_cond, dap_enc_key_t *a_e } dap_chain_datum_tx_t *dap_chain_net_srv_emit_delegate_taking_tx_create(json_object *a_json_arr_reply, dap_chain_net_t *a_net, dap_enc_key_t *a_enc_key, - dap_chain_addr_t *a_addr_to, uint256_t a_value, uint256_t a_fee, dap_hash_fast_t *a_tx_in_hash, dap_list_t* tsd_items) + dap_chain_addr_t *a_to_addr, uint256_t *a_value, uint32_t a_addr_count /*!not change type!*/, uint256_t a_fee, dap_hash_fast_t *a_tx_in_hash, dap_list_t* tsd_items) { + dap_return_val_if_pass(!a_to_addr, NULL); + dap_return_val_if_pass(!a_value, NULL); + dap_return_val_if_pass(!a_addr_count, NULL); + dap_return_val_if_pass(!a_enc_key, NULL); + dap_return_val_if_pass(!a_tx_in_hash, NULL); // create empty transaction dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create(); @@ -299,10 +451,18 @@ dap_chain_datum_tx_t *dap_chain_net_srv_emit_delegate_taking_tx_create(json_obje const char *l_tx_ticker = dap_ledger_tx_get_token_ticker_by_hash(a_net->pub.ledger, a_tx_in_hash); bool l_taking_native = !dap_strcmp(a_net->pub.native_ticker, l_tx_ticker); - uint256_t l_value = a_value, l_fee_transfer = {}; // how many coins to transfer + uint256_t l_value = {}, l_fee_transfer = {}; // how many coins to transfer uint256_t l_net_fee, l_fee_total = a_fee; dap_chain_addr_t l_net_fee_addr; + for (size_t i = 0; i < a_addr_count; ++i) { + if(IS_ZERO_256(a_value[i])) { + m_tx_fail(ERROR_VALUE, "Format -value <256 bit integer> and not equal zero"); + } + if (SUM_256_256(l_value, a_value[i], &l_value)) + m_tx_fail(ERROR_OVERFLOW, "Integer overflow in TX composer"); + } + bool l_net_fee_used = dap_chain_net_tx_get_fee(a_net->pub.id, &l_net_fee, &l_net_fee_addr); if (l_net_fee_used && SUM_256_256(l_fee_total, l_net_fee, &l_fee_total)) m_tx_fail(ERROR_OVERFLOW, "Integer overflow in TX composer"); @@ -310,7 +470,7 @@ dap_chain_datum_tx_t *dap_chain_net_srv_emit_delegate_taking_tx_create(json_obje dap_chain_addr_t l_owner_addr; dap_chain_addr_fill_from_key(&l_owner_addr, a_enc_key, a_net->pub.id); dap_list_t *l_list_fee_out = dap_ledger_get_list_tx_outs_with_val(l_ledger, a_net->pub.native_ticker, - &l_owner_addr, l_fee_total, &l_fee_transfer); + &l_owner_addr, l_fee_total, &l_fee_transfer); if (!l_list_fee_out) m_tx_fail(ERROR_FUNDS, "Nothing to pay for fee (not enough funds)"); // add 'in' items to pay fee @@ -344,22 +504,31 @@ dap_chain_datum_tx_t *dap_chain_net_srv_emit_delegate_taking_tx_create(json_obje } // add 'out' or 'out_ext' item for emission - int rc = l_taking_native ? dap_chain_datum_tx_add_out_item(&l_tx, a_addr_to, a_value) : - dap_chain_datum_tx_add_out_ext_item(&l_tx, a_addr_to, a_value, l_tx_ticker); - if (rc != 1) - m_tx_fail(ERROR_COMPOSE, "Cant add emission output"); + for (size_t i = 0; i < a_addr_count; ++i) { + int rc = l_taking_native ? dap_chain_datum_tx_add_out_item(&l_tx, a_to_addr + i, a_value[i]) : + dap_chain_datum_tx_add_out_ext_item(&l_tx, a_to_addr + i, a_value[i], l_tx_ticker); + if (rc != 1) + m_tx_fail(ERROR_COMPOSE, "Cant add tx output"); + } // coin back uint256_t l_value_back = {}; SUBTRACT_256_256(l_cond_prev->header.value, l_value, &l_value_back); - if (!IS_ZERO_256(l_value_back)) { - dap_chain_tx_out_cond_t *l_out_cond = DAP_DUP_SIZE(l_cond_prev, sizeof(dap_chain_tx_out_cond_t) + l_cond_prev->tsd_size); - if (!l_out_cond) - m_tx_fail(ERROR_COMPOSE, c_error_memory_alloc); - l_out_cond->header.value = l_value_back; - if (-1 == dap_chain_datum_tx_add_item(&l_tx, (const uint8_t *)l_out_cond)) - m_tx_fail(ERROR_COMPOSE, "Cant add emission cond output"); + dap_chain_tx_out_cond_t *l_out_cond = DAP_DUP_SIZE(l_cond_prev, sizeof(dap_chain_tx_out_cond_t) + l_cond_prev->tsd_size); + if (!l_out_cond) + m_tx_fail(ERROR_MEMORY, c_error_memory_alloc); + l_out_cond->header.value = l_value_back; + + if (-1 == dap_chain_datum_tx_add_item(&l_tx, (const uint8_t *)l_out_cond)) { DAP_DELETE(l_out_cond); + m_tx_fail(ERROR_COMPOSE, "Cant add emission cond output"); + } + DAP_DELETE(l_out_cond); + + if (a_addr_count > 1) { + dap_chain_tx_tsd_t * l_addr_cnt_tsd = dap_chain_datum_tx_item_tsd_create(&a_addr_count, DAP_CHAIN_DATUM_TRANSFER_TSD_TYPE_OUT_COUNT, sizeof(uint32_t)); + if (!l_addr_cnt_tsd || dap_chain_datum_tx_add_item(&l_tx, l_addr_cnt_tsd) != 1 ) + m_tx_fail(ERROR_COMPOSE, "Can't add TSD section item with addr count"); } // add track for takeoff from conditional value @@ -377,7 +546,7 @@ dap_chain_datum_tx_t *dap_chain_net_srv_emit_delegate_taking_tx_create(json_obje // add fee items if (l_net_fee_used) { int rc = l_taking_native ? dap_chain_datum_tx_add_out_item(&l_tx, &l_net_fee_addr, l_net_fee) - : dap_chain_datum_tx_add_out_ext_item(&l_tx, &l_net_fee_addr, l_net_fee, a_net->pub.native_ticker); + : dap_chain_datum_tx_add_out_ext_item(&l_tx, &l_net_fee_addr, l_net_fee, a_net->pub.native_ticker); if (rc != 1) m_tx_fail(ERROR_COMPOSE, "Cant add net fee output"); } @@ -388,10 +557,10 @@ dap_chain_datum_tx_t *dap_chain_net_srv_emit_delegate_taking_tx_create(json_obje // fee coin back SUBTRACT_256_256(l_fee_transfer, l_fee_total, &l_fee_back); if (!IS_ZERO_256(l_fee_back)) { - int rc = l_taking_native ? dap_chain_datum_tx_add_out_item(&l_tx, &l_owner_addr, l_fee_back) - : dap_chain_datum_tx_add_out_ext_item(&l_tx, &l_owner_addr, l_fee_back, a_net->pub.native_ticker); - if (rc != 1) - m_tx_fail(ERROR_COMPOSE, "Cant add fee back output"); + int rc = l_taking_native ? dap_chain_datum_tx_add_out_item(&l_tx, &l_owner_addr, l_fee_back) + : dap_chain_datum_tx_add_out_ext_item(&l_tx, &l_owner_addr, l_fee_back, a_net->pub.native_ticker); + if (rc != 1) + m_tx_fail(ERROR_COMPOSE, "Cant add fee back output"); } // add 'sign' item @@ -401,6 +570,7 @@ dap_chain_datum_tx_t *dap_chain_net_srv_emit_delegate_taking_tx_create(json_obje return l_tx; } + #undef m_tx_fail dap_chain_datum_tx_t *dap_chain_net_srv_emit_delegate_taking_tx_sign(json_object *a_json_arr_reply, dap_chain_net_t *a_net, dap_enc_key_t *a_enc_key, dap_chain_datum_tx_t *a_tx_in) @@ -433,7 +603,7 @@ dap_chain_datum_tx_t *dap_chain_net_srv_emit_delegate_taking_tx_sign(json_object } dap_chain_datum_tx_t *l_tx = DAP_DUP_SIZE(a_tx_in, dap_chain_datum_tx_get_size(a_tx_in)); if (!l_tx) - m_sign_fail(ERROR_FUNDS, c_error_memory_alloc); + m_sign_fail(ERROR_MEMORY, c_error_memory_alloc); // add 'sign' item if (dap_chain_datum_tx_add_sign_item(&l_tx, a_enc_key) != 1) m_sign_fail(ERROR_COMPOSE, "Can't add sign output"); @@ -444,7 +614,14 @@ dap_chain_datum_tx_t *dap_chain_net_srv_emit_delegate_taking_tx_sign(json_object static int s_cli_hold(int a_argc, char **a_argv, int a_arg_index, json_object **a_json_arr_reply, dap_chain_net_t *a_net, dap_chain_t *a_chain, const char *a_hash_out_type) { - const char *l_token_str = NULL, *l_value_str = NULL, *l_wallet_str = NULL, *l_fee_str = NULL, *l_signs_min_str = NULL, *l_pkeys_str = NULL; + const char *l_token_str = NULL, + *l_value_str = NULL, + *l_wallet_str = NULL, + *l_fee_str = NULL, + *l_signs_min_str = NULL, + *l_pkeys_str = NULL, + *l_tag_str = NULL; + dap_cli_server_cmd_find_option_val(a_argv, a_arg_index, a_argc, "-token", &l_token_str); if (!l_token_str) { dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Emitting delegation holding requires parameter -token"); @@ -461,7 +638,7 @@ static int s_cli_hold(int a_argc, char **a_argv, int a_arg_index, json_object ** } uint256_t l_value = dap_chain_balance_scan(l_value_str); if (IS_ZERO_256(l_value)) { - dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Format -value <256 bit integer>"); + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Format -value <256 bit integer> and not equal zero"); return ERROR_VALUE; } dap_cli_server_cmd_find_option_val(a_argv, a_arg_index, a_argc, "-fee", &l_fee_str); @@ -471,7 +648,7 @@ static int s_cli_hold(int a_argc, char **a_argv, int a_arg_index, json_object ** } uint256_t l_fee = dap_chain_balance_scan(l_fee_str); if (IS_ZERO_256(l_fee)) { - dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Format -fee <256 bit integer>"); + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Format -fee <256 bit integer> and not equal zer"); return ERROR_VALUE; } dap_cli_server_cmd_find_option_val(a_argv, a_arg_index, a_argc, "-signs_minimum", &l_signs_min_str); @@ -489,11 +666,15 @@ static int s_cli_hold(int a_argc, char **a_argv, int a_arg_index, json_object ** dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Emitting delegation holding requires parameter -w"); return ERROR_PARAM; } + dap_chain_wallet_t *l_wallet = dap_chain_wallet_open(l_wallet_str, dap_chain_wallet_get_path(g_config), NULL); if (!l_wallet) { dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Specified wallet %s not found", l_wallet_str); return ERROR_VALUE; } + + dap_cli_server_cmd_find_option_val(a_argv, a_arg_index, a_argc, "-tag", &l_tag_str); + const char *l_sign_str = dap_chain_wallet_check_sign(l_wallet); dap_enc_key_t *l_enc_key = dap_chain_wallet_get_key(l_wallet, 0); dap_chain_wallet_close(l_wallet); @@ -524,6 +705,13 @@ static int s_cli_hold(int a_argc, char **a_argv, int a_arg_index, json_object ** DAP_DEL_MULTY(l_enc_key, l_pkey_hashes); return ERROR_VALUE; } + for (size_t j = 0; j < i; ++j) { + if (!memcmp(l_pkey_hashes + j, l_pkey_hashes + i, sizeof(dap_chain_hash_fast_t))){ + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Find pkey hash %s dublicate", l_hash_str_buf); + DAP_DEL_MULTY(l_enc_key, l_pkey_hashes); + return ERROR_VALUE; + } + } if (*l_cur_ptr == 0) { l_hashes_count = i + 1; break; @@ -541,7 +729,7 @@ static int s_cli_hold(int a_argc, char **a_argv, int a_arg_index, json_object ** return ERROR_VALUE; } // Create conditional transaction for delegated emissions - dap_chain_datum_tx_t *l_tx = s_emitting_tx_create(*a_json_arr_reply, a_net, l_enc_key, l_token_str, l_value, l_fee, l_signs_min, l_pkey_hashes, l_hashes_count); + dap_chain_datum_tx_t *l_tx = s_emitting_tx_create(*a_json_arr_reply, a_net, l_enc_key, l_token_str, l_value, l_fee, l_signs_min, l_pkey_hashes, l_hashes_count, l_tag_str); DAP_DEL_MULTY(l_enc_key, l_pkey_hashes); if (!l_tx) { dap_json_rpc_error_add(*a_json_arr_reply, ERROR_CREATE, "Can't compose transaction for delegated emission"); @@ -563,12 +751,39 @@ static int s_cli_hold(int a_argc, char **a_argv, int a_arg_index, json_object ** return DAP_NO_ERROR; } -static int s_cli_take(int a_argc, char **a_argv, int a_arg_index, json_object **a_json_arr_reply, dap_chain_net_t *a_net, dap_chain_t *a_chain, const char *a_hash_out_type) +static int s_cli_refill(int a_argc, char **a_argv, int a_arg_index, json_object **a_json_arr_reply, dap_chain_net_t *a_net, dap_chain_t *a_chain, const char *a_hash_out_type) { - const char *l_tx_in_hash_str = NULL, *l_addr_str = NULL, *l_value_str = NULL, *l_wallet_str = NULL, *l_fee_str = NULL; + const char *l_token_str = NULL, *l_value_str = NULL, *l_wallet_str = NULL, *l_fee_str = NULL, *l_tx_in_hash_str = NULL; + dap_cli_server_cmd_find_option_val(a_argv, a_arg_index, a_argc, "-value", &l_value_str); + if (!l_value_str) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Refill command requires parameter -value"); + return ERROR_PARAM; + } + uint256_t l_value = dap_chain_balance_scan(l_value_str); + if (IS_ZERO_256(l_value)) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Format -value <256 bit integer> and not equal zero"); + return ERROR_VALUE; + } + dap_cli_server_cmd_find_option_val(a_argv, a_arg_index, a_argc, "-fee", &l_fee_str); + if (!l_fee_str) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Refill command requires parameter -fee"); + return ERROR_PARAM; + } + uint256_t l_fee = dap_chain_balance_scan(l_fee_str); + if (IS_ZERO_256(l_fee)) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Format -fee <256 bit integer> and not equal zer"); + return ERROR_VALUE; + } + + dap_cli_server_cmd_find_option_val(a_argv, a_arg_index, a_argc, "-w", &l_wallet_str); + if (!l_wallet_str) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Refill command requires parameter -w"); + return ERROR_PARAM; + } + dap_cli_server_cmd_find_option_val(a_argv, a_arg_index, a_argc, "-tx", &l_tx_in_hash_str); if (!l_tx_in_hash_str) { - dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Emitting delegation taking requires parameter -tx"); + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Refill command requires parameter -tx"); return ERROR_PARAM; } dap_hash_fast_t l_tx_in_hash; @@ -580,16 +795,66 @@ static int s_cli_take(int a_argc, char **a_argv, int a_arg_index, json_object ** dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "TX %s not found in ledger", l_tx_in_hash_str); return ERROR_VALUE; } - dap_cli_server_cmd_find_option_val(a_argv, a_arg_index, a_argc, "-value", &l_value_str); - if (!l_value_str) { - dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Emitting delegation taking requires parameter -value"); + + + dap_chain_wallet_t *l_wallet = dap_chain_wallet_open(l_wallet_str, dap_chain_wallet_get_path(g_config), NULL); + if (!l_wallet) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Specified wallet %s not found", l_wallet_str); + return ERROR_VALUE; + } + const char *l_sign_str = dap_chain_wallet_check_sign(l_wallet); + dap_enc_key_t *l_enc_key = dap_chain_wallet_get_key(l_wallet, 0); + dap_chain_wallet_close(l_wallet); + + // Create conditional transaction for refill + dap_chain_datum_tx_t *l_tx = dap_chain_net_srv_emit_delegate_refilling_tx_create(*a_json_arr_reply, a_net, l_enc_key, l_value, l_fee, &l_tx_in_hash, NULL); + DAP_DELETE(l_enc_key); + if (!l_tx) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_CREATE, "Can't compose transaction for refill shared funds tx"); + return ERROR_CREATE; + } + char *l_tx_hash_str = s_tx_put(l_tx, a_chain, a_hash_out_type); + DAP_DELETE(l_tx); + if (!l_tx_hash_str) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PLACE, "Can't place transaction for refill shared funds tx in mempool"); + return ERROR_PLACE; + } + json_object * l_json_obj_create_val = json_object_new_object(); + json_object_object_add(l_json_obj_create_val, "status", json_object_new_string("success")); + if (dap_strcmp(l_sign_str, "")) + json_object_object_add(l_json_obj_create_val, "sign", json_object_new_string(l_sign_str)); + json_object_object_add(l_json_obj_create_val, "tx_hash", json_object_new_string(l_tx_hash_str)); + json_object_array_add(*a_json_arr_reply, l_json_obj_create_val); + DAP_DELETE(l_tx_hash_str); + return DAP_NO_ERROR; +} + +static int s_cli_take(int a_argc, char **a_argv, int a_arg_index, json_object **a_json_arr_reply, dap_chain_net_t *a_net, dap_chain_t *a_chain, const char *a_hash_out_type) +{ + const char *l_tx_in_hash_str = NULL, *l_addr_str = NULL, *l_value_str = NULL, *l_wallet_str = NULL, *l_fee_str = NULL; + + uint256_t *l_value = NULL; + dap_chain_addr_t *l_to_addr = NULL; + uint32_t + l_addr_el_count = 0, // not change type! use in batching TSD section + l_value_el_count = 0; + dap_list_t *l_tsd_list = NULL; + + dap_cli_server_cmd_find_option_val(a_argv, a_arg_index, a_argc, "-tx", &l_tx_in_hash_str); + if (!l_tx_in_hash_str) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Emitting delegation taking requires parameter -tx"); return ERROR_PARAM; } - uint256_t l_value = dap_chain_balance_scan(l_value_str); - if (IS_ZERO_256(l_value)) { - dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Format -value <256 bit integer>"); + dap_hash_fast_t l_tx_in_hash; + if (dap_chain_hash_fast_from_str(l_tx_in_hash_str, &l_tx_in_hash)) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Can't recognize %s as a hex or base58 format hash", l_tx_in_hash_str); + return ERROR_VALUE; + } + if (!dap_ledger_tx_find_by_hash(a_net->pub.ledger, &l_tx_in_hash)) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "TX %s not found in ledger", l_tx_in_hash_str); return ERROR_VALUE; } + dap_cli_server_cmd_find_option_val(a_argv, a_arg_index, a_argc, "-fee", &l_fee_str); if (!l_fee_str) { dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Emitting delegation taking requires parameter -fee"); @@ -597,7 +862,7 @@ static int s_cli_take(int a_argc, char **a_argv, int a_arg_index, json_object ** } uint256_t l_fee = dap_chain_balance_scan(l_fee_str); if (IS_ZERO_256(l_fee)) { - dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Format -fee <256 bit integer>"); + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Format -fee <256 bit integer> and not equal zer"); return ERROR_VALUE; } @@ -614,21 +879,55 @@ static int s_cli_take(int a_argc, char **a_argv, int a_arg_index, json_object ** const char *l_sign_str = dap_chain_wallet_check_sign(l_wallet); dap_enc_key_t *l_enc_key = dap_chain_wallet_get_key(l_wallet, 0); dap_chain_wallet_close(l_wallet); - dap_cli_server_cmd_find_option_val(a_argv, a_arg_index, a_argc, "-addr_to", &l_addr_str); + + dap_cli_server_cmd_find_option_val(a_argv, a_arg_index, a_argc, "-value", &l_value_str); if (!l_value_str) { - DAP_DELETE(l_enc_key); - dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Emitting delegation taking requires parameter -addr_to"); + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Emitting delegation taking requires parameter -value"); return ERROR_PARAM; } - dap_chain_addr_t *l_addr = dap_chain_addr_from_str(l_addr_str); - if (!l_addr) { + dap_cli_server_cmd_find_option_val(a_argv, a_arg_index, a_argc, "-to_addr", &l_addr_str); + if (!l_addr_str) { DAP_DELETE(l_enc_key); - dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Incorrect addr format for string %s", l_addr_str); + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Emitting delegation taking requires parameter -to_addr"); + return ERROR_PARAM; + } + + l_addr_el_count = dap_chain_addr_from_str_array(l_addr_str, &l_to_addr); + l_value_el_count = dap_str_symbol_count(l_value_str, ',') + 1; + + if (l_addr_el_count != l_value_el_count) { + DAP_DELETE(l_to_addr); + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "num of '-to_addr' and '-value' should be equal"); return ERROR_VALUE; } - // Create emission from conditional transaction - dap_chain_datum_tx_t *l_tx = dap_chain_net_srv_emit_delegate_taking_tx_create(*a_json_arr_reply, a_net, l_enc_key, l_addr, l_value, l_fee, &l_tx_in_hash, NULL); - DAP_DEL_MULTY(l_enc_key, l_addr); + + l_value = DAP_NEW_Z_COUNT(uint256_t, l_value_el_count); + if (!l_value) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_MEMORY, c_error_memory_alloc); + return ERROR_MEMORY; + } + char **l_value_array = dap_strsplit(l_value_str, ",", l_value_el_count); + if (!l_value_array) { + DAP_DELETE(l_value); + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Can't read '-to_addr' arg"); + return ERROR_PARAM; + } + for (size_t i = 0; i < l_value_el_count; ++i) { + l_value[i] = dap_chain_balance_scan(l_value_array[i]); + if(IS_ZERO_256(l_value[i])) { + DAP_DELETE(l_value); + dap_strfreev(l_value_array); + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Format -value <256 bit integer> and not equal zero"); + return ERROR_VALUE; + } + } + dap_strfreev(l_value_array); + + // Create emission from conditional transaction + + dap_chain_datum_tx_t *l_tx = dap_chain_net_srv_emit_delegate_taking_tx_create(*a_json_arr_reply, a_net, l_enc_key, l_to_addr, l_value, l_addr_el_count, l_fee, &l_tx_in_hash, l_tsd_list); + DAP_DEL_MULTY(l_value, l_to_addr, l_enc_key); + dap_list_free_full(l_tsd_list, NULL); if (!l_tx) { dap_json_rpc_error_add(*a_json_arr_reply, ERROR_CREATE, "Can't compose transaction for delegated emission"); return ERROR_CREATE; @@ -651,7 +950,7 @@ static int s_cli_take(int a_argc, char **a_argv, int a_arg_index, json_object ** static int s_cli_sign(int a_argc, char **a_argv, int a_arg_index, json_object **a_json_arr_reply, dap_chain_net_t *a_net, dap_chain_t *a_chain, const char *a_hash_out_type) { - const char *l_tx_in_hash_str = NULL, *l_wallet_str = NULL; + const char *l_tx_in_hash_str = NULL, *l_wallet_str = NULL, *l_cert_str = NULL; dap_cli_server_cmd_find_option_val(a_argv, a_arg_index, a_argc, "-tx", &l_tx_in_hash_str); if (!l_tx_in_hash_str) { dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Emitting delegation taking requires parameter -tx"); @@ -667,19 +966,37 @@ static int s_cli_sign(int a_argc, char **a_argv, int a_arg_index, json_object ** dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "TX %s not found in mempool", l_tx_in_hash_str); return ERROR_VALUE; } + + dap_enc_key_t *l_enc_key = NULL; + const char *l_sign_str = NULL; + dap_cli_server_cmd_find_option_val(a_argv, a_arg_index, a_argc, "-w", &l_wallet_str); - if (!l_wallet_str) { - dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Emitting delegation taking requires parameter -w"); + dap_cli_server_cmd_find_option_val(a_argv, a_arg_index, a_argc, "-cert", &l_cert_str); + if (!l_wallet_str && !l_cert_str) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Emitting delegation sign requires parameter -w or -cert"); return ERROR_PARAM; } - dap_chain_wallet_t *l_wallet = dap_chain_wallet_open(l_wallet_str, dap_chain_wallet_get_path(g_config), NULL); - if (!l_wallet) { - dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Specified wallet %s not found", l_wallet_str); - return ERROR_VALUE; + if (l_wallet_str) { + dap_chain_wallet_t *l_wallet = dap_chain_wallet_open(l_wallet_str, dap_chain_wallet_get_path(g_config), NULL); + if (!l_wallet) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Specified wallet %s not found", l_wallet_str); + return ERROR_VALUE; + } + l_sign_str = dap_chain_wallet_check_sign(l_wallet); + l_enc_key = dap_chain_wallet_get_key(l_wallet, 0); + dap_chain_wallet_close(l_wallet); + } else if (l_cert_str) { + dap_cert_t *l_cert = dap_cert_find_by_name(l_cert_str); + if (!l_cert) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Specified certificate %s not found", l_cert_str); + return ERROR_VALUE; + } + if (dap_sign_type_is_depricated(dap_sign_type_from_key_type(l_cert->enc_key->type))) + l_sign_str = "The Bliss, Picnic and Tesla signatures is deprecated. We recommend you to create a new wallet with another available signature and transfer funds there.\n"; + else + l_sign_str = ""; + l_enc_key = dap_cert_get_keys_from_certs(&l_cert, 1, 0); } - const char *l_sign_str = dap_chain_wallet_check_sign(l_wallet); - dap_enc_key_t *l_enc_key = dap_chain_wallet_get_key(l_wallet, 0); - dap_chain_wallet_close(l_wallet); // Create emission from conditional transaction dap_chain_datum_tx_t *l_tx = dap_chain_net_srv_emit_delegate_taking_tx_sign(*a_json_arr_reply, a_net, l_enc_key, (dap_chain_datum_tx_t *)l_tx_in->data); @@ -707,6 +1024,70 @@ static int s_cli_sign(int a_argc, char **a_argv, int a_arg_index, json_object ** return DAP_NO_ERROR; } +static int s_cli_info(int a_argc, char **a_argv, int a_arg_index, json_object **a_json_arr_reply, dap_chain_net_t *a_net, dap_chain_t *a_chain, const char *a_hash_out_type) +{ + const char *l_tx_hash_str = NULL, *l_wallet_str = NULL; + dap_cli_server_cmd_find_option_val(a_argv, a_arg_index, a_argc, "-tx", &l_tx_hash_str); + if (!l_tx_hash_str) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Emitting delegation taking requires parameter -tx"); + return ERROR_PARAM; + } + dap_hash_fast_t l_tx_hash; + if (dap_chain_hash_fast_from_str(l_tx_hash_str, &l_tx_hash)) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_VALUE, "Can't recognize %s as a hex or base58 format hash", l_tx_hash_str); + return ERROR_VALUE; + } + dap_hash_fast_t l_final_tx_hash = dap_ledger_get_final_chain_tx_hash(a_net->pub.ledger, DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_EMIT_DELEGATE, &l_tx_hash, false); + dap_chain_datum_tx_t *l_tx = dap_ledger_tx_find_by_hash(a_net->pub.ledger, &l_final_tx_hash); + dap_chain_tx_out_cond_t *l_cond = dap_chain_datum_tx_out_cond_get(l_tx, DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_EMIT_DELEGATE, NULL); + if (!l_cond) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_TX_MISMATCH, "Can't find final tx_out_cond"); + return ERROR_TX_MISMATCH; + } + + const char *l_tx_ticker = dap_ledger_tx_get_token_ticker_by_hash(a_net->pub.ledger, &l_final_tx_hash); + const char *l_balance_coins, *l_balance_datoshi = dap_uint256_to_char(l_cond->header.value, &l_balance_coins); + + json_object *l_jobj_balance = json_object_new_object(); + json_object *l_jobj_token = json_object_new_object(); + json_object *l_jobj_take_verify = json_object_new_object(); + json_object *l_jobj_pkey_hashes = json_object_new_array(); + json_object *l_jobj_tags = json_object_new_array(); + json_object *l_json_jobj_info = json_object_new_object(); + + bool l_is_base_hash_type = dap_strcmp(a_hash_out_type, "hex"); + // tocken block + const char *l_description = dap_ledger_get_description_by_ticker(a_net->pub.ledger, l_tx_ticker); + json_object *l_jobj_description = l_description ? json_object_new_string(l_description) + : json_object_new_null(); + json_object_object_add(l_jobj_token, "ticker", json_object_new_string(l_tx_ticker)); + json_object_object_add(l_jobj_token, "description", l_jobj_description); + // balance block + json_object_object_add(l_jobj_balance, "coins", json_object_new_string(l_balance_coins)); + json_object_object_add(l_jobj_balance, "datoshi", json_object_new_string(l_balance_datoshi)); + // verify block + json_object_object_add(l_jobj_take_verify, "signs_minimum", json_object_new_uint64(l_cond->subtype.srv_emit_delegate.signers_minimum)); + dap_tsd_t *l_tsd = NULL; size_t l_tsd_size = 0; + dap_tsd_iter(l_tsd, l_tsd_size, l_cond->tsd, l_cond->tsd_size) { + if (l_tsd->type == DAP_CHAIN_TX_OUT_COND_TSD_HASH && l_tsd->size == sizeof(dap_hash_fast_t)) { + json_object_array_add(l_jobj_pkey_hashes, json_object_new_string(l_is_base_hash_type ? dap_enc_base58_encode_hash_to_str_static((const dap_chain_hash_fast_t *)l_tsd->data) : dap_hash_fast_to_str_static((const dap_chain_hash_fast_t *)l_tsd->data))); + } + if (l_tsd->type == DAP_CHAIN_TX_OUT_COND_TSD_STR) { + json_object_array_add(l_jobj_tags, json_object_new_string((char*)(l_tsd->data))); + } + } + json_object_object_add(l_jobj_take_verify, "owner_hashes", l_jobj_pkey_hashes); + // result block + json_object_object_add(l_json_jobj_info, "tx_hash", json_object_new_string(l_is_base_hash_type ? dap_enc_base58_encode_hash_to_str_static(&l_tx_hash) : dap_hash_fast_to_str_static(&l_tx_hash))); + json_object_object_add(l_json_jobj_info, "tx_hash_final", json_object_new_string(l_is_base_hash_type ? dap_enc_base58_encode_hash_to_str_static(&l_final_tx_hash) : dap_hash_fast_to_str_static(&l_final_tx_hash))); + json_object_object_add(l_json_jobj_info, "tags", l_jobj_tags); + json_object_object_add(l_json_jobj_info, "balance", l_jobj_balance); + json_object_object_add(l_json_jobj_info, "take_verify", l_jobj_take_verify); + json_object_object_add(l_json_jobj_info, "token", l_jobj_token); + json_object_array_add(*a_json_arr_reply, l_json_jobj_info); + return DAP_NO_ERROR; +} + /** * @brief s_cli_stake_lock * @param a_argc @@ -717,9 +1098,6 @@ static int s_cli_sign(int a_argc, char **a_argv, int a_arg_index, json_object ** static int s_cli_emit_delegate(int a_argc, char **a_argv, void **a_str_reply) { json_object **a_json_arr_reply = (json_object **)a_str_reply; - enum { - CMD_NONE, CMD_HOLD, CMD_TAKE, CMD_SIGN - }; int l_arg_index = 1; dap_chain_net_t *l_net = NULL; dap_chain_t *l_chain = NULL; @@ -727,7 +1105,7 @@ static int s_cli_emit_delegate(int a_argc, char **a_argv, void **a_str_reply) dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-H", &l_hash_out_type); if (!l_hash_out_type) l_hash_out_type = "hex"; - else if (dap_strcmp(l_hash_out_type," hex") && dap_strcmp(l_hash_out_type, "base58")) { + else if (dap_strcmp(l_hash_out_type,"hex") && dap_strcmp(l_hash_out_type, "base58")) { dap_json_rpc_error_add(*a_json_arr_reply, ERROR_PARAM, "Invalid parameter -H, valid values: -H <hex | base58>"); return ERROR_PARAM; @@ -736,13 +1114,21 @@ static int s_cli_emit_delegate(int a_argc, char **a_argv, void **a_str_reply) if (l_err_net_chain) return l_err_net_chain; - int l_cmd_num = CMD_NONE; + if (dap_chain_net_get_load_mode(l_net)) { + dap_json_rpc_error_add(*a_json_arr_reply, ERROR_NETWORK, "Can't apply command while network in load mode"); + return ERROR_NETWORK; + } + if (dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, dap_min(a_argc, l_arg_index + 1), "hold", NULL)) return s_cli_hold(a_argc, a_argv, l_arg_index + 1, a_json_arr_reply, l_net, l_chain, l_hash_out_type); + else if (dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, dap_min(a_argc, l_arg_index + 1), "refill", NULL)) + return s_cli_refill(a_argc, a_argv, l_arg_index + 1, a_json_arr_reply, l_net, l_chain, l_hash_out_type); else if (dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, dap_min(a_argc, l_arg_index + 1), "take", NULL)) return s_cli_take(a_argc, a_argv, l_arg_index + 1, a_json_arr_reply, l_net, l_chain, l_hash_out_type); else if (dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, dap_min(a_argc, l_arg_index + 1), "sign", NULL)) return s_cli_sign(a_argc, a_argv, l_arg_index + 1, a_json_arr_reply, l_net, l_chain, l_hash_out_type); + else if (dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, dap_min(a_argc, l_arg_index + 1), "info", NULL)) + return s_cli_info(a_argc, a_argv, l_arg_index + 1, a_json_arr_reply, l_net, l_chain, l_hash_out_type); else { dap_json_rpc_error_add(*a_json_arr_reply, ERROR_SUBCOMMAND, "Subcommand %s not recognized", a_argv[l_arg_index]); return ERROR_SUBCOMMAND; @@ -753,13 +1139,43 @@ int dap_chain_net_srv_emit_delegate_init() { dap_ledger_verificator_add(DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_EMIT_DELEGATE, s_emit_delegate_verificator, NULL, NULL); dap_cli_server_cmd_add("emit_delegate", s_cli_emit_delegate, "Emitting delegation service commands", - "emit_delegate hold -net <net_name> -w <wallet_name> -token <ticker> -value <value> -fee <value>" - "-signs_minimum <value_int> -pkey_hashes <hash1[,hash2,...,hashN]> [-chain <chain_name>] [-H {hex(default) | base58}]\n" - "emit_delegate take -net <net_name> -w <wallet_name> -tx <transaction_hash> -addr_to <addr> -value <value> -fee <value> [-chain <chain_name>] [-H {hex(default) | base58}]\n" - "emit_delegate sign -net <net_name> -w <wallet_name> -tx <transaction_hash> [-chain <chain_name>] [-H {hex(default) | base58}]\n\n" - "Hint:\n" - "\texample value_coins (only natural) 1.0 123.4567\n" - "\texample value_datoshi (only integer) 1 20 0.4321e+4\n" + "emit_delegate hold - to create new delegation\n" + "\t-net <net_name>\n" + "\t-w <wallet_name> - wallet to writeoff value, pay fee and sign tx\n" + "\t-token <ticker> - token ticker to hold\n" + "\t-value <value> - value to hold\n" + "\t-fee <value> - fee value\n" + "\t-signs_minimum <value_int> - minimum signs count needed to verify take datum\n" + "\t-pkey_hashes <hash1[,hash2,...,hashN]> - owners pkey hashes, who can sign take datum\n" + "\t[-tag \"<str>\"] - additional info about tx\n" + "\t[-H {hex(default) | base58}] - datum hash return format\n" + "emit_delegate refill - to refill value\n" + "\t-net <net_name>\n" + "\t-w <wallet_name> - wallet to writeoff value and pay fee\n" + "\t-value <value> - value to refill\n" + "\t-fee <value> - fee value\n" + "\t-tx <transaction_hash> - shared funds tx hash to refill\n" + "\t[-H {hex(default) | base58}] - datum hash return format\n" + "emit_delegate take - create take datum to writeoff value from0 shared funds tx\n" + "\t-net <net_name>\n" + "\t-w <wallet_name> - wallet to pay fee\n" + "\t-tx <transaction_hash> - shared funds tx hash to writeoff\n" + "\t-to_addr <addr1[,addr2,...,addrN]> - recipient addresses, count should be equal values count\n" + "\t-value <value1[,value2,...,valueN]> - value sent to each recipient, count should be equal addresses count\n" + "\t-fee <value> - fee value\n" + "\t[-H {hex(default) | base58}] - datum hash return format\n" + "emit_delegate sign - add wallet sign to take datum\n" + "\t-net <net_name>\n" + "\t-w <wallet_name> | -cert <cert_name> - wallet or cert to sign\n" + "\t-tx <transaction_hash> - shared funds tx hash to sign\n" + "\t[-H {hex(default) | base58}] - datum hash return format\n" + "emit_delegate info - get info about shared funds tx by hash\n" + "\t-net <net_name>\n" + "\t-tx <transaction_hash> - shared funds tx hash to get info\n" + "\t[-H {hex(default) | base58}] - tx hash format\n" + "Hint:\n" + "\texample value_coins (only natural) 1.0 123.4567\n" + "\texample value_datoshi (only integer) 1 20 0.4321e+4\n" ); dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_EMIT_DELEGATE_ID }; diff --git a/modules/service/emit-delegate/include/dap_chain_net_srv_emit_delegate.h b/modules/service/emit-delegate/include/dap_chain_net_srv_emit_delegate.h index 8cca1e2faa3dd7a585a89c9a4cbbd84f001b4f71..d0545c22eb66f6f010095f418facca464cf572c6 100644 --- a/modules/service/emit-delegate/include/dap_chain_net_srv_emit_delegate.h +++ b/modules/service/emit-delegate/include/dap_chain_net_srv_emit_delegate.h @@ -2,6 +2,7 @@ #define DAP_CHAIN_NET_SRV_EMIT_DELEGATE_ID 0x07 #define DAP_CHAIN_NET_SRV_EMIT_DELEGATE_TSD_WRITEOFF 0x14 +#define DAP_CHAIN_NET_SRV_EMIT_DELEGATE_TSD_REFILL 0x15 #include "dap_chain_datum_tx.h" #include "dap_chain_mempool.h" @@ -12,6 +13,8 @@ void dap_chain_net_srv_emit_delegate_deinit(); dap_chain_datum_tx_t *dap_chain_net_srv_emit_delegate_taking_tx_create(json_object *a_json_arr_rweply, dap_chain_net_t *a_net, dap_enc_key_t *a_enc_key, - dap_chain_addr_t *a_addr_to, uint256_t a_value, uint256_t a_fee, dap_hash_fast_t *a_tx_in_hash, dap_list_t *tsd_items); + dap_chain_addr_t *a_addr_to, uint256_t *a_value, uint32_t a_addr_count, uint256_t a_fee, dap_hash_fast_t *a_tx_in_hash, dap_list_t *tsd_items); +dap_chain_datum_tx_t *dap_chain_net_srv_emit_delegate_refilling_tx_create(json_object *a_json_arr_reply, dap_chain_net_t *a_net, dap_enc_key_t *a_enc_key, + uint256_t a_value, uint256_t a_fee, dap_hash_fast_t *a_tx_in_hash, dap_list_t* tsd_items); dap_chain_datum_tx_t *dap_chain_net_srv_emit_delegate_taking_tx_sign(json_object *a_json_arr_reply, dap_chain_net_t *a_net, dap_enc_key_t *a_enc_key, dap_chain_datum_tx_t *a_tx_in); diff --git a/modules/service/stake/dap_chain_net_srv_stake_pos_delegate.c b/modules/service/stake/dap_chain_net_srv_stake_pos_delegate.c index 59320cec6de15245daecfc9701db163d7dd24cf3..73fe463d109981f771cd73ad912c6d9687ff4f20 100644 --- a/modules/service/stake/dap_chain_net_srv_stake_pos_delegate.c +++ b/modules/service/stake/dap_chain_net_srv_stake_pos_delegate.c @@ -135,17 +135,6 @@ static bool s_tag_check_key_delegation(dap_ledger_t *a_ledger, dap_chain_datum_t return false; } -static dap_pkey_t *s_get_pkey_by_hash_callback(const uint8_t *a_hash) -{ - - dap_chain_net_srv_stake_item_t *l_stake = NULL; - for (dap_list_t *l_srv_stake_list = s_srv_stake_list; l_srv_stake_list && !l_stake; l_srv_stake_list = l_srv_stake_list->next) { - dap_chain_net_srv_stake_t *l_srv_stake = l_srv_stake_list->data; - HASH_FIND(hh, l_srv_stake->itemlist, a_hash, sizeof(dap_hash_fast_t), l_stake); - } - return l_stake ? l_stake->pkey : NULL; -} - /** * @brief dap_stream_ch_vpn_init Init actions for VPN stream channel * @return 0 if everything is okay, lesser then zero if errors @@ -197,7 +186,6 @@ int dap_chain_net_srv_stake_pos_delegate_init() dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_STAKE_POS_DELEGATE_ID }; dap_ledger_service_add(l_uid, "pos_delegate", s_tag_check_key_delegation); s_debug_more = dap_config_get_item_bool_default(g_config, "stake", "debug_more", s_debug_more); - dap_sign_set_pkey_by_hash_callback(s_get_pkey_by_hash_callback); return 0; } @@ -278,7 +266,7 @@ static int s_stake_verificator_callback(dap_ledger_t *a_ledger, dap_chain_tx_out log_it(L_WARNING, "Conditional out and conditional in have different headers"); \ return -3; \ } \ - if (l_tx_new_cond->tsd_size != a_cond->tsd_size || \ + if (l_tx_new_cond->tsd_size < a_cond->tsd_size || \ memcmp(l_tx_new_cond->tsd, a_cond->tsd, a_cond->tsd_size)) { \ log_it(L_WARNING, "Conditional out and conditional in have different TSD sections"); \ return -4; \ @@ -1172,9 +1160,7 @@ dap_chain_datum_decree_t *dap_chain_net_srv_stake_decree_approve(dap_chain_net_t 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->pub.id; - dap_chain_t *l_chain = dap_chain_net_get_default_chain_by_chain_type(a_net, CHAIN_TYPE_ANCHOR); - if (!l_chain) - l_chain = dap_chain_net_get_chain_by_chain_type(a_net, CHAIN_TYPE_ANCHOR); + dap_chain_t *l_chain = dap_chain_net_get_chain_by_chain_type(a_net, CHAIN_TYPE_ANCHOR); if (!l_chain) { log_it(L_ERROR, "No chain supported anchor datum type"); DAP_DEL_Z(l_decree); @@ -1237,9 +1223,7 @@ static dap_chain_datum_decree_t *s_decree_pkey_update(dap_chain_net_t *a_net, da 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->pub.id; - dap_chain_t *l_chain = dap_chain_net_get_default_chain_by_chain_type(a_net, CHAIN_TYPE_ANCHOR); - if (!l_chain) - l_chain = dap_chain_net_get_chain_by_chain_type(a_net, CHAIN_TYPE_ANCHOR); + dap_chain_t *l_chain = dap_chain_net_get_chain_by_chain_type(a_net, CHAIN_TYPE_ANCHOR); if (!l_chain) { log_it(L_ERROR, "No chain supported anchor datum type"); DAP_DEL_Z(l_decree); @@ -1274,9 +1258,7 @@ static char *s_stake_decree_put(dap_chain_datum_decree_t *a_decree, dap_chain_ne { size_t l_decree_size = dap_chain_datum_decree_get_size(a_decree); dap_chain_datum_t *l_datum = dap_chain_datum_create(DAP_CHAIN_DATUM_DECREE, a_decree, l_decree_size); - dap_chain_t *l_chain = dap_chain_net_get_default_chain_by_chain_type(a_net, CHAIN_TYPE_DECREE); - if (!l_chain) - l_chain = dap_chain_net_get_chain_by_chain_type(a_net, CHAIN_TYPE_DECREE); + dap_chain_t *l_chain = dap_chain_net_get_chain_by_chain_type(a_net, CHAIN_TYPE_DECREE); if (!l_chain) { log_it(L_ERROR, "No chain supported decree datum type"); return NULL; @@ -1457,9 +1439,7 @@ static dap_chain_datum_decree_t *s_stake_decree_invalidate(dap_chain_net_t *a_ne 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->pub.id; - dap_chain_t *l_chain = dap_chain_net_get_default_chain_by_chain_type(a_net, CHAIN_TYPE_ANCHOR); - if (!l_chain) - l_chain = dap_chain_net_get_chain_by_chain_type(a_net, CHAIN_TYPE_ANCHOR); + dap_chain_t *l_chain = dap_chain_net_get_chain_by_chain_type(a_net, CHAIN_TYPE_ANCHOR); if (!l_chain) { log_it(L_ERROR, "No chain supported anchor datum type"); DAP_DEL_Z(l_decree); @@ -2225,8 +2205,9 @@ static int s_cli_srv_stake_delegate(int a_argc, char **a_argv, int a_arg_index, l_pkey = dap_pkey_get_from_str(l_pkey_full_str); } else { dap_hash_fast_t l_pkey_hash = {}; - if (!dap_chain_hash_fast_from_str(l_pkey_str, &l_pkey_hash)) - l_pkey = dap_ledger_find_pkey_by_hash(l_net->pub.ledger, &l_pkey_hash); + if (!dap_chain_hash_fast_from_str(l_pkey_str, &l_pkey_hash)) { + l_pkey = dap_chain_cs_blocks_get_pkey_by_hash(l_net, &l_pkey_hash); + } } if (!l_pkey) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_INVALID_PKEY_ERR, "Invalid pkey string format, can't get pkey_full"); @@ -2236,12 +2217,14 @@ static int s_cli_srv_stake_delegate(int a_argc, char **a_argv, int a_arg_index, if (l_pkey->header.type.type != dap_pkey_type_from_sign_type(l_type).type) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_INVALID_PKEY_ERR, "pkey and sign types is different"); dap_enc_key_delete(l_enc_key); + DAP_DELETE(l_pkey); return DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_INVALID_PKEY_ERR; } dap_chain_hash_fast_t l_hash_public_key = {0}; if (!dap_pkey_get_hash(l_pkey, &l_hash_public_key)) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_INVALID_PKEY_ERR, "Invalid pkey hash format"); dap_enc_key_delete(l_enc_key); + DAP_DELETE(l_pkey); return DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_INVALID_PKEY_ERR; } dap_chain_addr_fill(&l_signing_addr, l_type, &l_hash_public_key, l_net->pub.id); @@ -2252,6 +2235,7 @@ static int s_cli_srv_stake_delegate(int a_argc, char **a_argv, int a_arg_index, if (dap_chain_node_addr_from_str(&l_node_addr, l_node_addr_str)) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_UNRECOGNIZED_ADDR_ERR, "Unrecognized node addr %s", l_node_addr_str); dap_enc_key_delete(l_enc_key); + DAP_DELETE(l_pkey); return DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_UNRECOGNIZED_ADDR_ERR; } } @@ -2260,25 +2244,27 @@ static int s_cli_srv_stake_delegate(int a_argc, char **a_argv, int a_arg_index, if (!l_order) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_NO_ORDER_ERR, "Specified order not found"); dap_enc_key_delete(l_enc_key); + DAP_DELETE(l_pkey); return DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_NO_ORDER_ERR; } if (l_order->direction == SERV_DIR_BUY) { // Staker order if (!l_cert_str) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_PARAM_ERR, "Command 'delegate' requires parameter -cert with this order type"); dap_enc_key_delete(l_enc_key); + DAP_DELETE(l_pkey); return DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_PARAM_ERR; } if (l_order->ext_size != 0) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_ORDER_SIZE_ERR, "Specified order has invalid size"); dap_enc_key_delete(l_enc_key); - DAP_DELETE(l_order); + DAP_DEL_MULTY(l_order, l_pkey); return DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_ORDER_SIZE_ERR; } l_prev_tx = dap_ledger_tx_find_by_hash(l_net->pub.ledger, &l_order->tx_cond_hash); if (!l_prev_tx) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_NO_TX_IN_LEDGER_ERR, "The order's conditional transaction not found in ledger"); dap_enc_key_delete(l_enc_key); - DAP_DELETE(l_order); + DAP_DEL_MULTY(l_order, l_pkey); return DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_NO_TX_IN_LEDGER_ERR; } int l_out_num = 0; @@ -2286,13 +2272,13 @@ static int s_cli_srv_stake_delegate(int a_argc, char **a_argv, int a_arg_index, if (!l_cond) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_INVALID_COND_TX_TYPE_ERR, "The order's conditional transaction has invalid type"); dap_enc_key_delete(l_enc_key); - DAP_DELETE(l_order); + DAP_DEL_MULTY(l_order, l_pkey); return DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_INVALID_COND_TX_TYPE_ERR; } if (dap_ledger_tx_hash_is_used_out_item(l_net->pub.ledger, &l_order->tx_cond_hash, l_out_num, NULL)) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_TX_ALREADY_SENT_ERR, "The order's conditional transaction is already spent"); dap_enc_key_delete(l_enc_key); - DAP_DELETE(l_order); + DAP_DEL_MULTY(l_order, l_pkey); return DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_TX_ALREADY_SENT_ERR; } char l_delegated_ticker[DAP_CHAIN_TICKER_SIZE_MAX]; @@ -2305,20 +2291,20 @@ static int s_cli_srv_stake_delegate(int a_argc, char **a_argv, int a_arg_index, if (l_cond->tsd_size != dap_chain_datum_tx_item_out_cond_create_srv_stake_get_tsd_size(true, 0)) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_INVALID_COND_TX_FORMAT_ERR, "The order's conditional transaction has invalid format"); dap_enc_key_delete(l_enc_key); - DAP_DELETE(l_order); + DAP_DEL_MULTY(l_order, l_pkey); return DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_INVALID_COND_TX_FORMAT_ERR; } if (compare256(l_cond->header.value, l_order->price)) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_COND_TX_DIF_VALUE_ERR, "The order's conditional transaction has different value"); dap_enc_key_delete(l_enc_key); - DAP_DELETE(l_order); + DAP_DEL_MULTY(l_order, l_pkey); return DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_COND_TX_DIF_VALUE_ERR; } if (!dap_chain_addr_is_blank(&l_cond->subtype.srv_stake_pos_delegate.signing_addr) || l_cond->subtype.srv_stake_pos_delegate.signer_node_addr.uint64) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_COND_TX_NO_ADDR_OR_KEY_ERR, "The order's conditional transaction gas not blank address or key"); dap_enc_key_delete(l_enc_key); - DAP_DELETE(l_order); + DAP_DEL_MULTY(l_order, l_pkey); return DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_COND_TX_NO_ADDR_OR_KEY_ERR; } l_value = l_order->price; @@ -2393,7 +2379,7 @@ static int s_cli_srv_stake_delegate(int a_argc, char **a_argv, int a_arg_index, if (dap_strcmp(l_order->price_ticker, l_delegated_ticker_str)) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_INVALID_ORDER_ERR, "Specified order is invalid"); dap_enc_key_delete(l_enc_key); - DAP_DELETE(l_order); + DAP_DEL_MULTY(l_order, l_pkey); return DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_INVALID_ORDER_ERR; } l_node_addr = l_order->node_addr; @@ -2499,15 +2485,13 @@ static int s_cli_srv_stake_pkey_show(int a_argc, char **a_argv, int a_arg_index, // search in curren dap_chain_net_srv_stake_item_t *l_stake = NULL; HASH_FIND(hh, l_srv_stake->itemlist, &l_pkey_hash, sizeof(dap_hash_fast_t), l_stake); - dap_pkey_t *l_pkey = l_stake ? l_stake->pkey : NULL; - if (!l_pkey) { - l_pkey = dap_ledger_find_pkey_by_hash(l_net->pub.ledger, &l_pkey_hash); - } + dap_pkey_t *l_pkey = (l_stake && l_stake->pkey) ? DAP_DUP_SIZE(l_stake->pkey, dap_pkey_get_size(l_stake->pkey)) : dap_chain_cs_blocks_get_pkey_by_hash(l_net, &l_pkey_hash); if (!l_pkey) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_DELEGATE_INVALID_PKEY_ERR, "pkey not finded"); return -25; } const char *l_pkey_str = dap_pkey_to_str(l_pkey, a_hash_out_type); + DAP_DELETE(l_pkey); json_object* l_json_obj_pkey = json_object_new_object(); json_object_object_add(l_json_obj_pkey, "hash", json_object_new_string(l_pkey_hash_str)); json_object_object_add(l_json_obj_pkey, "pkey", json_object_new_string(l_pkey_str)); @@ -3523,6 +3507,13 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, void **a_str_reply) json_object_object_add(l_json_obj_tx, "node_address", json_object_new_string(l_node_address_text_block)); json_object_object_add(l_json_obj_tx, "value_coins", json_object_new_string(l_coins)); json_object_object_add(l_json_obj_tx, "value_datoshi", json_object_new_string(l_balance)); + dap_hash_fast_t l_owner_hash = dap_ledger_get_first_chain_tx_hash(l_net->pub.ledger, l_datum_tx, DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_POS_DELEGATE); + dap_chain_datum_tx_t *l_owner_tx = dap_hash_fast_is_blank(&l_owner_hash) ? l_datum_tx + : dap_ledger_tx_find_datum_by_hash(l_net->pub.ledger, &l_owner_hash, NULL, false); + assert(l_owner_tx); + dap_sign_t *l_owner_sign = dap_chain_datum_tx_get_sign(l_owner_tx, 0); + dap_chain_addr_t l_owner_addr; dap_chain_addr_fill_from_sign(&l_owner_addr, l_owner_sign, l_net->pub.id); + json_object_object_add(l_json_obj_tx, "owner_addr", json_object_new_string(dap_chain_addr_to_str_static(&l_owner_addr))); json_object_array_add(l_json_arr_tx, l_json_obj_tx); DAP_DELETE(l_node_address_text_block); } @@ -3550,9 +3541,7 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, void **a_str_reply) dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_NET_ERR, "Network %s not found", l_net_str); return DAP_CHAIN_NODE_CLI_SRV_STAKE_NET_ERR; } - dap_chain_t *l_chain = dap_chain_net_get_default_chain_by_chain_type(l_net, CHAIN_TYPE_ANCHOR); - if (!l_chain) - l_chain = dap_chain_net_get_chain_by_chain_type(l_net, CHAIN_TYPE_ANCHOR); + dap_chain_t *l_chain = dap_chain_net_get_chain_by_chain_type(l_net, CHAIN_TYPE_ANCHOR); if (!l_chain) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_ANCHOR_NOT_SUPPORT_ERR, "No chain supported anchor datum type"); return DAP_CHAIN_NODE_CLI_SRV_STAKE_ANCHOR_NOT_SUPPORT_ERR; @@ -3614,9 +3603,7 @@ static int s_cli_srv_stake(int a_argc, char **a_argv, void **a_str_reply) dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_NET_ERR, "Network %s not found", l_net_str); return DAP_CHAIN_NODE_CLI_SRV_STAKE_NET_ERR; } - dap_chain_t *l_chain = dap_chain_net_get_default_chain_by_chain_type(l_net, CHAIN_TYPE_ANCHOR); - if (!l_chain) - l_chain = dap_chain_net_get_chain_by_chain_type(l_net, CHAIN_TYPE_ANCHOR); + dap_chain_t *l_chain = dap_chain_net_get_chain_by_chain_type(l_net, CHAIN_TYPE_ANCHOR); if (!l_chain) { dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_SRV_STAKE_ANCHOR_NOT_SUPPORT_ERR, "No chain supported anchor datum type"); return DAP_CHAIN_NODE_CLI_SRV_STAKE_ANCHOR_NOT_SUPPORT_ERR; @@ -4149,3 +4136,17 @@ size_t dap_chain_net_srv_stake_get_total_keys(dap_chain_net_id_t a_net_id, size_ } return l_total_count; } + +/** + * @brief search pkey by hash in delegate table + * @param a_net_id net id to search + * @param a_hash hash to search + * @return pointer to pkey, NULL if error + */ +dap_pkey_t *dap_chain_net_srv_stake_get_pkey_by_hash(dap_chain_net_id_t a_net_id, dap_hash_fast_t *a_hash) +{ + dap_chain_net_srv_stake_t*l_srv_stake = s_srv_stake_by_net_id(a_net_id); + dap_chain_net_srv_stake_item_t *l_stake = NULL; + HASH_FIND(hh, l_srv_stake->itemlist, a_hash, sizeof(dap_hash_fast_t), l_stake); + return l_stake ? l_stake->pkey : NULL; +} diff --git a/modules/service/stake/include/dap_chain_net_srv_stake_pos_delegate.h b/modules/service/stake/include/dap_chain_net_srv_stake_pos_delegate.h index 3fb0c4e77b259aef36b48a81cb8b34e41756793f..0598e89482123ba586fa31c9395cea25cac9f86b 100644 --- a/modules/service/stake/include/dap_chain_net_srv_stake_pos_delegate.h +++ b/modules/service/stake/include/dap_chain_net_srv_stake_pos_delegate.h @@ -104,3 +104,4 @@ int dap_chain_net_srv_stake_mark_validator_active(dap_chain_addr_t *a_signing_ad dap_chain_net_srv_stake_item_t *dap_chain_net_srv_stake_check_pkey_hash(dap_chain_net_id_t a_net_id, dap_hash_fast_t *a_pkey_hash); uint256_t dap_chain_net_srv_stake_get_total_weight(dap_chain_net_id_t a_net_id, uint256_t *a_locked_weight); size_t dap_chain_net_srv_stake_get_total_keys(dap_chain_net_id_t a_net_id, size_t *a_in_active_count); +dap_pkey_t *dap_chain_net_srv_stake_get_pkey_by_hash(dap_chain_net_id_t a_net_id, dap_hash_fast_t *a_hash); diff --git a/modules/service/voting/dap_chain_net_srv_voting.c b/modules/service/voting/dap_chain_net_srv_voting.c index c74cf6fd33c9b20d697e37c1b38737aa0dad1006..fb8f37c799ba841765a9fc03eb37fc23757f4fda 100644 --- a/modules/service/voting/dap_chain_net_srv_voting.c +++ b/modules/service/voting/dap_chain_net_srv_voting.c @@ -50,6 +50,7 @@ typedef struct dap_chain_net_voting_params_offsets{ uint64_t votes_max_count; bool delegate_key_required; bool vote_changing_allowed; + char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX]; } dap_chain_net_voting_params_offsets_t; typedef struct dap_chain_net_vote_option { @@ -60,7 +61,7 @@ typedef struct dap_chain_net_vote_option { typedef struct dap_chain_net_voting_cond_outs { dap_chain_hash_fast_t tx_hash; int out_idx; - + dap_hash_fast_t pkey_hash; UT_hash_handle hh; } dap_chain_net_voting_cond_outs_t; @@ -86,9 +87,10 @@ typedef struct dap_chain_net_votings { static dap_chain_net_votings_t *s_votings; static pthread_rwlock_t s_votings_rwlock; -static int s_datum_tx_voting_coin_check_cond_out(dap_chain_net_t *a_net, dap_hash_fast_t a_voting_hash, dap_hash_fast_t a_tx_cond_hash, int a_cond_out_idx); +static int s_datum_tx_voting_coin_check_cond_out(dap_chain_net_t *a_net, dap_hash_fast_t a_voting_hash, dap_hash_fast_t a_tx_cond_hash, int a_cond_out_idx, dap_hash_fast_t *a_vote_hash); /// -1 error, 0 - unspent, 1 - spent -static int s_datum_tx_voting_coin_check_spent(dap_chain_net_t *a_net, dap_hash_fast_t a_voting_hash, dap_hash_fast_t a_tx_prev_hash, int a_out_idx, dap_hash_fast_t *a_pkey_hash); +static int s_datum_tx_voting_coin_check_spent(dap_chain_net_t *a_net, dap_hash_fast_t a_voting_hash, + dap_hash_fast_t a_tx_prev_hash, int a_out_idx, dap_hash_fast_t *a_pkey_hash); static int s_datum_tx_voting_verification_callback(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, bool a_apply); static bool s_datum_tx_voting_verification_delete_callback(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in); static int s_cli_voting(int argc, char **argv, void **a_str_reply); @@ -114,15 +116,19 @@ int dap_chain_net_srv_voting_init() { pthread_rwlock_init(&s_votings_rwlock, NULL); dap_chain_ledger_voting_verificator_add(s_datum_tx_voting_verification_callback, s_datum_tx_voting_verification_delete_callback); - dap_cli_server_cmd_add("voting", s_cli_voting, "Voting commands.", "" - "voting create -net <net_name> -question <\"Question_string\"> -options <\"Option0\", \"Option1\" ... \"OptionN\"> [-expire <voting_expire_time_in_RCF822>] [-max_votes_count <Votes_count>] [-delegated_key_required] [-vote_changing_allowed] -fee <value_datoshi> -w <fee_wallet_name>\n" - "voting vote -net <net_name> -hash <voting_hash> -option_idx <option_index> [-cert <delegate_cert_name>] -fee <value_datoshi> -w <fee_wallet_name>\n" - "voting list -net <net_name>\n" - "voting dump -net <net_name> -hash <voting_hash>\n"); - - + dap_cli_cmd_t *l_poll_cmd = dap_cli_server_cmd_add( + "poll", s_cli_voting, "Voting/poll commands", + "poll create -net <net_name> -question <\"Question_string\"> -options <\"Option0\", \"Option1\" ... \"OptionN\"> [-expire <poll_expire_time_in_RCF822>]" + " [-max_votes_count <Votes_count>] [-delegated_key_required] [-vote_changing_allowed] -fee <value_datoshi> -w <fee_wallet_name> [-token <ticker>]\n" + "poll vote -net <net_name> -hash <poll_hash> -option_idx <option_index> [-cert <delegate_cert_name>] -fee <value_datoshi> -w <fee_wallet_name>\n" + "poll list -net <net_name>\n" + "poll dump -net <net_name> -hash <poll_hash>\n" + "Hint:\n" + "\texample value_coins (only natural) 1.0 123.4567\n" + "\texample value_datoshi (only integer) 1 20 0.4321e+4\n"); + dap_cli_server_alias_add(l_poll_cmd, NULL, "voting"); dap_chain_net_srv_uid_t l_uid = { .uint64 = DAP_CHAIN_NET_SRV_VOTING_ID }; - dap_ledger_service_add(l_uid, "voting", s_tag_check_voting); + dap_ledger_service_add(l_uid, "poll", s_tag_check_voting); return 0; } @@ -146,7 +152,7 @@ uint64_t* dap_chain_net_voting_get_result(dap_ledger_t* a_ledger, dap_chain_hash pthread_rwlock_unlock(&s_votings_rwlock); if(!l_voting || l_voting->net_id.uint64 != a_ledger->net->pub.id.uint64){ char* l_hash_str = dap_hash_fast_to_str_new(a_voting_hash); - log_it(L_ERROR, "Can't find voting with hash %s in net %s", l_hash_str, a_ledger->net->pub.name); + log_it(L_ERROR, "Can't find poll with hash %s in net %s", l_hash_str, a_ledger->net->pub.name); DAP_DEL_Z(l_hash_str); return NULL; } @@ -172,7 +178,6 @@ uint64_t* dap_chain_net_voting_get_result(dap_ledger_t* a_ledger, dap_chain_hash return l_voting_results; } - static int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a_type, dap_chain_datum_tx_t *a_tx_in, dap_hash_fast_t *a_tx_hash, bool a_apply) { if (!a_apply) { @@ -181,7 +186,7 @@ static int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t HASH_FIND(hh, s_votings, a_tx_hash, sizeof(dap_hash_fast_t), l_voting); pthread_rwlock_unlock(&s_votings_rwlock); if (l_voting && l_voting->net_id.uint64 == a_ledger->net->pub.id.uint64) { - log_it(L_DEBUG, "Voting with hash %s is already presents in net %s", dap_hash_fast_to_str_static(a_tx_hash), a_ledger->net->pub.name); + log_it(L_DEBUG, "Poll with hash %s is already presents in net %s", dap_hash_fast_to_str_static(a_tx_hash), a_ledger->net->pub.name); return -1; } @@ -203,7 +208,7 @@ static int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t dap_list_free(l_tsd_list); if (!l_question_len || !l_options_count) { - log_it(L_WARNING, "Voting with hash %s contain no question or answer options", dap_hash_fast_to_str_static(a_tx_hash)); + log_it(L_WARNING, "Poll with hash %s contain no question or answer options", dap_hash_fast_to_str_static(a_tx_hash)); return -2; } @@ -218,14 +223,29 @@ static int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t dap_list_t* l_tsd_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_TSD, NULL); for (dap_list_t *it = l_tsd_list; it; it = it->next) { - dap_tsd_t* l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t*)it->data)->tsd; + dap_chain_tx_tsd_t *l_tx_tsd = it->data; + dap_tsd_t *l_tsd = (dap_tsd_t *)l_tx_tsd->tsd; + if (l_tx_tsd->header.size < sizeof(dap_tsd_t) || + l_tx_tsd->header.size != dap_tsd_size(l_tsd)) { + log_it(L_WARNING, "Incorrect size %" DAP_UINT64_FORMAT_U " of TX_TSD item for poll %s", + l_tx_tsd->header.size, dap_hash_fast_to_str_static(a_tx_hash)); + return -DAP_LEDGER_CHECK_INVALID_SIZE; + } dap_chain_net_vote_option_t *l_vote_option = NULL; switch(l_tsd->type){ case VOTING_TSD_TYPE_QUESTION: + if (!l_tsd->size) { + log_it(L_WARNING, "Incorrect size %u of TSD section QUESTION for poll %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); + return -DAP_LEDGER_CHECK_INVALID_SIZE; + } l_item->voting_params.voting_question_offset = (size_t)(l_tsd->data - (byte_t*)l_item->voting_params.voting_tx); l_item->voting_params.voting_question_length = l_tsd->size; break; case VOTING_TSD_TYPE_ANSWER: + if (!l_tsd->size) { + log_it(L_WARNING, "Incorrect size %u of TSD section ANSWER for poll %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); + return -DAP_LEDGER_CHECK_INVALID_SIZE; + } l_vote_option = DAP_NEW_Z(dap_chain_net_vote_option_t); l_vote_option->vote_option_offset = (size_t)(l_tsd->data - (byte_t*)l_item->voting_params.voting_tx); l_vote_option->vote_option_length = l_tsd->size; @@ -233,41 +253,50 @@ static int s_voting_verificator(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t break; case VOTING_TSD_TYPE_EXPIRE: if (l_tsd->size != sizeof(dap_time_t)) { - log_it(L_WARNING, "Incorrect size %u of TSD section EXPIRE vot voting %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); + log_it(L_WARNING, "Incorrect size %u of TSD section EXPIRE for poll %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); return -DAP_LEDGER_CHECK_INVALID_SIZE; } l_item->voting_params.voting_expire = *(dap_time_t *)l_tsd->data; break; case VOTING_TSD_TYPE_MAX_VOTES_COUNT: if (l_tsd->size != sizeof(uint64_t)) { - log_it(L_WARNING, "Incorrect size %u of TSD section MAX_VOTES_COUNT vot voting %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); + log_it(L_WARNING, "Incorrect size %u of TSD section MAX_VOTES_COUNT for poll %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); return -DAP_LEDGER_CHECK_INVALID_SIZE; } l_item->voting_params.votes_max_count = *(uint64_t *)l_tsd->data; break; case VOTING_TSD_TYPE_DELEGATED_KEY_REQUIRED: if (l_tsd->size != sizeof(byte_t)) { - log_it(L_WARNING, "Incorrect size %u of TSD section DELEGATED_KEY_REQUIRED vot voting %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); + log_it(L_WARNING, "Incorrect size %u of TSD section DELEGATED_KEY_REQUIRED for poll %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); return -DAP_LEDGER_CHECK_INVALID_SIZE; } l_item->voting_params.delegate_key_required = *(byte_t *)l_tsd->data; break; case VOTING_TSD_TYPE_VOTE_CHANGING_ALLOWED: if (l_tsd->size != sizeof(byte_t)) { - log_it(L_WARNING, "Incorrect size %u of TSD section VOTE_CHANGING_ALLOWED vot voting %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); + log_it(L_WARNING, "Incorrect size %u of TSD section VOTE_CHANGING_ALLOWED for poll %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); return -DAP_LEDGER_CHECK_INVALID_SIZE; } l_item->voting_params.vote_changing_allowed = *(byte_t *)l_tsd->data; break; + case VOTING_TSD_TYPE_TOKEN: + if (!l_tsd->size || l_tsd->size >= DAP_CHAIN_TICKER_SIZE_MAX) { + log_it(L_WARNING, "Incorrect size %u of TSD section TOKEN for poll %s", l_tsd->size, dap_hash_fast_to_str_static(a_tx_hash)); + return -DAP_LEDGER_CHECK_INVALID_SIZE; + } + strcpy(l_item->voting_params.token_ticker, (char *)l_tsd->data); default: break; } } dap_list_free(l_tsd_list); + if (!*l_item->voting_params.token_ticker) + strcpy(l_item->voting_params.token_ticker, a_ledger->net->pub.native_ticker); pthread_rwlock_wrlock(&s_votings_rwlock); HASH_ADD(hh, s_votings, voting_hash, sizeof(dap_hash_fast_t), l_item); pthread_rwlock_unlock(&s_votings_rwlock); + log_it(L_NOTICE, "Poll with hash %s succefully added to ledger", dap_hash_fast_to_str_static(a_tx_hash)); return DAP_LEDGER_CHECK_OK; } @@ -284,88 +313,62 @@ static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a HASH_FIND(hh, s_votings, &l_vote_tx_item->voting_hash, sizeof(dap_hash_fast_t), l_voting); pthread_rwlock_unlock(&s_votings_rwlock); if (!l_voting || l_voting->net_id.uint64 != a_ledger->net->pub.id.uint64) { - log_it(L_ERROR, "Can't find voting with hash %s in net %s", + log_it(L_ERROR, "Can't find poll with hash %s in net %s", dap_chain_hash_fast_to_str_static(&l_vote_tx_item->voting_hash), a_ledger->net->pub.name); return -5; } - dap_hash_fast_t pkey_hash = {}; - int l_item_cnt = 0; - dap_list_t *l_signs_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_SIG, &l_item_cnt); - - if (!l_signs_list) { - log_it(L_WARNING, "Can't get signs from tx %s", dap_chain_hash_fast_to_str_static(a_tx_hash)); - return -9; + // Get last sign item from transaction + dap_hash_fast_t l_pkey_hash = {}; + dap_sign_t *l_pkey_sign = NULL; + uint8_t *l_tx_item = NULL; size_t l_size; int i, l_sign_num = 0; + TX_ITEM_ITER_TX_TYPE(l_tx_item, TX_ITEM_TYPE_SIG, l_size, i, a_tx_in) { + l_pkey_sign = dap_chain_datum_tx_item_sign_get_sig((dap_chain_tx_sig_t *)l_tx_item); + l_sign_num++; + } + dap_sign_get_pkey_hash(l_pkey_sign, &l_pkey_hash); + if (--l_sign_num && dap_chain_datum_tx_verify_sign(a_tx_in, l_sign_num)) { + log_it(L_WARNING, "Last vote tx %s sign verification failed", dap_chain_hash_fast_to_str_static(a_tx_hash)); + return -22; } - dap_chain_tx_sig_t *l_vote_sig = (dap_chain_tx_sig_t *)(dap_list_last(l_signs_list)->data); - dap_sign_get_pkey_hash((dap_sign_t*)l_vote_sig->sig, &pkey_hash); - dap_list_free(l_signs_list); - if (!a_apply) { - if (l_vote_tx_item->answer_idx > dap_list_length(l_voting->voting_params.option_offsets_list)) { - log_it(L_WARNING, "Invalid vote option index %" DAP_UINT64_FORMAT_U " for vote tx %s", - l_vote_tx_item->answer_idx, dap_chain_hash_fast_to_str_static(a_tx_hash)); - return -6; - } - if (l_voting->voting_params.votes_max_count && dap_list_length(l_voting->votes) >= l_voting->voting_params.votes_max_count){ - log_it(L_WARNING, "The required number of votes has been collected for voting %s", dap_chain_hash_fast_to_str_static(&l_voting->voting_hash)); - return -7; - } - if (l_voting->voting_params.voting_expire && l_voting->voting_params.voting_expire <= a_tx_in->header.ts_created) { - log_it(L_WARNING, "The voting %s has been expired", dap_chain_hash_fast_to_str_static(&l_voting->voting_hash)); - return -8; - } - - if (l_voting->voting_params.delegate_key_required && - !dap_chain_net_srv_stake_check_pkey_hash(a_ledger->net->pub.id, &pkey_hash)){ - log_it(L_WARNING, "Voting %s required a delegated key", dap_chain_hash_fast_to_str_static(&l_voting->voting_hash)); - return -10; - } - - for (dap_list_t *it = l_voting->votes; it; it = it->next) { - if (dap_hash_fast_compare(&((dap_chain_net_vote_t *)it->data)->pkey_hash, &pkey_hash)) { - dap_hash_fast_t *l_vote_hash = &((dap_chain_net_vote_t *)it->data)->vote_hash; - if (!l_voting->voting_params.vote_changing_allowed) { - char l_vote_hash_str[DAP_HASH_FAST_STR_SIZE]; - dap_hash_fast_to_str(l_vote_hash, l_vote_hash_str, DAP_HASH_FAST_STR_SIZE); - log_it(L_WARNING, "The voting %s don't allow change your vote %s", - dap_hash_fast_to_str_static(&l_voting->voting_hash), l_vote_hash_str); - return -11; - } - break; - } - } + if (l_vote_tx_item->answer_idx > dap_list_length(l_voting->voting_params.option_offsets_list)) { + log_it(L_WARNING, "Invalid vote option index %" DAP_UINT64_FORMAT_U " for vote tx %s", + l_vote_tx_item->answer_idx, dap_chain_hash_fast_to_str_static(a_tx_hash)); + return -6; + } + if (l_voting->voting_params.votes_max_count && dap_list_length(l_voting->votes) >= l_voting->voting_params.votes_max_count){ + log_it(L_WARNING, "The required number of votes has been collected for poll %s", dap_chain_hash_fast_to_str_static(&l_voting->voting_hash)); + return -7; + } + if (l_voting->voting_params.voting_expire && l_voting->voting_params.voting_expire <= a_tx_in->header.ts_created) { + log_it(L_WARNING, "The voting %s has been expired", dap_chain_hash_fast_to_str_static(&l_voting->voting_hash)); + return -8; } - uint256_t l_weight = {}; + if (l_voting->voting_params.delegate_key_required && + !dap_chain_net_srv_stake_check_pkey_hash(a_ledger->net->pub.id, &l_pkey_hash)){ + log_it(L_WARNING, "Poll %s required a delegated key", dap_chain_hash_fast_to_str_static(&l_voting->voting_hash)); + return -10; + } - // check out conds - dap_list_t *l_tsd_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_TSD, NULL); - for (dap_list_t *it = l_tsd_list; it; it = it->next) { - dap_tsd_t *l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t*)it->data)->tsd; - dap_hash_fast_t l_hash = ((dap_chain_tx_voting_tx_cond_t*)l_tsd->data)->tx_hash; - int l_out_idx = ((dap_chain_tx_voting_tx_cond_t*)l_tsd->data)->out_idx; - if (l_tsd->type == VOTING_TSD_TYPE_VOTE_TX_COND) { - if (s_datum_tx_voting_coin_check_cond_out(a_ledger->net, l_vote_tx_item->voting_hash, l_hash, l_out_idx)) - continue; - dap_chain_datum_tx_t *l_tx_prev_temp = dap_ledger_tx_find_by_hash(a_ledger, &l_hash); - dap_chain_tx_out_cond_t *l_prev_out = (dap_chain_tx_out_cond_t*)dap_chain_datum_tx_item_get(l_tx_prev_temp, &l_out_idx, NULL, TX_ITEM_TYPE_OUT_COND, NULL); - if (!l_prev_out || l_prev_out->header.subtype != DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK) - continue; - if (SUM_256_256(l_weight, l_prev_out->header.value, &l_weight)) { - log_it(L_WARNING, "Integer overflow while parsing vote tx %s", dap_chain_hash_fast_to_str_static(a_tx_hash)); - return -DAP_LEDGER_CHECK_INTEGER_OVERFLOW; + dap_list_t *l_old_vote = NULL; + for (dap_list_t *it = l_voting->votes; it; it = it->next) { + if (dap_hash_fast_compare(&((dap_chain_net_vote_t *)it->data)->pkey_hash, &l_pkey_hash)) { + dap_hash_fast_t *l_vote_hash = &((dap_chain_net_vote_t *)it->data)->vote_hash; + if (!l_voting->voting_params.vote_changing_allowed) { + char l_vote_hash_str[DAP_HASH_FAST_STR_SIZE]; + dap_hash_fast_to_str(l_vote_hash, l_vote_hash_str, DAP_HASH_FAST_STR_SIZE); + log_it(L_WARNING, "The poll %s don't allow change your vote %s", + dap_hash_fast_to_str_static(&l_voting->voting_hash), l_vote_hash_str); + return -11; } - - dap_chain_net_voting_cond_outs_t *l_item = DAP_NEW_Z_RET_VAL_IF_FAIL(dap_chain_net_voting_cond_outs_t, -DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); - l_item->tx_hash = l_hash; - l_item->out_idx = l_out_idx; - pthread_rwlock_wrlock(&l_voting->s_tx_outs_rwlock); - HASH_ADD(hh, l_voting->voting_spent_cond_outs, tx_hash, sizeof(dap_hash_fast_t), l_item); - pthread_rwlock_unlock(&l_voting->s_tx_outs_rwlock); + l_old_vote = it; + break; } } - dap_list_free(l_tsd_list); + + uint256_t l_weight = {}; // check inputs dap_list_t *l_ins_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_IN, NULL); if (!l_ins_list) { @@ -374,17 +377,35 @@ static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a } for (dap_list_t *it = l_ins_list; it; it = it->next) { dap_chain_tx_in_t *l_tx_in = (dap_chain_tx_in_t *)it->data; - if (!s_datum_tx_voting_coin_check_spent(a_ledger->net, l_vote_tx_item->voting_hash, - l_tx_in->header.tx_prev_hash, l_tx_in->header.tx_out_prev_idx, &pkey_hash)) { - dap_chain_datum_tx_t *l_tx_prev_temp = dap_ledger_tx_find_by_hash(a_ledger, &l_tx_in->header.tx_prev_hash); - dap_chain_tx_out_t *l_prev_out_union = (dap_chain_tx_out_t *)dap_chain_datum_tx_out_get_by_out_idx(l_tx_prev_temp, l_tx_in->header.tx_out_prev_idx); - if (!l_prev_out_union) - continue; - if ((l_prev_out_union->header.type == TX_ITEM_TYPE_OUT || l_prev_out_union->header.type == TX_ITEM_TYPE_OUT_EXT) && - SUM_256_256(l_weight, l_prev_out_union->header.value, &l_weight)) { - log_it(L_WARNING, "Integer overflow while parsing vote tx %s", dap_chain_hash_fast_to_str_static(a_tx_hash)); - return -DAP_LEDGER_CHECK_INTEGER_OVERFLOW; - } + dap_chain_datum_tx_t *l_tx_prev_temp = dap_ledger_tx_find_by_hash(a_ledger, &l_tx_in->header.tx_prev_hash); + dap_chain_tx_out_ext_t *l_prev_out_union = (dap_chain_tx_out_ext_t *)dap_chain_datum_tx_out_get_by_out_idx( + l_tx_prev_temp, l_tx_in->header.tx_out_prev_idx); + if (!l_prev_out_union) + return -18; + const char *l_ticker_in = NULL; + switch (l_prev_out_union->header.type) { + case TX_ITEM_TYPE_OUT: + l_ticker_in = dap_ledger_tx_get_token_ticker_by_hash(a_ledger, &l_tx_in->header.tx_prev_hash); + break; + case TX_ITEM_TYPE_OUT_EXT: + l_ticker_in = l_prev_out_union->token; + break; + default: + log_it(L_WARNING, "Unexpected tx item %d in vote tx %s", l_prev_out_union->header.type, dap_hash_fast_to_str_static(a_tx_hash)); + return -19; + } + if (dap_strcmp(l_ticker_in, l_voting->voting_params.token_ticker)) + continue; + if (s_datum_tx_voting_coin_check_spent(a_ledger->net, l_vote_tx_item->voting_hash, + l_tx_in->header.tx_prev_hash, l_tx_in->header.tx_out_prev_idx, + l_old_vote ? &l_pkey_hash : NULL)) { + log_it(L_WARNING, "Coin with out number %u for tx %s is spent for poll %s", l_tx_in->header.tx_out_prev_idx, + dap_hash_fast_to_str_static(a_tx_hash), dap_hash_fast_to_str_static(&l_vote_tx_item->voting_hash)); + return -20; + } + if (SUM_256_256(l_weight, l_prev_out_union->header.value, &l_weight)) { + log_it(L_WARNING, "Integer overflow while parsing vote tx %s", dap_chain_hash_fast_to_str_static(a_tx_hash)); + return -DAP_LEDGER_CHECK_INTEGER_OVERFLOW; } } dap_list_free(l_ins_list); @@ -394,62 +415,76 @@ static int s_vote_verificator(dap_ledger_t *a_ledger, dap_chain_tx_item_type_t a return -13; } + // check out conds + dap_list_t *l_tsd_list = dap_chain_datum_tx_items_get(a_tx_in, TX_ITEM_TYPE_TSD, NULL); + for (dap_list_t *it = l_tsd_list; it; it = it->next) { + dap_tsd_t *l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t*)it->data)->tsd; + dap_hash_fast_t l_hash = ((dap_chain_tx_voting_tx_cond_t*)l_tsd->data)->tx_hash; + int l_out_idx = ((dap_chain_tx_voting_tx_cond_t*)l_tsd->data)->out_idx; + if (l_tsd->type != VOTING_TSD_TYPE_VOTE_TX_COND) + return -14; + dap_chain_datum_tx_t *l_tx_prev_temp = dap_ledger_tx_find_by_hash(a_ledger, &l_hash); + dap_chain_tx_out_cond_t *l_prev_out = (dap_chain_tx_out_cond_t *)dap_chain_datum_tx_out_get_by_out_idx(l_tx_prev_temp, l_out_idx); + if (!l_prev_out || l_prev_out->header.item_type != TX_ITEM_TYPE_OUT_COND || + l_prev_out->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE) + return -16; + if (!dap_ledger_check_condition_owner(a_ledger, &l_hash, l_prev_out->header.subtype, l_out_idx, l_pkey_sign)) + return -17; + if (s_datum_tx_voting_coin_check_cond_out(a_ledger->net, l_vote_tx_item->voting_hash, l_hash, l_out_idx, + l_old_vote ? &l_pkey_hash : NULL)) + return -15; + if (SUM_256_256(l_weight, l_prev_out->header.value, &l_weight)) { + log_it(L_WARNING, "Integer overflow while parsing vote tx %s", dap_chain_hash_fast_to_str_static(a_tx_hash)); + return -DAP_LEDGER_CHECK_INTEGER_OVERFLOW; + } + } + if (a_apply) { + // Mark conditional outs + pthread_rwlock_wrlock(&l_voting->s_tx_outs_rwlock); + if (l_old_vote) { + dap_hash_fast_t *l_vote_hash = &((dap_chain_net_vote_t *)l_old_vote->data)->vote_hash; + dap_chain_net_voting_cond_outs_t *it = NULL, *tmp; + HASH_ITER(hh, l_voting->voting_spent_cond_outs, it, tmp) { + if (!dap_hash_fast_compare(l_vote_hash, &it->pkey_hash)) + continue; + HASH_DEL(l_voting->voting_spent_cond_outs, it); + DAP_DELETE(it); + } + } + for (dap_list_t *it = l_tsd_list; it; it = it->next) { + dap_tsd_t *l_tsd = (dap_tsd_t *)((dap_chain_tx_tsd_t *)it->data)->tsd; + if (l_tsd->type != VOTING_TSD_TYPE_VOTE_TX_COND) + continue; + dap_chain_net_voting_cond_outs_t *l_tx_out = DAP_NEW_Z_RET_VAL_IF_FAIL(dap_chain_net_voting_cond_outs_t, -DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); + l_tx_out->tx_hash = ((dap_chain_tx_voting_tx_cond_t *)l_tsd->data)->tx_hash; + l_tx_out->out_idx = ((dap_chain_tx_voting_tx_cond_t *)l_tsd->data)->out_idx; + l_tx_out->pkey_hash = l_pkey_hash; + HASH_ADD(hh, l_voting->voting_spent_cond_outs, tx_hash, sizeof(dap_hash_fast_t), l_tx_out); + } + pthread_rwlock_unlock(&l_voting->s_tx_outs_rwlock); + dap_chain_net_vote_t *l_vote_item = DAP_NEW_Z_RET_VAL_IF_FAIL(dap_chain_net_vote_t, -DAP_LEDGER_CHECK_NOT_ENOUGH_MEMORY); l_vote_item->vote_hash = *a_tx_hash; - l_vote_item->pkey_hash = pkey_hash; + l_vote_item->pkey_hash = l_pkey_hash; l_vote_item->answer_idx = l_vote_tx_item->answer_idx; l_vote_item->weight = l_weight; - // cycle is safe cause return after link deletion - for (dap_list_t *it = l_voting->votes; it; it = it->next) { - if (dap_hash_fast_compare(&((dap_chain_net_vote_t *)it->data)->pkey_hash, &pkey_hash)){ - if (!l_voting->voting_params.vote_changing_allowed) { - char l_vote_hash_str[DAP_HASH_FAST_STR_SIZE]; - dap_hash_fast_to_str(a_tx_hash, l_vote_hash_str, DAP_HASH_FAST_STR_SIZE); - log_it(L_WARNING, "The voting %s don't allow change your vote %s", - dap_hash_fast_to_str_static(&l_voting->voting_hash), l_vote_hash_str); - DAP_DELETE(l_vote_item); - return -11; - } - dap_hash_fast_t *l_vote_hash = &((dap_chain_net_vote_t *)it->data)->vote_hash; - //delete conditional outputs - dap_chain_datum_tx_t *l_old_tx = dap_ledger_tx_find_by_hash(a_ledger, l_vote_hash); - if (!l_old_tx) { - char l_vote_hash_str[DAP_HASH_FAST_STR_SIZE]; - dap_hash_fast_to_str(l_vote_hash, l_vote_hash_str, DAP_HASH_FAST_STR_SIZE); - log_it(L_ERROR, "Can't find old vote %s of voting %s in ledger", - l_vote_hash_str, dap_hash_fast_to_str_static(&l_voting->voting_hash)); - } - dap_list_t* l_tsd_list = dap_chain_datum_tx_items_get(l_old_tx, TX_ITEM_TYPE_TSD, NULL); - for (dap_list_t *it_tsd = l_tsd_list; it_tsd; it_tsd = it_tsd->next) { - dap_tsd_t* l_tsd = (dap_tsd_t*)((dap_chain_tx_tsd_t*)it_tsd->data)->tsd; - dap_hash_fast_t *l_hash = &((dap_chain_tx_voting_tx_cond_t*)l_tsd->data)->tx_hash; - if (l_tsd->type == VOTING_TSD_TYPE_VOTE_TX_COND) { - dap_chain_net_voting_cond_outs_t *l_tx_outs = NULL; - pthread_rwlock_wrlock(&l_voting->s_tx_outs_rwlock); - HASH_FIND(hh, l_voting->voting_spent_cond_outs, l_hash, sizeof(dap_hash_fast_t), l_tx_outs); - if(l_tx_outs) - HASH_DELETE(hh, l_voting->voting_spent_cond_outs, l_tx_outs); - pthread_rwlock_unlock(&l_voting->s_tx_outs_rwlock); - } - } - dap_list_free(l_tsd_list); - // change vote & move it to the end of list - l_voting->votes = dap_list_remove_link(l_voting->votes, it); - l_voting->votes = dap_list_append(l_voting->votes, l_vote_item); - char l_vote_hash_str[DAP_HASH_FAST_STR_SIZE]; - dap_hash_fast_to_str(&((dap_chain_net_vote_t *)it->data)->vote_hash, l_vote_hash_str, DAP_HASH_FAST_STR_SIZE); - DAP_DELETE(it->data); - log_it(L_INFO, "Vote %s of voting %s has been changed", l_vote_hash_str, dap_hash_fast_to_str_static(&l_voting->voting_hash)); - return DAP_LEDGER_CHECK_OK; - } + if (l_old_vote) { + // change vote & move it to the end of list + const char *l_vote_hash_str = dap_hash_fast_to_str_static(&((dap_chain_net_vote_t *)l_old_vote->data)->vote_hash); + DAP_DELETE(l_old_vote->data); + l_voting->votes = dap_list_delete_link(l_voting->votes, l_old_vote); + log_it(L_NOTICE, "Vote %s of poll %s has been changed", l_vote_hash_str, dap_hash_fast_to_str_static(&l_voting->voting_hash)); + } else { + const char *l_vote_hash_str = dap_hash_fast_to_str_static(a_tx_hash); + log_it(L_NOTICE, "Vote %s of poll %s has been accepted", l_vote_hash_str, dap_hash_fast_to_str_static(&l_voting->voting_hash)); } + l_voting->votes = dap_list_append(l_voting->votes, l_vote_item); - char l_vote_hash_str[DAP_HASH_FAST_STR_SIZE]; - dap_hash_fast_to_str(a_tx_hash, l_vote_hash_str, DAP_HASH_FAST_STR_SIZE); - log_it(L_INFO, "Vote %s of voting %s has been accepted", l_vote_hash_str, dap_hash_fast_to_str_static(&l_voting->voting_hash)); } + dap_list_free(l_tsd_list); + return DAP_LEDGER_CHECK_OK; } @@ -459,7 +494,7 @@ int s_datum_tx_voting_verification_callback(dap_ledger_t *a_ledger, dap_chain_tx return s_voting_verificator(a_ledger, a_type, a_tx_in, a_tx_hash, a_apply); if (a_type == TX_ITEM_TYPE_VOTE) return s_vote_verificator(a_ledger, a_type, a_tx_in, a_tx_hash, a_apply); - log_it(L_ERROR, "Item %d is not supported in votings", a_type); + log_it(L_ERROR, "Item %d is not supported in polls", a_type); return -3; } @@ -474,7 +509,7 @@ static bool s_datum_tx_voting_verification_delete_callback(dap_ledger_t *a_ledge HASH_FIND(hh, s_votings, &l_hash, sizeof(dap_hash_fast_t), l_voting); if(!l_voting){ char* l_hash_str = dap_hash_fast_to_str_new(&l_hash); - log_it(L_ERROR, "Can't find voting with hash %s in net %s", l_hash_str, a_ledger->net->pub.name); + log_it(L_ERROR, "Can't find poll with hash %s in net %s", l_hash_str, a_ledger->net->pub.name); DAP_DEL_Z(l_hash_str); pthread_rwlock_unlock(&s_votings_rwlock); return false; @@ -508,13 +543,13 @@ static bool s_datum_tx_voting_verification_delete_callback(dap_ledger_t *a_ledge return false; } - dap_chain_net_votings_t * l_voting = NULL; + dap_chain_net_votings_t *l_voting = NULL; pthread_rwlock_wrlock(&s_votings_rwlock); HASH_FIND(hh, s_votings, &l_vote_tx_item->voting_hash, sizeof(dap_hash_fast_t), l_voting); pthread_rwlock_unlock(&s_votings_rwlock); if(!l_voting || l_voting->net_id.uint64 != a_ledger->net->pub.id.uint64) { char *l_hash_str = dap_chain_hash_fast_to_str_new(&l_hash); - log_it(L_ERROR, "Can't find voting with hash %s in net %s", l_hash_str, a_ledger->net->pub.name); + log_it(L_ERROR, "Can't find poll with hash %s in net %s", l_hash_str, a_ledger->net->pub.name); DAP_DELETE(l_hash_str); return false; } @@ -620,10 +655,11 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) const char* l_max_votes_count_str = NULL; const char* l_fee_str = NULL; const char* l_wallet_str = NULL; + const char *l_token_str = NULL; dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-question", &l_question_str); if (!l_question_str){ - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_QUESTION_PARAM_MISSING, "Voting requires a question parameter to be valid."); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_QUESTION_PARAM_MISSING, "Poll requires a question parameter to be valid."); return -DAP_CHAIN_NET_VOTE_CREATE_QUESTION_PARAM_MISSING; } @@ -636,7 +672,7 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) dap_list_t *l_options_list = NULL; dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-options", &l_options_list_str); if (!l_options_list_str){ - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_OPTION_PARAM_MISSING, "Voting requires a question parameter to be valid."); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_OPTION_PARAM_MISSING, "Poll requires a question parameter to be valid."); return -DAP_CHAIN_NET_VOTE_CREATE_OPTION_PARAM_MISSING; } // Parse options list @@ -652,21 +688,20 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) return -DAP_CHAIN_NET_VOTE_CREATE_CONTAIN_MAX_OPTIONS; } - dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-expire", &l_voting_expire_str); - dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-max_votes_count", &l_max_votes_count_str); dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-fee", &l_fee_str); if (!l_fee_str){ - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_FEE_PARAM_NOT_VALID, "Voting requires paramete -fee to be valid."); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_FEE_PARAM_NOT_VALID, "Poll requires parameter -fee to be valid."); return -DAP_CHAIN_NET_VOTE_CREATE_FEE_PARAM_NOT_VALID; } uint256_t l_value_fee = dap_chain_balance_scan(l_fee_str); dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-w", &l_wallet_str); if (!l_wallet_str){ - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_WALLET_PARAM_NOT_VALID, "Voting requires parameter -w to be valid."); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_WALLET_PARAM_NOT_VALID, "Poll requires parameter -w to be valid."); return -DAP_CHAIN_NET_VOTE_CREATE_WALLET_PARAM_NOT_VALID; } + dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-expire", &l_voting_expire_str); dap_time_t l_time_expire = 0; if (l_voting_expire_str) l_time_expire = dap_time_from_str_rfc822(l_voting_expire_str); @@ -675,6 +710,8 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) "Wrong time format. -expire parameter must be in format \"Day Month Year HH:MM:SS Timezone\" e.g. \"19 August 2024 22:00:00 +00\""); return -DAP_CHAIN_NET_VOTE_CREATE_WRONG_TIME_FORMAT; } + + dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-max_votes_count", &l_max_votes_count_str); uint64_t l_max_count = 0; if (l_max_votes_count_str) l_max_count = strtoul(l_max_votes_count_str, NULL, 10); @@ -688,8 +725,26 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) return -DAP_CHAIN_NET_VOTE_CREATE_WALLET_DOES_NOT_EXIST; } + + dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-token", &l_token_str); + if (l_token_str && !dap_ledger_token_ticker_check(l_net->pub.ledger, l_token_str)) { + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_WRONG_TOKEN, "Token %s does not exist", l_token_str); + return -DAP_CHAIN_NET_VOTE_CREATE_WRONG_TOKEN; + } + char *l_hash_ret = NULL; - int res = dap_chain_net_vote_create(l_question_str, l_options_list, l_time_expire, l_max_count, l_value_fee, l_is_delegated_key, l_is_vote_changing_allowed, l_wallet_fee, l_net, l_hash_out_type, &l_hash_ret); + int res = dap_chain_net_vote_create(l_question_str, + l_options_list, + l_time_expire, + l_max_count, + l_value_fee, + l_is_delegated_key, + l_is_vote_changing_allowed, + l_wallet_fee, + l_net, + l_token_str, + l_hash_out_type, + &l_hash_ret); dap_list_free(l_options_list); dap_chain_wallet_close(l_wallet_fee); @@ -707,7 +762,7 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) return DAP_CHAIN_NET_VOTE_CREATE_LENGTH_QUESTION_OVERSIZE_MAX; } break; case DAP_CHAIN_NET_VOTE_CREATE_COUNT_OPTION_OVERSIZE_MAX: { - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_COUNT_OPTION_OVERSIZE_MAX, "The voting can contain no more than %d options", + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_COUNT_OPTION_OVERSIZE_MAX, "The poll can contain no more than %d options", DAP_CHAIN_DATUM_TX_VOTING_OPTION_MAX_COUNT); return DAP_CHAIN_NET_VOTE_CREATE_COUNT_OPTION_OVERSIZE_MAX; } break; @@ -716,7 +771,7 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) return DAP_CHAIN_NET_VOTE_CREATE_FEE_IS_ZERO; } break; case DAP_CHAIN_NET_VOTE_CREATE_SOURCE_ADDRESS_IS_INVALID: { - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_SOURCE_ADDRESS_IS_INVALID, "source address is invalid"); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_SOURCE_ADDRESS_IS_INVALID, "Source address is invalid"); return DAP_CHAIN_NET_VOTE_CREATE_SOURCE_ADDRESS_IS_INVALID; } break; case DAP_CHAIN_NET_VOTE_CREATE_NOT_ENOUGH_FUNDS_TO_TRANSFER: { @@ -729,11 +784,11 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) return DAP_CHAIN_NET_VOTE_CREATE_MAX_COUNT_OPTION_EXCEEDED; } break; case DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_OPTION_TSD_ITEM: { - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_OPTION_TSD_ITEM, "Can't create voting with expired time"); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_OPTION_TSD_ITEM, "Can't create poll with expired time"); return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_OPTION_TSD_ITEM; } break; case DAP_CHAIN_NET_VOTE_CREATE_INPUT_TIME_MORE_CURRENT_TIME: { - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_INPUT_TIME_MORE_CURRENT_TIME, "Can't create voting with expired time"); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_CREATE_INPUT_TIME_MORE_CURRENT_TIME, "Can't create poll with expired time"); return DAP_CHAIN_NET_VOTE_CREATE_INPUT_TIME_MORE_CURRENT_TIME; } break; case DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_CREATE_TSD_EXPIRE_TIME: { @@ -780,10 +835,11 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) } dap_hash_fast_t l_voting_hash = {}; - dap_chain_hash_fast_from_str(l_hash_str, &l_voting_hash); - + if (dap_chain_hash_fast_from_str(l_hash_str, &l_voting_hash)) { + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_HASH_INVALID, "Hash string is not recognozed as hex of base58 hash"); + return -DAP_CHAIN_NET_VOTE_VOTING_HASH_INVALID; + } - dap_chain_hash_fast_t l_pkey_hash; dap_cli_server_cmd_find_option_val(a_argv, arg_index, a_argc, "-cert", &l_cert_name); dap_cert_t * l_cert = dap_cert_find_by_name(l_cert_name); if (l_cert_name){ @@ -817,8 +873,8 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) } const char *c_wallets_path = dap_chain_wallet_get_path(g_config); - dap_chain_wallet_t *l_wallet_fee = dap_chain_wallet_open(l_wallet_str, c_wallets_path,NULL); - if (!l_wallet_fee) { + dap_chain_wallet_t *l_wallet = dap_chain_wallet_open(l_wallet_str, c_wallets_path,NULL); + if (!l_wallet) { dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_WALLET_DOES_NOT_EXIST, "Wallet %s does not exist", l_wallet_str); return -DAP_CHAIN_NET_VOTE_VOTING_WALLET_DOES_NOT_EXIST; } @@ -827,9 +883,9 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) char *l_hash_tx; - int res = dap_chain_net_vote_voting(l_cert, l_value_fee, l_wallet_fee, l_voting_hash, l_option_idx_count, + int res = dap_chain_net_vote_voting(l_cert, l_value_fee, l_wallet, l_voting_hash, l_option_idx_count, l_net, l_hash_out_type, &l_hash_tx); - dap_chain_wallet_close(l_wallet_fee); + dap_chain_wallet_close(l_wallet); switch (res) { case DAP_CHAIN_NET_VOTE_VOTING_OK: { @@ -840,14 +896,14 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) return DAP_CHAIN_NET_VOTE_CREATE_OK; } break; case DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_FIND_VOTE: { - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_FIND_VOTE, "Can't find voting with hash %s", l_hash_str); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_FIND_VOTE, "Can't find poll with hash %s", l_hash_str); } break; case DAP_CHAIN_NET_VOTE_VOTING_THIS_VOTING_HAVE_MAX_VALUE_VOTES: { dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_THIS_VOTING_HAVE_MAX_VALUE_VOTES, - "This voting already received the required number of votes."); + "This poll already received the required number of votes."); } break; case DAP_CHAIN_NET_VOTE_VOTING_ALREADY_EXPIRED: { - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_ALREADY_EXPIRED, "This voting already expired."); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_ALREADY_EXPIRED, "This poll is already expired."); } break; case DAP_CHAIN_NET_VOTE_VOTING_NO_KEY_FOUND_IN_CERT: { dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_NO_KEY_FOUND_IN_CERT, @@ -855,18 +911,13 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) } break; case DAP_CHAIN_NET_VOTE_VOTING_CERT_REQUIRED: { dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_CERT_REQUIRED, - "This voting required a delegated key. Parameter -cert must contain a valid certificate name"); - } break; - case DAP_CHAIN_NET_VOTE_VOTING_NO_PUBLIC_KEY_IN_CERT: { - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_NO_PUBLIC_KEY_IN_CERT, - "Can't serialize public key of certificate \"%s\"", - l_cert_name); + "This poll required a delegated key. Parameter -cert must contain a valid certificate name"); } break; case DAP_CHAIN_NET_VOTE_VOTING_KEY_IS_NOT_DELEGATED: { dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_KEY_IS_NOT_DELEGATED, "Your key is not delegated."); } break; case DAP_CHAIN_NET_VOTE_VOTING_DOES_NOT_ALLOW_CHANGE_YOUR_VOTE: { - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_DOES_NOT_ALLOW_CHANGE_YOUR_VOTE, "The voting doesn't allow change your vote."); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_DOES_NOT_ALLOW_CHANGE_YOUR_VOTE, "The poll doesn't allow change your vote."); } break; case DAP_CHAIN_NET_VOTE_VOTING_SOURCE_ADDRESS_INVALID: { dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_VOTING_SOURCE_ADDRESS_INVALID, "source address is invalid"); @@ -910,7 +961,7 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) }break; case CMD_LIST:{ json_object* json_vote_out = json_object_new_object(); - json_object_object_add(json_vote_out, "List of votings in net", json_object_new_string(l_net->pub.name)); + json_object_object_add(json_vote_out, "list_of_polls", json_object_new_string(l_net->pub.name)); json_object* json_arr_voting_out = json_object_new_array(); dap_chain_net_votings_t *l_voting = NULL, *l_tmp; pthread_rwlock_rdlock(&s_votings_rwlock); @@ -918,11 +969,12 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) if (l_voting->net_id.uint64 != l_net->pub.id.uint64) continue; json_object* json_obj_vote = json_object_new_object(); - json_object_object_add( json_obj_vote, "voting_tx", + json_object_object_add( json_obj_vote, "poll_tx", json_object_new_string(dap_chain_hash_fast_to_str_static(&l_voting->voting_hash))); char* l_voting_question = (char*)l_voting->voting_params.voting_tx + l_voting->voting_params.voting_question_offset; json_object_object_add( json_obj_vote, "question", json_object_new_string_len(l_voting_question, l_voting->voting_params.voting_question_length) ); + json_object_object_add(json_obj_vote, "token", json_object_new_string(l_voting->voting_params.token_ticker)); json_object_array_add(json_arr_voting_out, json_obj_vote); } pthread_rwlock_unlock(&s_votings_rwlock); @@ -937,13 +989,17 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) } dap_hash_fast_t l_voting_hash = {}; - dap_chain_hash_fast_from_str(l_hash_str, &l_voting_hash); + if (dap_chain_hash_fast_from_str(l_hash_str, &l_voting_hash)) { + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_DUMP_HASH_PARAM_INVALID, + "Can't recognize hash string as a valid HEX or BASE58 format hash"); + return -DAP_CHAIN_NET_VOTE_DUMP_HASH_PARAM_INVALID; + } dap_chain_net_votings_t *l_voting = NULL; pthread_rwlock_rdlock(&s_votings_rwlock); HASH_FIND(hh, s_votings, &l_voting_hash, sizeof(l_voting_hash), l_voting); pthread_rwlock_unlock(&s_votings_rwlock); if (!l_voting) { - dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_DUMP_CAN_NOT_FIND_VOTE, "Can't find voting with hash %s", l_hash_str); + dap_json_rpc_error_add(*json_arr_reply, DAP_CHAIN_NET_VOTE_DUMP_CAN_NOT_FIND_VOTE, "Can't find poll with hash %s", l_hash_str); return -DAP_CHAIN_NET_VOTE_DUMP_CAN_NOT_FIND_VOTE; } @@ -969,10 +1025,11 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) } json_object* json_vote_out = json_object_new_object(); - json_object_object_add(json_vote_out, "voting_tx", json_object_new_string(l_hash_str)); + json_object_object_add(json_vote_out, "poll_tx", json_object_new_string(l_hash_str)); json_object_object_add(json_vote_out, "question", json_object_new_string_len((char*)l_voting->voting_params.voting_tx + l_voting->voting_params.voting_question_offset, l_voting->voting_params.voting_question_length)); + json_object_object_add(json_vote_out, "token", json_object_new_string(l_voting->voting_params.token_ticker)); if (l_voting->voting_params.voting_expire) { char l_tmp_buf[DAP_TIME_STR_SIZE]; dap_time_to_str_rfc822(l_tmp_buf, DAP_TIME_STR_SIZE, l_voting->voting_params.voting_expire); @@ -998,21 +1055,20 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) json_object* json_vote_obj = json_object_new_object(); json_object_object_add(json_vote_obj, "option_id", json_object_new_int(i)); dap_chain_net_vote_option_t* l_vote_option = (dap_chain_net_vote_option_t*)l_option->data; - json_object_object_add( json_vote_obj, "vote_tx", + json_object_object_add( json_vote_obj, "option_text", json_object_new_string_len((char*)l_voting->voting_params.voting_tx + l_vote_option->vote_option_offset, l_vote_option->vote_option_length) ); - json_object_object_add(json_vote_obj, "voting_power", json_object_new_uint64( l_results[i].num_of_votes) ); - int l_percentage = l_votes_count ? (int)((float)l_results[i].num_of_votes/l_votes_count * 100 + 0.5) : 0; - json_object_object_add(json_vote_obj, "vote_share", json_object_new_int(l_percentage) ); + json_object_object_add(json_vote_obj, "votes_count", json_object_new_uint64( l_results[i].num_of_votes) ); + int l_percentage = l_votes_count ? ((double)(l_results[i].num_of_votes * 100))/l_votes_count + 0.5 : 0; + json_object_object_add(json_vote_obj, "votes_percent", json_object_new_int(l_percentage) ); uint256_t l_weight_percentage = { }; - DIV_256_COIN(l_results[i].weights, l_total_weight, &l_weight_percentage); MULT_256_COIN(l_weight_percentage, dap_chain_coins_to_balance("100.0"), &l_weight_percentage); const char *l_weight_percentage_str = dap_uint256_decimal_to_round_char(l_weight_percentage, 2, true), *l_w_coins, *l_w_datoshi = dap_uint256_to_char(l_results[i].weights, &l_w_coins); - json_object_object_add(json_vote_obj, "vote_sum", json_object_new_string(l_w_coins)); - json_object_object_add(json_vote_obj, "vote_sum_datoshi", json_object_new_string(l_w_datoshi)); - json_object_object_add(json_vote_obj, "vote_sum_weight", json_object_new_string(l_weight_percentage_str)); + json_object_object_add(json_vote_obj, "votes_sum", json_object_new_string(l_w_coins)); + json_object_object_add(json_vote_obj, "votes_sum_datoshi", json_object_new_string(l_w_datoshi)); + json_object_object_add(json_vote_obj, "votes_sum_weight", json_object_new_string(l_weight_percentage_str)); json_object_array_add(json_arr_vote_out, json_vote_obj); } json_object_object_add(json_vote_out, "results", json_arr_vote_out); @@ -1020,7 +1076,6 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) const char *l_tw_coins, *l_tw_datoshi = dap_uint256_to_char(l_total_weight, &l_tw_coins); json_object_object_add(json_vote_out, "total_sum", json_object_new_string(l_tw_coins)); json_object_object_add(json_vote_out, "total_sum_datoshi", json_object_new_string(l_tw_datoshi)); - json_object_object_add(json_vote_out, "ticker", json_object_new_string(l_net->pub.native_ticker)); json_object_array_add(*json_arr_reply, json_vote_out); } break; default: @@ -1029,164 +1084,116 @@ static int s_cli_voting(int a_argc, char **a_argv, void **a_str_reply) return 0; } -static int s_datum_tx_voting_coin_check_spent(dap_chain_net_t *a_net, dap_hash_fast_t a_voting_hash, dap_hash_fast_t a_tx_prev_hash, int a_out_idx, dap_hash_fast_t *a_pkey_hash) +static int s_tx_is_spent(dap_ledger_t *a_ledger, dap_hash_fast_t *a_tx_hash, dap_hash_fast_t *a_voting_hash, + dap_hash_fast_t *a_pkey_hash, dap_chain_net_votings_t *a_voting, dap_time_t a_voting_ts) { - int l_coin_is_spent = 0; - - - dap_ledger_t *l_ledger = a_net->pub.ledger; - if(!l_ledger){ - log_it(L_ERROR, "Can't find ledger"); - return -1; + dap_chain_datum_tx_t *l_tx = dap_ledger_tx_find_by_hash(a_ledger, a_tx_hash); + if (!l_tx) { + log_it(L_ERROR, "Can't find tx %s", dap_hash_fast_to_str_static(a_tx_hash)); + return -3; } - dap_chain_datum_tx_t *l_voting_tx = dap_ledger_tx_find_by_hash(l_ledger, &a_voting_hash); - const char *l_native_ticker = a_net->pub.native_ticker; - - dap_list_t *l_tx_list = NULL; // "stack" for saving txs on up level - dap_chain_datum_tx_t *l_tx = dap_ledger_tx_find_by_hash(l_ledger, &a_tx_prev_hash); - if (!l_tx){ - log_it(L_ERROR, "Can't find tx"); - return -1; - } - - if (l_tx->header.ts_created < l_voting_tx->header.ts_created){ + if (l_tx->header.ts_created < a_voting_ts) return 0; - } - - if (s_datum_tx_voting_coin_check_cond_out(a_net, a_voting_hash, a_tx_prev_hash, a_out_idx) != 0){ - return 1; - } dap_chain_tx_vote_t *l_vote = (dap_chain_tx_vote_t *)dap_chain_datum_tx_item_get(l_tx, NULL, NULL, TX_ITEM_TYPE_VOTE, NULL); - if (l_vote && dap_hash_fast_compare(&l_vote->voting_hash, &a_voting_hash)) { - dap_chain_net_votings_t *l_voting = NULL; - pthread_rwlock_wrlock(&s_votings_rwlock); - HASH_FIND(hh, s_votings, &a_voting_hash, sizeof(dap_hash_fast_t), l_voting); - pthread_rwlock_unlock(&s_votings_rwlock); - if (l_voting) { - for (dap_list_t *it = l_voting->votes; it; it = it->next) { - dap_chain_net_vote_t *l_vote = (dap_chain_net_vote_t *)it->data; - if (dap_hash_fast_compare(&l_vote->vote_hash, &a_tx_prev_hash)) { - if (l_voting->voting_params.vote_changing_allowed && - !dap_hash_fast_is_blank(a_pkey_hash) && - dap_hash_fast_compare(&l_vote->pkey_hash, a_pkey_hash)) - break; // it's vote changing, allow it - return 1; - } + if (l_vote && dap_hash_fast_compare(&l_vote->voting_hash, &a_voting->voting_hash)) { + for (dap_list_t *it = a_voting->votes; it; it = it->next) { + dap_chain_net_vote_t *l_vote = (dap_chain_net_vote_t *)it->data; + if (dap_hash_fast_compare(&l_vote->vote_hash, a_tx_hash)) { + if (a_voting->voting_params.vote_changing_allowed && + !dap_hash_fast_is_blank(a_pkey_hash) && + dap_hash_fast_compare(&l_vote->pkey_hash, a_pkey_hash)) + break; // it's vote changing, allow it + return 1; } } } + dap_list_t *l_ins_list = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN, NULL); + l_ins_list = dap_list_concat(l_ins_list, dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN_COND, NULL)); + if (!l_ins_list) // it's emisssion or reward TX, not marked yet + return 0; - dap_list_t *l_ins_list_temp = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN, NULL); - dap_list_t *l_cond_ins_list = dap_chain_datum_tx_items_get(l_tx, TX_ITEM_TYPE_IN_COND, NULL); - if (!l_ins_list_temp && !l_cond_ins_list){ - log_it(L_ERROR, "Can't get inputs from tx"); - return -1; - } - - dap_list_t *l_ins_list = NULL; - l_ins_list = dap_list_concat(l_ins_list, l_ins_list_temp); - l_ins_list = dap_list_concat(l_ins_list, l_cond_ins_list); - - l_tx_list = dap_list_append(l_tx_list, l_ins_list); - dap_list_t* l_tx_temp = dap_list_last(l_tx_list); - - while(l_tx_temp && !l_coin_is_spent){ - if (l_tx_temp->data == NULL){ - l_tx_list = dap_list_delete_link(l_tx_list, l_tx_temp); - l_tx_temp = l_tx_list ? dap_list_last(l_tx_list) : NULL; - continue; - } - dap_list_t *l_ins_list = (dap_list_t*)l_tx_temp->data; - dap_chain_tx_in_t* l_temp_in = (dap_chain_tx_in_t*)l_ins_list->data; - dap_chain_datum_tx_t *l_tx_prev_temp = dap_ledger_tx_find_by_hash(l_ledger, &l_temp_in->header.tx_prev_hash); + dap_hash_fast_t l_prev_hash = {}; + for (dap_list_t *it = l_ins_list; it; it = it->next) { + uint32_t l_prev_idx = -1; + if (*(byte_t *)it->data == TX_ITEM_TYPE_IN_COND) { + dap_chain_tx_in_cond_t *in = it->data; + l_prev_hash = in->header.tx_prev_hash; + l_prev_idx = in->header.tx_out_prev_idx; + } else { + dap_chain_tx_in_t *in = it->data; + l_prev_hash = in->header.tx_prev_hash; + l_prev_idx = in->header.tx_out_prev_idx; + } + dap_chain_datum_tx_t *l_tx_prev = dap_ledger_tx_find_by_hash(a_ledger, &l_prev_hash); const char* l_tx_token = NULL; - dap_chain_tx_out_t *l_prev_out_union = (dap_chain_tx_out_t*)dap_chain_datum_tx_out_get_by_out_idx(l_tx_prev_temp, l_temp_in->header.tx_out_prev_idx); - if (!l_prev_out_union){ - l_tx_temp->data = dap_list_remove(l_tx_temp->data, l_temp_in); - if (l_tx_temp->data == NULL){ - l_tx_list = dap_list_delete_link(l_tx_list, l_tx_temp); - l_tx_temp = l_tx_list ? dap_list_last(l_tx_list) : NULL; - } - continue; - } - - switch (l_prev_out_union->header.type) { + byte_t *l_prev_out_union = dap_chain_datum_tx_out_get_by_out_idx(l_tx_prev, l_prev_idx); + switch (*l_prev_out_union) { case TX_ITEM_TYPE_OUT:{ - l_tx_token = dap_ledger_tx_get_token_ticker_by_hash(l_ledger, &l_temp_in->header.tx_prev_hash); + dap_chain_tx_out_t *l_out = (dap_chain_tx_out_t *)l_prev_out_union; + l_tx_token = dap_ledger_tx_get_token_ticker_by_hash(a_ledger, &l_prev_hash); }break; case TX_ITEM_TYPE_OUT_EXT:{ - dap_chain_tx_out_ext_t *l_temp_out = (dap_chain_tx_out_ext_t *)l_prev_out_union; - l_tx_token = l_temp_out->token; + dap_chain_tx_out_ext_t *l_out = (dap_chain_tx_out_ext_t *)l_prev_out_union; + l_tx_token = l_out->token; }break; case TX_ITEM_TYPE_OUT_COND:{ - dap_chain_tx_out_cond_t *l_temp_out = (dap_chain_tx_out_cond_t*)l_prev_out_union; - if (l_temp_out->header.subtype == DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK || - s_datum_tx_voting_coin_check_cond_out(a_net, a_voting_hash, l_temp_in->header.tx_prev_hash, l_temp_in->header.tx_out_prev_idx) == 0) + dap_chain_tx_out_cond_t *l_out = (dap_chain_tx_out_cond_t *)l_prev_out_union; + if (l_out->header.subtype != DAP_CHAIN_TX_OUT_COND_SUBTYPE_FEE) { + l_tx_token = a_ledger->net->pub.native_ticker; break; + } + if (s_datum_tx_voting_coin_check_cond_out(a_ledger->net, *a_voting_hash, l_prev_hash, l_prev_idx, a_pkey_hash) != 0) { + dap_list_free(l_ins_list); + return 1; + } + l_tx_token = dap_ledger_tx_get_token_ticker_by_hash(a_ledger, &l_prev_hash); } default: - l_tx_temp->data = dap_list_remove((dap_list_t*)l_tx_temp->data, l_temp_in); - if (l_tx_temp->data == NULL){ - l_tx_list = dap_list_delete_link(l_tx_list, l_tx_temp); - l_tx_temp = l_tx_list ? dap_list_last(l_tx_list) : NULL; - } - continue; + break; } - - if (l_tx_prev_temp->header.ts_created < l_voting_tx->header.ts_created || - dap_strcmp(l_tx_token, l_native_ticker)){ - l_tx_temp->data = dap_list_remove((dap_list_t*)l_tx_temp->data, l_temp_in); - if (l_tx_temp->data == NULL){ - l_tx_list = dap_list_delete_link(l_tx_list, l_tx_temp); - l_tx_temp = l_tx_list ? dap_list_last(l_tx_list) : NULL; - } + if (dap_strcmp(l_tx_token, a_voting->voting_params.token_ticker)) continue; - } - + } - dap_chain_tx_vote_t *l_vote =(dap_chain_tx_vote_t *) dap_chain_datum_tx_item_get(l_tx_prev_temp, NULL, NULL, TX_ITEM_TYPE_VOTE, NULL); - if(l_vote && dap_hash_fast_compare(&l_vote->voting_hash, &a_voting_hash)){ - dap_chain_net_votings_t *l_voting = NULL; - pthread_rwlock_wrlock(&s_votings_rwlock); - HASH_FIND(hh, s_votings, &a_voting_hash, sizeof(dap_hash_fast_t), l_voting); - pthread_rwlock_unlock(&s_votings_rwlock); - dap_list_t *l_temp = NULL; - while (l_temp){ - dap_chain_net_vote_t *l_vote = (dap_chain_net_vote_t *)l_temp->data; - if (dap_hash_fast_compare(&l_vote->vote_hash, &l_temp_in->header.tx_prev_hash)){ - l_coin_is_spent = 1; - break; - } - l_temp = l_temp->next; - } - } + return s_tx_is_spent(a_ledger, &l_prev_hash, a_voting_hash, a_pkey_hash, a_voting, a_voting_ts); +} - l_ins_list = dap_chain_datum_tx_items_get(l_tx_prev_temp, TX_ITEM_TYPE_IN, NULL); - l_tx_list = dap_list_append(l_tx_list, l_ins_list); - l_tx_temp->data = dap_list_remove((dap_list_t*)l_tx_temp->data, l_temp_in); - l_tx_temp = l_tx_list ? dap_list_last(l_tx_list) : NULL; +static int s_datum_tx_voting_coin_check_spent(dap_chain_net_t *a_net, dap_hash_fast_t a_voting_hash, + dap_hash_fast_t a_tx_prev_hash, int a_out_idx, dap_hash_fast_t *a_pkey_hash) +{ + int l_coin_is_spent = 0; + dap_chain_net_votings_t *l_voting = NULL; + pthread_rwlock_wrlock(&s_votings_rwlock); + HASH_FIND(hh, s_votings, &a_voting_hash, sizeof(dap_hash_fast_t), l_voting); + pthread_rwlock_unlock(&s_votings_rwlock); + if (!l_voting) { + log_it(L_ERROR, "Can't find poll %s", dap_hash_fast_to_str_static(&a_voting_hash)); + return -1; } - if(l_tx_list){ - l_tx_temp = l_tx_list; - while(l_tx_temp){ - if (l_tx_temp->data) - dap_list_free((dap_list_t*)l_tx_temp->data); - l_tx_list = dap_list_delete_link(l_tx_list, l_tx_temp); - l_tx_temp = dap_list_first(l_tx_list); - } + dap_ledger_t *l_ledger = a_net->pub.ledger; + dap_chain_datum_tx_t *l_voting_tx = dap_ledger_tx_find_by_hash(l_ledger, &a_voting_hash); + if (!l_voting_tx) { + log_it(L_ERROR, "Can't find poll tx %s", dap_hash_fast_to_str_static(&a_voting_hash)); + return -2; } - return l_coin_is_spent; + if (s_datum_tx_voting_coin_check_cond_out(a_net, a_voting_hash, a_tx_prev_hash, a_out_idx, a_pkey_hash) != 0) + return 1; + + return s_tx_is_spent(l_ledger, &a_tx_prev_hash, &a_voting_hash, a_pkey_hash, l_voting, l_voting_tx->header.ts_created); + } -static int s_datum_tx_voting_coin_check_cond_out(dap_chain_net_t *a_net, dap_hash_fast_t a_voting_hash, dap_hash_fast_t a_tx_cond_hash, int a_cond_out_idx) +static int s_datum_tx_voting_coin_check_cond_out(dap_chain_net_t *a_net, dap_hash_fast_t a_voting_hash, + dap_hash_fast_t a_tx_cond_hash, int a_cond_out_idx, + dap_hash_fast_t *a_pkey_hash) { dap_chain_net_votings_t * l_voting = NULL; @@ -1194,27 +1201,27 @@ static int s_datum_tx_voting_coin_check_cond_out(dap_chain_net_t *a_net, dap_has HASH_FIND(hh, s_votings, &a_voting_hash, sizeof(dap_hash_fast_t), l_voting); pthread_rwlock_unlock(&s_votings_rwlock); if(!l_voting || l_voting->net_id.uint64 != a_net->pub.id.uint64) { - log_it(L_ERROR, "Can't find voting with hash %s in net %s", + log_it(L_ERROR, "Can't find poll with hash %s in net %s", dap_chain_hash_fast_to_str_static(&a_voting_hash), a_net->pub.name); return -1; } - dap_chain_net_voting_cond_outs_t *l_tx_outs = NULL; + dap_chain_net_voting_cond_outs_t *l_tx_out = NULL; pthread_rwlock_wrlock(&l_voting->s_tx_outs_rwlock); - HASH_FIND(hh, l_voting->voting_spent_cond_outs, &a_tx_cond_hash, sizeof(dap_hash_fast_t), l_tx_outs); + HASH_FIND(hh, l_voting->voting_spent_cond_outs, &a_tx_cond_hash, sizeof(dap_hash_fast_t), l_tx_out); pthread_rwlock_unlock(&l_voting->s_tx_outs_rwlock); - if (!l_tx_outs || l_tx_outs->out_idx != a_cond_out_idx){ - return 0; - } + if (l_tx_out && l_tx_out->out_idx == a_cond_out_idx) + return a_pkey_hash ? !dap_hash_fast_compare(a_pkey_hash, &l_tx_out->pkey_hash) : 1; - return 1; + return 0; } int dap_chain_net_vote_create(const char *a_question, dap_list_t *a_options, dap_time_t a_expire_vote, uint64_t a_max_vote, uint256_t a_fee, bool a_delegated_key_required, bool a_vote_changing_allowed, dap_chain_wallet_t *a_wallet, - dap_chain_net_t *a_net, const char *a_hash_out_type, char **a_hash_output) { + dap_chain_net_t *a_net, const char *a_token_ticker, + const char *a_hash_out_type, char **a_hash_output) { if (strlen(a_question) > DAP_CHAIN_DATUM_TX_VOTING_QUESTION_MAX_LENGTH){ return DAP_CHAIN_NET_VOTE_CREATE_LENGTH_QUESTION_OVERSIZE_MAX; @@ -1248,8 +1255,7 @@ int dap_chain_net_vote_create(const char *a_question, dap_list_t *a_options, dap dap_ledger_t* l_ledger = a_net->pub.ledger; dap_list_t *l_list_used_out = NULL; if (dap_chain_wallet_cache_tx_find_outs_with_val(a_net, l_native_ticker, l_addr_from, &l_list_used_out, l_total_fee, &l_value_transfer) == -101) - l_list_used_out = dap_ledger_get_list_tx_outs_with_val(l_ledger, l_native_ticker, - l_addr_from, l_total_fee, &l_value_transfer); + l_list_used_out = dap_ledger_get_list_tx_outs_with_val(l_ledger, l_native_ticker, l_addr_from, l_total_fee, &l_value_transfer); if (!l_list_used_out) { return DAP_CHAIN_NET_VOTE_CREATE_NOT_ENOUGH_FUNDS_TO_TRANSFER; } @@ -1332,6 +1338,16 @@ int dap_chain_net_vote_create(const char *a_question, dap_list_t *a_options, dap DAP_DEL_Z(l_vote_changing_item); } + if (a_token_ticker) { + dap_chain_tx_tsd_t *l_voting_token_item = dap_chain_datum_voting_token_tsd_create(a_token_ticker); + if (!l_voting_token_item) { + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_CREATE_TSD_TOKEN; + } + dap_chain_datum_tx_add_item(&l_tx, l_voting_token_item); + DAP_DEL_Z(l_voting_token_item); + } + // add 'in' items uint256_t l_value_to_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out); assert(EQUAL_256(l_value_to_items, l_value_transfer)); @@ -1339,7 +1355,7 @@ int dap_chain_net_vote_create(const char *a_question, dap_list_t *a_options, dap uint256_t l_value_pack = {}; // Network fee if (l_net_fee_used) { - if (dap_chain_datum_tx_add_out_item(&l_tx, &l_addr_fee, l_net_fee) == 1) + if (dap_chain_datum_tx_add_out_ext_item(&l_tx, &l_addr_fee, l_net_fee, l_native_ticker) == 1) SUM_256_256(l_value_pack, l_net_fee, &l_value_pack); else { dap_chain_datum_tx_delete(l_tx); @@ -1359,7 +1375,7 @@ int dap_chain_net_vote_create(const char *a_question, dap_list_t *a_options, dap uint256_t l_value_back; SUBTRACT_256_256(l_value_transfer, l_value_pack, &l_value_back); if(!IS_ZERO_256(l_value_back)) { - if(dap_chain_datum_tx_add_out_item(&l_tx, l_addr_from, l_value_back) != 1) { + if(dap_chain_datum_tx_add_out_ext_item(&l_tx, l_addr_from, l_value_back, l_native_ticker) != 1) { dap_chain_datum_tx_delete(l_tx); return DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_ADD_OUT_WITH_VALUE_BACK; } @@ -1406,47 +1422,42 @@ int dap_chain_net_vote_voting(dap_cert_t *a_cert, uint256_t a_fee, dap_chain_wal if (l_voting->voting_params.voting_expire && dap_time_now() > l_voting->voting_params.voting_expire) return DAP_CHAIN_NET_VOTE_VOTING_ALREADY_EXPIRED; - dap_hash_fast_t l_pkey_hash = {0}; + dap_chain_addr_t *l_addr_from = dap_chain_wallet_get_addr(a_wallet, a_net->pub.id); + if (!l_addr_from) + return DAP_CHAIN_NET_VOTE_VOTING_SOURCE_ADDRESS_INVALID; + dap_hash_fast_t l_pkey_hash = {0}; if (l_voting->voting_params.delegate_key_required) { if (!a_cert) return DAP_CHAIN_NET_VOTE_VOTING_CERT_REQUIRED; - if (!a_cert->enc_key) + if (dap_cert_get_pkey_hash(a_cert, &l_pkey_hash)) return DAP_CHAIN_NET_VOTE_VOTING_NO_KEY_FOUND_IN_CERT; - // Get publivc key hash - size_t l_pub_key_size = 0; - uint8_t *l_pub_key = dap_enc_key_serialize_pub_key(a_cert->enc_key, &l_pub_key_size);; - if (l_pub_key == NULL) - return DAP_CHAIN_NET_VOTE_VOTING_NO_PUBLIC_KEY_IN_CERT; - - dap_hash_fast(l_pub_key, l_pub_key_size, &l_pkey_hash); - DAP_DELETE(l_pub_key); if (!dap_chain_net_srv_stake_check_pkey_hash(a_net->pub.id, &l_pkey_hash)) return DAP_CHAIN_NET_VOTE_VOTING_KEY_IS_NOT_DELEGATED; - for (dap_list_t *it = l_voting->votes; it; it = it->next) - if (dap_hash_fast_compare(&((dap_chain_net_vote_t *)it->data)->pkey_hash, &l_pkey_hash) && - !l_voting->voting_params.vote_changing_allowed) - return DAP_CHAIN_NET_VOTE_VOTING_DOES_NOT_ALLOW_CHANGE_YOUR_VOTE; - } - - dap_enc_key_t *l_priv_key = NULL; - - l_priv_key = dap_chain_wallet_get_key(a_wallet, 0); - - const dap_chain_addr_t *l_addr_from = (const dap_chain_addr_t *) dap_chain_wallet_get_addr(a_wallet, a_net->pub.id); + } else + l_pkey_hash = l_addr_from->data.hash_fast; - if (!l_addr_from) - return DAP_CHAIN_NET_VOTE_VOTING_SOURCE_ADDRESS_INVALID; + bool l_vote_changed = false; + for (dap_list_t *it = l_voting->votes; it; it = it->next) + if (dap_hash_fast_compare(&((dap_chain_net_vote_t *)it->data)->pkey_hash, &l_pkey_hash)) { + if (!l_voting->voting_params.vote_changing_allowed) + return DAP_CHAIN_NET_VOTE_VOTING_DOES_NOT_ALLOW_CHANGE_YOUR_VOTE; + l_vote_changed = true; + break; + } - const char *l_native_ticker = a_net->pub.native_ticker; - uint256_t l_net_fee = {}, l_total_fee = {}, l_value_transfer; + const char *l_token_ticker = l_voting->voting_params.token_ticker; + uint256_t l_net_fee = {}, l_total_fee = a_fee, l_value_transfer, l_fee_transfer; dap_chain_addr_t l_addr_fee = {}; bool l_net_fee_used = dap_chain_net_tx_get_fee(a_net->pub.id, &l_net_fee, &l_addr_fee); - SUM_256_256(l_net_fee, a_fee, &l_total_fee); + if (l_net_fee_used) + SUM_256_256(l_net_fee, a_fee, &l_total_fee); - dap_ledger_t* l_ledger = dap_ledger_by_net_name(a_net->pub.name); - dap_list_t *l_list_used_out = dap_ledger_get_list_tx_outs(l_ledger, l_native_ticker, l_addr_from, &l_value_transfer); - if (!l_list_used_out || compare256(l_value_transfer, l_total_fee) <= 0) { + bool l_native_tx = !dap_strcmp(l_token_ticker, a_net->pub.native_ticker); + dap_ledger_t *l_ledger = a_net->pub.ledger; + dap_list_t *l_list_used_out = dap_ledger_get_list_tx_outs(l_ledger, l_token_ticker, l_addr_from, &l_value_transfer); + if (!l_list_used_out || (l_native_tx && compare256(l_value_transfer, l_total_fee) < 0)) { + dap_list_free_full(l_list_used_out, NULL); return DAP_CHAIN_NET_VOTE_VOTING_NOT_ENOUGH_FUNDS_TO_TRANSFER; } @@ -1455,24 +1466,42 @@ int dap_chain_net_vote_voting(dap_cert_t *a_cert, uint256_t a_fee, dap_chain_wal dap_list_t *it, *tmp; DL_FOREACH_SAFE(l_list_used_out, it, tmp) { dap_chain_tx_used_out_item_t *l_out = (dap_chain_tx_used_out_item_t *)it->data; - if (s_datum_tx_voting_coin_check_spent(a_net, a_hash, l_out->tx_hash_fast, l_out->num_idx_out, &l_pkey_hash) && - !l_voting->voting_params.vote_changing_allowed) { - dap_list_delete_link(l_list_used_out, it); + if (s_datum_tx_voting_coin_check_spent(a_net, a_hash, l_out->tx_hash_fast, l_out->num_idx_out, + l_vote_changed ? &l_pkey_hash : NULL)) { + l_list_used_out = dap_list_delete_link(l_list_used_out, it); continue; } if (SUM_256_256(l_value_transfer_new, l_out->value, &l_value_transfer_new)) return DAP_CHAIN_NET_VOTE_VOTING_INTEGER_OVERFLOW; } - if (IS_ZERO_256(l_value_transfer_new) || compare256(l_value_transfer_new, l_total_fee) <= 0){ + if (IS_ZERO_256(l_value_transfer_new) || (l_native_tx && compare256(l_value_transfer_new, l_total_fee) <= 0)) return DAP_CHAIN_NET_VOTE_VOTING_UNSPENT_UTX0_FOR_PARTICIPATION_THIS_VOTING; - } l_value_transfer = l_value_transfer_new; // create empty transaction dap_chain_datum_tx_t *l_tx = dap_chain_datum_tx_create(); + uint256_t l_value_back = l_value_transfer, l_fee_back = {}; + if (!l_native_tx) { + dap_list_t *l_list_fee_outs = dap_ledger_get_list_tx_outs_with_val(l_ledger, a_net->pub.native_ticker, l_addr_from, l_total_fee, &l_fee_transfer); + if (!l_list_fee_outs) { + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_VOTING_NOT_ENOUGH_FUNDS_TO_TRANSFER; + } + uint256_t l_value_fee_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_fee_outs); + assert(EQUAL_256(l_value_fee_items, l_fee_transfer)); + dap_list_free_full(l_list_fee_outs, NULL); + SUBTRACT_256_256(l_fee_transfer, l_total_fee, &l_fee_back); + } else + SUBTRACT_256_256(l_value_transfer, l_total_fee, &l_value_back); + + // add 'in' items + uint256_t l_value_to_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out); + assert(EQUAL_256(l_value_to_items, l_value_transfer)); + dap_list_free_full(l_list_used_out, NULL); + // Add vote item if (a_option_idx > dap_list_length(l_voting->voting_params.option_offsets_list)){ dap_chain_datum_tx_delete(l_tx); @@ -1486,17 +1515,13 @@ int dap_chain_net_vote_voting(dap_cert_t *a_cert, uint256_t a_fee, dap_chain_wal dap_chain_datum_tx_add_item(&l_tx, l_vote_item); DAP_DEL_Z(l_vote_item); - // add stake out conds items - dap_list_t *l_outs = dap_ledger_get_list_tx_cond_outs(l_ledger, a_net->pub.native_ticker, l_addr_from, - DAP_CHAIN_TX_OUT_COND_SUBTYPE_SRV_STAKE_LOCK, NULL); - dap_list_t *l_temp = l_outs; - while(l_temp){ - dap_chain_tx_used_out_item_t *l_out_item = (dap_chain_tx_used_out_item_t *)l_temp->data; - if (dap_ledger_tx_hash_is_used_out_item(a_net->pub.ledger, &l_out_item->tx_hash_fast, l_out_item->num_idx_out, NULL) || - s_datum_tx_voting_coin_check_cond_out(a_net, a_hash, l_out_item->tx_hash_fast, l_out_item->num_idx_out ) != 0){ - l_temp = l_temp->next; + // add out conds items + dap_list_t *l_outs = dap_ledger_get_list_tx_cond_outs(l_ledger, DAP_CHAIN_TX_OUT_COND_SUBTYPE_ALL, l_token_ticker, l_addr_from); + for (dap_list_t *it = l_outs; it; it = it->next) { + dap_chain_tx_used_out_item_t *l_out_item = (dap_chain_tx_used_out_item_t *)it->data; + if (s_datum_tx_voting_coin_check_cond_out(a_net, a_hash, l_out_item->tx_hash_fast, l_out_item->num_idx_out, + l_vote_changed ? &l_pkey_hash : NULL) != 0) continue; - } dap_chain_tx_tsd_t *l_item = dap_chain_datum_voting_vote_tx_cond_tsd_create(l_out_item->tx_hash_fast, l_out_item->num_idx_out); if(!l_item){ dap_chain_datum_tx_delete(l_tx); @@ -1506,55 +1531,44 @@ int dap_chain_net_vote_voting(dap_cert_t *a_cert, uint256_t a_fee, dap_chain_wal } dap_chain_datum_tx_add_item(&l_tx, l_item); DAP_DEL_Z(l_item); - l_temp = l_temp->next; } dap_list_free_full(l_outs, NULL); - // add 'in' items - uint256_t l_value_to_items = dap_chain_datum_tx_add_in_item_list(&l_tx, l_list_used_out); - assert(EQUAL_256(l_value_to_items, l_value_transfer)); - dap_list_free_full(l_list_used_out, NULL); - uint256_t l_value_pack = {}; // Network fee - if (l_net_fee_used) { - if (dap_chain_datum_tx_add_out_item(&l_tx, &l_addr_fee, l_net_fee) == 1) - SUM_256_256(l_value_pack, l_net_fee, &l_value_pack); - else { - dap_chain_datum_tx_delete(l_tx); - return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_NET_FEE_OUT; - } + if (l_net_fee_used && dap_chain_datum_tx_add_out_ext_item(&l_tx, &l_addr_fee, l_net_fee, a_net->pub.native_ticker) != 1) { + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_NET_FEE_OUT; } + // Validator's fee - if (!IS_ZERO_256(a_fee)) { - if (dap_chain_datum_tx_add_fee_item(&l_tx, a_fee) == 1) - SUM_256_256(l_value_pack, a_fee, &l_value_pack); - else { - dap_chain_datum_tx_delete(l_tx); - return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_NET_FEE_OUT; - } + if (!IS_ZERO_256(a_fee) && dap_chain_datum_tx_add_fee_item(&l_tx, a_fee) != 1) { + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_NET_FEE_OUT; } + // coin back - uint256_t l_value_back; - SUBTRACT_256_256(l_value_transfer, l_value_pack, &l_value_back); - if(!IS_ZERO_256(l_value_back)) { - if(dap_chain_datum_tx_add_out_item(&l_tx, l_addr_from, l_value_back) != 1) { - dap_chain_datum_tx_delete(l_tx); - return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_OUT_WITH_VALUE_BACK; - } + if (!IS_ZERO_256(l_value_back) && dap_chain_datum_tx_add_out_ext_item(&l_tx, l_addr_from, l_value_back, l_token_ticker) != 1) { + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_OUT_WITH_VALUE_BACK; + } + if (!IS_ZERO_256(l_fee_back) && dap_chain_datum_tx_add_out_ext_item(&l_tx, l_addr_from, l_fee_back, a_net->pub.native_ticker) != 1) { + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_ADD_OUT_WITH_VALUE_BACK; } + dap_enc_key_t *l_priv_key = dap_chain_wallet_get_key(a_wallet, 0); // add 'sign' items with wallet sign - if(dap_chain_datum_tx_add_sign_item(&l_tx, l_priv_key) != 1) { + if (dap_chain_datum_tx_add_sign_item(&l_tx, l_priv_key) != 1) { dap_chain_datum_tx_delete(l_tx); + dap_enc_key_delete(l_priv_key); return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_SIGN_TX; } + dap_enc_key_delete(l_priv_key); // add 'sign' items with delegated key if needed - if(a_cert){ - if(dap_chain_datum_tx_add_sign_item(&l_tx, a_cert->enc_key) != 1) { - dap_chain_datum_tx_delete(l_tx); - return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_SIGN_TX; - } + if (a_cert && dap_chain_datum_tx_add_sign_item(&l_tx, a_cert->enc_key) != 1) { + dap_chain_datum_tx_delete(l_tx); + return DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_SIGN_TX; } size_t l_tx_size = dap_chain_datum_tx_get_size(l_tx); diff --git a/modules/service/voting/include/dap_chain_net_srv_voting.h b/modules/service/voting/include/dap_chain_net_srv_voting.h index 4bd1f71694180eb6b8f390da41c401cca1241aec..70509562bd780f59492b249905f881a3866799a3 100644 --- a/modules/service/voting/include/dap_chain_net_srv_voting.h +++ b/modules/service/voting/include/dap_chain_net_srv_voting.h @@ -94,13 +94,14 @@ enum DAP_CHAIN_NET_VOTE_CREATE_ERROR { DAP_CHAIN_NET_VOTE_CREATE_WALLET_PARAM_NOT_VALID, DAP_CHAIN_NET_VOTE_CREATE_WALLET_DOES_NOT_EXIST, DAP_CHAIN_NET_VOTE_CREATE_WRONG_TIME_FORMAT, - + DAP_CHAIN_NET_VOTE_CREATE_WRONG_TOKEN, + DAP_CHAIN_NET_VOTE_CREATE_CAN_NOT_CREATE_TSD_TOKEN, DAP_CHAIN_NET_VOTE_CREATE_UNKNOWN_ERR }; int dap_chain_net_vote_create(const char *a_question, dap_list_t *a_options, dap_time_t a_expire_vote, uint64_t a_max_vote, uint256_t a_fee, bool a_delegated_key_required, bool a_vote_changing_allowed, dap_chain_wallet_t *a_wallet, - dap_chain_net_t *a_net, const char *a_hash_out_type, char **a_hash_output); + dap_chain_net_t *a_net, const char *a_token_ticker, const char *a_hash_out_type, char **a_hash_output); enum DAP_CHAIN_NET_VOTE_VOTING_ERROR{ DAP_CHAIN_NET_VOTE_VOTING_OK, @@ -108,7 +109,6 @@ enum DAP_CHAIN_NET_VOTE_VOTING_ERROR{ DAP_CHAIN_NET_VOTE_VOTING_THIS_VOTING_HAVE_MAX_VALUE_VOTES, DAP_CHAIN_NET_VOTE_VOTING_ALREADY_EXPIRED, DAP_CHAIN_NET_VOTE_VOTING_NO_KEY_FOUND_IN_CERT, - DAP_CHAIN_NET_VOTE_VOTING_NO_PUBLIC_KEY_IN_CERT, DAP_CHAIN_NET_VOTE_VOTING_CERT_REQUIRED, DAP_CHAIN_NET_VOTE_VOTING_KEY_IS_NOT_DELEGATED, DAP_CHAIN_NET_VOTE_VOTING_DOES_NOT_ALLOW_CHANGE_YOUR_VOTE, @@ -125,13 +125,13 @@ enum DAP_CHAIN_NET_VOTE_VOTING_ERROR{ DAP_CHAIN_NET_VOTE_VOTING_NET_PARAM_MISSING, DAP_CHAIN_NET_VOTE_VOTING_NET_PARAM_NOT_VALID, DAP_CHAIN_NET_VOTE_VOTING_HASH_NOT_FOUND, + DAP_CHAIN_NET_VOTE_VOTING_HASH_INVALID, DAP_CHAIN_NET_VOTE_VOTING_CAN_NOT_FIND_CERT, DAP_CHAIN_NET_VOTE_VOTING_FEE_PARAM_NOT_VALID, DAP_CHAIN_NET_VOTE_VOTING_FEE_PARAM_BAD_TYPE, DAP_CHAIN_NET_VOTE_VOTING_WALLET_PARAM_NOT_VALID, DAP_CHAIN_NET_VOTE_VOTING_OPTION_IDX_PARAM_NOT_VALID, DAP_CHAIN_NET_VOTE_VOTING_WALLET_DOES_NOT_EXIST, - DAP_CHAIN_NET_VOTE_VOTING_UNKNOWN_ERR, DAP_CHAIN_NET_VOTE_VOTING_INTEGER_OVERFLOW @@ -140,6 +140,7 @@ enum DAP_CHAIN_NET_VOTE_VOTING_ERROR{ enum DAP_CHAIN_NET_VOTE_DUMP_ERROR{ DAP_CHAIN_NET_VOTE_DUMP_HASH_PARAM_NOT_FOUND, + DAP_CHAIN_NET_VOTE_DUMP_HASH_PARAM_INVALID, DAP_CHAIN_NET_VOTE_DUMP_CAN_NOT_FIND_VOTE, DAP_CHAIN_NET_VOTE_DUMP_NO_OPTIONS, DAP_CHAIN_NET_VOTE_DUMP_MEMORY_ERR diff --git a/modules/type/blocks/dap_chain_cs_blocks.c b/modules/type/blocks/dap_chain_cs_blocks.c index cc844a27f605128d51836bb2775bf75966ceb13c..b21899511e0ddf09ff27e30e2abd03018a534520 100644 --- a/modules/type/blocks/dap_chain_cs_blocks.c +++ b/modules/type/blocks/dap_chain_cs_blocks.c @@ -2786,3 +2786,34 @@ static dap_list_t *s_callback_get_txs(dap_chain_t *a_chain, size_t a_count, size return l_list; } +/** + * @brief search pkey in block signs + * @param a_chain chain to search + * @param a_pkey_hash - pkey hash + * @return pointer to dap_pkey_t if finded, other - NULL + */ +dap_pkey_t *dap_chain_cs_blocks_get_pkey_by_hash(dap_chain_net_t *a_net, dap_hash_fast_t *a_pkey_hash) +{ + dap_return_val_if_pass(!a_pkey_hash, NULL); + dap_chain_t *l_chain = dap_chain_net_get_chain_by_chain_type(a_net, CHAIN_TYPE_TX); + if (!l_chain || !DAP_CHAIN_CS_BLOCKS(l_chain) || !PVT(DAP_CHAIN_CS_BLOCKS(l_chain))) + return NULL; + dap_pkey_t *l_ret = NULL; + pthread_rwlock_rdlock(&PVT(DAP_CHAIN_CS_BLOCKS(l_chain))->rwlock); + for (dap_chain_block_cache_t *l_block_cache = PVT(DAP_CHAIN_CS_BLOCKS(l_chain))->blocks; l_block_cache; l_block_cache = l_block_cache->hh.next) { + for (size_t i = 0; i < l_block_cache->sign_count; i++) { + dap_sign_t *l_sign = dap_chain_block_sign_get(l_block_cache->block, l_block_cache->block_size, i); + if (dap_sign_is_use_pkey_hash(l_sign)) + continue; + dap_chain_hash_fast_t l_sign_hash = {}; + dap_sign_get_pkey_hash(l_sign, &l_sign_hash); + if(!memcmp(&l_sign_hash, a_pkey_hash, sizeof(dap_chain_hash_fast_t))) { + l_ret = dap_pkey_get_from_sign(l_sign); + break; + } + } + } + pthread_rwlock_unlock(&PVT(DAP_CHAIN_CS_BLOCKS(l_chain))->rwlock); + return l_ret; +} + diff --git a/modules/type/blocks/include/dap_chain_cs_blocks.h b/modules/type/blocks/include/dap_chain_cs_blocks.h index 489595cd33ee8c2c4361e1bcd74faf23205070e6..00c74e85a2caf537461469ed3cf14ad87fe243c6 100644 --- a/modules/type/blocks/include/dap_chain_cs_blocks.h +++ b/modules/type/blocks/include/dap_chain_cs_blocks.h @@ -98,3 +98,5 @@ DAP_STATIC_INLINE char *dap_chain_cs_blocks_get_reward_group(const char *a_net_n { return dap_strdup_printf("local.%s.rewards", a_net_name); } + +dap_pkey_t *dap_chain_cs_blocks_get_pkey_by_hash(dap_chain_net_t *a_net, dap_hash_fast_t *a_pkey_hash); diff --git a/modules/wallet/dap_chain_wallet.c b/modules/wallet/dap_chain_wallet.c index 0438874c4406ca89ee081e966e0a08398385146d..b6abfef6d90fe4c5be56f2bc776db390b88a6e2d 100644 --- a/modules/wallet/dap_chain_wallet.c +++ b/modules/wallet/dap_chain_wallet.c @@ -1101,7 +1101,7 @@ const char* dap_chain_wallet_check_sign(dap_chain_wallet_t *a_wallet) { for (size_t i = 0; i < l_wallet_internal->certs_count; ++i) { dap_return_val_if_pass(!l_wallet_internal->certs[i], "The wallet contains an undefined certificate.\n"); dap_sign_type_t l_sign_type = dap_sign_type_from_key_type(l_wallet_internal->certs[i]->enc_key->type); - if (SIG_TYPE_BLISS == l_sign_type.type || SIG_TYPE_PICNIC == l_sign_type.type || SIG_TYPE_TESLA == l_sign_type.type) { + if (dap_sign_type_is_depricated(l_sign_type)) { return "The Bliss, Picnic and Tesla signatures is deprecated. We recommend you to create a new wallet with another available signature and transfer funds there.\n"; } } diff --git a/modules/wallet/dap_chain_wallet_cache.c b/modules/wallet/dap_chain_wallet_cache.c index f4a70a606bd9190c6daa5c5d76f46d025e4cc7d5..2acea49e373b2a76fc5d303b44a8b06e4ed26ae4 100644 --- a/modules/wallet/dap_chain_wallet_cache.c +++ b/modules/wallet/dap_chain_wallet_cache.c @@ -678,7 +678,7 @@ static int s_save_tx_cache_for_addr(dap_chain_t *a_chain, dap_chain_addr_t *a_ad dap_chain_addr_t l_addr; uint256_t l_value; uint8_t *l_prev_item = NULL; - int l_prev_idx; + int l_prev_idx = 0; switch(*l_tx_item) { case TX_ITEM_TYPE_IN: {