diff --git a/modules/consensus/esbocs/dap_chain_cs_esbocs.c b/modules/consensus/esbocs/dap_chain_cs_esbocs.c index 549de5d62917dc5b750ddfe4e2cd80a90c6cb4be..2ff488b5aa98add4eab1c977eaf7a66b2319bd06 100644 --- a/modules/consensus/esbocs/dap_chain_cs_esbocs.c +++ b/modules/consensus/esbocs/dap_chain_cs_esbocs.c @@ -195,7 +195,9 @@ int dap_chain_cs_esbocs_init() "esbocs emergency_validators {add|remove} -net <net_name> [-chain <chain_name>] -cert <poa_cert_name> -pkey_hash <validator_pkey_hash>\n" "\tAdd or remove validator by its signature public key hash to list of validators allowed to work in emergency mode\n" "esbocs emergency_validators show -net <net_name> [-chain <chain_name>]\n" - "\tShow list of validators public key hashes allowed to work in emergency mode\n"); + "\tShow list of validators public key hashes allowed to work in emergency mode\n" + "esbocs status -net <net_name> [-chain <chain_name>]\n" + "\tShow current esbocs consensus status\n"); return 0; } @@ -1259,11 +1261,11 @@ static int s_signs_sort_callback(dap_list_t *a_sign1, dap_list_t *a_sign2) return l_ret; } -dap_chain_esbocs_directive_t *s_session_directive_ready(dap_chain_esbocs_session_t *a_session) +static bool s_session_directive_ready(dap_chain_esbocs_session_t *a_session) { size_t l_list_length = dap_list_length(a_session->cur_round.all_validators); if (a_session->cur_round.total_validators_synced * 3 < l_list_length * 2) - return NULL; // Not a valid round, less than 2/3 participants + return false; // Not a valid round, less than 2/3 participants bool l_kick = false; dap_chain_esbocs_penalty_item_t *l_item, *l_tmp; HASH_ITER(hh, a_session->penalty, l_item, l_tmp) { @@ -1275,15 +1277,28 @@ dap_chain_esbocs_directive_t *s_session_directive_ready(dap_chain_esbocs_session } if (l_item->miss_count >= DAP_CHAIN_ESBOCS_PENALTY_KICK && l_key_state == 1) { l_kick = true; - break; + return true; } if (l_item->miss_count == 0 && l_key_state == -1) - break; + return false; } if (!l_item) - return NULL; + return true; +} + +static dap_chain_esbocs_directive_t* s_session_directive_compose(dap_chain_esbocs_session_t *a_session) { + size_t l_list_length = dap_list_length(a_session->cur_round.all_validators); debug_if(PVT(a_session->esbocs)->debug, L_MSG, "Current consensus online %hu from %zu is acceptable, so issue the directive", - a_session->cur_round.total_validators_synced, l_list_length); + a_session->cur_round.total_validators_synced, l_list_length); + bool l_kick = false; + dap_chain_esbocs_penalty_item_t *l_item = NULL, *l_tmp = NULL; + HASH_ITER(hh, a_session->penalty, l_item, l_tmp) { + int l_key_state = dap_chain_net_srv_stake_key_delegated(&l_item->signing_addr); + if (l_item->miss_count >= DAP_CHAIN_ESBOCS_PENALTY_KICK && l_key_state == 1) { + l_kick = true; + break; + } + } uint32_t l_directive_size = s_directive_calc_size(l_kick ? DAP_CHAIN_ESBOCS_DIRECTIVE_KICK : DAP_CHAIN_ESBOCS_DIRECTIVE_LIFT); dap_chain_esbocs_directive_t *l_ret = NULL; DAP_NEW_Z_SIZE_RET_VAL(l_ret, dap_chain_esbocs_directive_t, l_directive_size, NULL, NULL); @@ -1334,7 +1349,8 @@ static void s_session_state_change(dap_chain_esbocs_session_t *a_session, enum s dap_chain_esbocs_directive_t *l_directive = NULL; #ifdef DAP_CHAIN_CS_ESBOCS_DIRECTIVE_SUPPORT if (!a_session->cur_round.directive && !PVT(a_session->esbocs)->emergency_mode) - l_directive = s_session_directive_ready(a_session); + if (s_session_directive_ready(a_session)) + l_directive = s_session_directive_compose(a_session); #endif if (l_directive) { dap_hash_fast_t l_directive_hash; @@ -1345,6 +1361,7 @@ static void s_session_state_change(dap_chain_esbocs_session_t *a_session, enum s a_session->chain->net_name, a_session->chain->name, a_session->cur_round.id, a_session->cur_round.attempt_num, l_candidate_hash_str); } + a_session->esbocs->last_directive_vote_timestamp = dap_time_now(); s_message_send(a_session, DAP_CHAIN_ESBOCS_MSG_TYPE_DIRECTIVE, &l_directive_hash, l_directive, l_directive->size, a_session->cur_round.all_validators); DAP_DELETE(l_directive); @@ -1982,7 +1999,7 @@ static void s_session_directive_process(dap_chain_esbocs_session_t *a_session, d s_message_send(a_session, l_type, a_directive_hash, NULL, 0, a_session->cur_round.all_validators); } -static int s_session_directive_apply(dap_chain_esbocs_directive_t *a_directive, dap_hash_fast_t *a_directive_hash) +static int s_session_directive_apply(dap_chain_esbocs_directive_t *a_directive, dap_hash_fast_t *a_directive_hash, dap_chain_esbocs_t* a_esbocs) { if (!a_directive) { log_it(L_ERROR, "Can't apply NULL directive"); @@ -2000,6 +2017,8 @@ static int s_session_directive_apply(dap_chain_esbocs_directive_t *a_directive, "KICK" : "LIFT"); return -3; } + // update last directive accept time + a_esbocs->last_directive_accept_timestamp = dap_time_now(); const char *l_penalty_group = s_get_penalty_group(l_key_addr->net_id); const char *l_directive_hash_str = dap_chain_hash_fast_to_str_new(a_directive_hash); const char *l_key_hash_str = dap_chain_hash_fast_to_str_new(&l_key_addr->data.hash_fast); @@ -2389,6 +2408,8 @@ static void s_session_packet_in(dap_chain_esbocs_session_t *a_session, dap_chain case DAP_CHAIN_ESBOCS_MSG_TYPE_SUBMIT: { uint8_t *l_candidate = l_message_data; size_t l_candidate_size = l_message_data_size; + // update last submitted candidate timestamp + l_session->esbocs->last_submitted_candidate_timestamp = dap_time_now(); // check for NULL candidate if (!l_candidate_size || dap_hash_fast_is_blank(&l_message->hdr.candidate_hash)) { debug_if(l_cs_debug, L_MSG, "net:%s, chain:%s, round:%"DAP_UINT64_FORMAT_U", attempt:%hhu." @@ -2611,6 +2632,7 @@ static void s_session_packet_in(dap_chain_esbocs_session_t *a_session, dap_chain l_session->chain->net_name, l_session->chain->name, l_session->cur_round.id, l_message->hdr.attempt_num, l_dirtective_hash_str, l_directive_size); } + l_session->esbocs->last_directive_vote_timestamp = dap_time_now(); s_session_directive_process(l_session, l_directive, &l_directive_hash); } break; @@ -2651,7 +2673,7 @@ static void s_session_packet_in(dap_chain_esbocs_session_t *a_session, dap_chain if (!l_session->cur_round.directive_applied && ++l_session->cur_round.votes_for_count * 3 >= dap_list_length(l_session->cur_round.all_validators) * 2) { - s_session_directive_apply(l_session->cur_round.directive, &l_session->cur_round.directive_hash); + s_session_directive_apply(l_session->cur_round.directive, &l_session->cur_round.directive_hash, l_session->esbocs); l_session->cur_round.directive_applied = true; s_session_state_change(l_session, DAP_CHAIN_ESBOCS_SESSION_STATE_PREVIOUS, dap_time_now()); } @@ -3011,13 +3033,15 @@ static int s_cli_esbocs(int a_argc, char **a_argv, void **a_str_reply) SUBCMD_UNDEFINED = 0, SUBCMD_MIN_VALIDATORS_COUNT, SUBCMD_CHECK_SIGNS_STRUCTURE, - SUBCMD_EMERGENCY_VALIDATOR + SUBCMD_EMERGENCY_VALIDATOR, + SUBCMD_STATUS } l_subcmd = SUBCMD_UNDEFINED; const char *l_subcmd_strs[] = { [SUBCMD_UNDEFINED] = NULL, [SUBCMD_MIN_VALIDATORS_COUNT] = "min_validators_count", [SUBCMD_CHECK_SIGNS_STRUCTURE] = "check_signs_structure", [SUBCMD_EMERGENCY_VALIDATOR] = "emergency_validators", + [SUBCMD_STATUS] = "status", }; const size_t l_subcmd_str_count = sizeof(l_subcmd_strs) / sizeof(char *); @@ -3059,7 +3083,7 @@ static int s_cli_esbocs(int a_argc, char **a_argv, void **a_str_reply) } } else if (dap_cli_server_cmd_check_option(a_argv, l_arg_index, l_arg_index + 1, "show") > 0) l_subcommand_show = true; - else + else if (l_subcmd != SUBCMD_STATUS) dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_ESBOCS_UNKNOWN,"Unrecognized subcommand '%s'", a_argv[l_arg_index]); } @@ -3162,6 +3186,90 @@ static int s_cli_esbocs(int a_argc, char **a_argv, void **a_str_reply) } } break; + case SUBCMD_STATUS: { + const char * l_net_str = NULL, *l_chain_str = NULL; + dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-net", &l_net_str); + if (!l_net_str) { + dap_json_rpc_error_add(DAP_CHAIN_NODE_CLI_COM_ESBOCS_NO_NET,"Command '%s' requires parameter -net", l_subcmd_strs[l_subcmd]); + return -DAP_CHAIN_NODE_CLI_COM_ESBOCS_NO_NET; + } + dap_cli_server_cmd_find_option_val(a_argv, l_arg_index, a_argc, "-chain", &l_chain_str); + + dap_chain_net_t *l_net = dap_chain_net_by_name(l_net_str); + if (!l_net) { + log_it(L_WARNING, "Can't find net %s", l_net_str); + return -DAP_CHAIN_NODE_CLI_COM_ESBOCS_CANT_FIND_NET; + } + + dap_chain_esbocs_session_t *l_session; + if (l_chain_str) { + DL_FOREACH(s_session_items, l_session) + if (!dap_strcmp(l_chain_str, l_session->chain->name)) + break; + if (!l_session) { + log_it(L_WARNING, "Session for net %s chain %s not found", l_net->pub.name, l_chain_str); + return -DAP_CHAIN_NODE_CLI_COM_ESBOCS_NO_SESSION; + } + } else { + DL_FOREACH(s_session_items, l_session) + if (l_session->chain->net_id.uint64 == l_net->pub.id.uint64) + break; + if (!l_session) { + log_it(L_WARNING, "Session for net %s not found", l_net->pub.name); + return -DAP_CHAIN_NODE_CLI_COM_ESBOCS_NO_SESSION; + } + } + + const char *l_penalty_group = s_get_penalty_group(l_net->pub.id); + size_t l_penalties_count = 0; + json_object * l_json_obj_banlist = json_object_new_object(); + json_object * l_json_arr_banlist = json_object_new_array(); + dap_global_db_obj_t *l_objs = dap_global_db_get_all_sync(l_penalty_group, &l_penalties_count); + for (size_t i = 0; i < l_penalties_count; i++) { + dap_chain_addr_t *l_validator_addr = dap_chain_addr_from_str(l_objs[i].key); + json_object* l_ban_validator = json_object_new_object(); + json_object_object_add(l_ban_validator, "node_addr", json_object_new_string(dap_chain_addr_to_str_static(l_validator_addr))); + json_object_array_add(l_json_arr_banlist, l_ban_validator); + } + if (!json_object_array_length(l_json_arr_banlist)) { + json_object_object_add(l_json_obj_banlist, "BANLIST", json_object_new_string("empty")); + } else { + json_object_object_add(l_json_obj_banlist, "BANLIST", l_json_arr_banlist); + } + json_object_array_add(*a_json_arr_reply, l_json_obj_banlist); + + json_object* l_json_obj_status = json_object_new_object(); + json_object_object_add(l_json_obj_status, "ban_list_count", json_object_new_int(l_penalties_count)); + json_object_object_add(l_json_obj_status, "sync_attempt", json_object_new_uint64(l_session->cur_round.sync_attempt)); + json_object_object_add(l_json_obj_status, "round_id", json_object_new_uint64(l_session->cur_round.id)); + json_object_array_add(*a_json_arr_reply, l_json_obj_status); + if (l_session->esbocs->last_submitted_candidate_timestamp) { + char l_time_buf[DAP_TIME_STR_SIZE] = {'\0'}; + dap_time_to_str_rfc822(l_time_buf, DAP_TIME_STR_SIZE, l_session->esbocs->last_submitted_candidate_timestamp); + json_object_object_add(l_json_obj_status, "last_submitted_candidate_timestamp", json_object_new_string(l_time_buf)); + } + dap_chain_datum_iter_t *l_datum_iter = l_session->chain->callback_datum_iter_create(l_session->chain); + dap_chain_datum_t * l_last_datum = l_session->chain->callback_datum_iter_get_last(l_datum_iter); + if (l_last_datum) { + char l_time_buf[DAP_TIME_STR_SIZE] = {'\0'}; + dap_time_to_str_rfc822(l_time_buf, DAP_TIME_STR_SIZE, l_last_datum->header.ts_create); + json_object_object_add(l_json_obj_status, "last_accepted_block_time", json_object_new_string(l_time_buf)); + } + l_session->chain->callback_datum_iter_delete(l_datum_iter); + + if (l_session->esbocs->last_directive_accept_timestamp) { + char l_time_buf[DAP_TIME_STR_SIZE] = {'\0'}; + dap_time_to_str_rfc822(l_time_buf, DAP_TIME_STR_SIZE, l_session->esbocs->last_directive_accept_timestamp); + json_object_object_add(l_json_obj_status, "last_directive_accept_timestamp", json_object_new_string(l_time_buf)); + } + + if (l_session->esbocs->last_directive_vote_timestamp) { + char l_time_buf[DAP_TIME_STR_SIZE] = {'\0'}; + dap_time_to_str_rfc822(l_time_buf, DAP_TIME_STR_SIZE, l_session->esbocs->last_directive_vote_timestamp); + json_object_object_add(l_json_obj_status, "last_directive_vote_timestamp", json_object_new_string(l_time_buf)); + } + } break; + default: dap_json_rpc_error_add(*a_json_arr_reply, DAP_CHAIN_NODE_CLI_COM_ESBOCS_SUB_ERR,"Unrecognized subcommand '%s'", a_argv[l_arg_index - 1]); } diff --git a/modules/consensus/esbocs/include/dap_chain_cs_esbocs.h b/modules/consensus/esbocs/include/dap_chain_cs_esbocs.h index e097d109a6114acb9a7e01ea5c6dc99e3323d675..6180596265b2fbe6cd8d139f4f6044d1ad3e7520 100644 --- a/modules/consensus/esbocs/include/dap_chain_cs_esbocs.h +++ b/modules/consensus/esbocs/include/dap_chain_cs_esbocs.h @@ -124,6 +124,9 @@ typedef struct dap_chain_esbocs { dap_chain_t *chain; dap_chain_cs_blocks_t *blocks; dap_chain_esbocs_session_t *session; + dap_time_t last_directive_vote_timestamp; + dap_time_t last_directive_accept_timestamp; + dap_time_t last_submitted_candidate_timestamp; void *_pvt; } dap_chain_esbocs_t; @@ -235,6 +238,11 @@ typedef enum s_com_esbocs_err{ DAP_CHAIN_NODE_CLI_COM_ESBOCS_HASH_FORMAT_ERR, DAP_CHAIN_NODE_CLI_COM_ESBOCS_ADD_DEL_ERR, DAP_CHAIN_NODE_CLI_COM_ESBOCS_SUB_ERR, + DAP_CHAIN_NODE_CLI_COM_ESBOCS_NO_NET, + DAP_CHAIN_NODE_CLI_COM_ESBOCS_CANT_FIND_NET, + DAP_CHAIN_NODE_CLI_COM_ESBOCS_NO_SESSION, + DAP_CHAIN_NODE_CLI_COM_ESBOCS_NO_STAKE, + DAP_CHAIN_NODE_CLI_COM_ESBOCS_WRONG_CHAIN, /* add custom codes here */