diff --git a/tl/generate/scheme/ton_api.tl b/tl/generate/scheme/ton_api.tl index cf265a79..7dc6d8ca 100644 --- a/tl/generate/scheme/ton_api.tl +++ b/tl/generate/scheme/ton_api.tl @@ -724,6 +724,10 @@ http.server.config dhs:(vector http.server.dnsEntry) local_hosts:(vector http.se ---types--- -validatorSession.stats id:tonNode.blockId round_id:int total_validators:int total_weight:long signatures:int - signatures_weight:long approve_signatures:int approve_signatures_weight:long - creator:int256 producers:(vector int256) = validatorSession.Stats; \ No newline at end of file +validatorSession.statsProducer id:int256 block_status:int block_timestamp:long = validatorSession.StatsProducer; + +validatorSession.statsRound timestamp:long producers:(vector validatorSession.statsProducer) = validatorSession.StatsRound; + +validatorSession.stats id:tonNode.blockId timestamp:long self:int256 creator:int256 total_validators:int total_weight:long + signatures:int signatures_weight:long approve_signatures:int approve_signatures_weight:long + first_round:int rounds:(vector validatorSession.statsRound) = validatorSession.Stats; diff --git a/tl/generate/scheme/ton_api.tlo b/tl/generate/scheme/ton_api.tlo index 7b87bd0e..c4961278 100644 Binary files a/tl/generate/scheme/ton_api.tlo and b/tl/generate/scheme/ton_api.tlo differ diff --git a/validator-session/validator-session-types.h b/validator-session/validator-session-types.h index 5ca6d8ba..357f6217 100644 --- a/validator-session/validator-session-types.h +++ b/validator-session/validator-session-types.h @@ -64,16 +64,30 @@ struct ValidatorSessionNode { }; struct ValidatorSessionStats { - td::uint32 round; - td::uint32 total_validators; - ValidatorWeight total_weight; - td::uint32 signatures; - ValidatorWeight signatures_weight; - td::uint32 approve_signatures; - ValidatorWeight approve_signatures_weight; + enum { status_none = 0, status_received = 1, status_rejected = 2, status_approved = 3 }; - PublicKeyHash creator; - std::vector producers; + struct Producer { + PublicKeyHash id = PublicKeyHash::zero(); + int block_status = status_none; + td::uint64 block_timestamp = 0; + }; + struct Round { + td::uint64 timestamp = 0; + std::vector producers; + }; + + td::uint32 first_round; + std::vector rounds; + + td::uint64 timestamp = 0; + PublicKeyHash self = PublicKeyHash::zero(); + PublicKeyHash creator = PublicKeyHash::zero(); + td::uint32 total_validators = 0; + ValidatorWeight total_weight = 0; + td::uint32 signatures = 0; + ValidatorWeight signatures_weight = 0; + td::uint32 approve_signatures = 0; + ValidatorWeight approve_signatures_weight = 0; }; } // namespace validatorsession diff --git a/validator-session/validator-session.cpp b/validator-session/validator-session.cpp index df5d1627..3f56e3a3 100644 --- a/validator-session/validator-session.cpp +++ b/validator-session/validator-session.cpp @@ -260,6 +260,7 @@ void ValidatorSessionImpl::process_broadcast(PublicKeyHash src, td::BufferSlice CHECK(!pending_reject_.count(block_id)); CHECK(!rejected_.count(block_id)); + stats_set_candidate_status(cur_round_, src, ValidatorSessionStats::status_received); auto v = virtual_state_->choose_blocks_to_approve(description(), local_idx()); for (auto &b : v) { if (b && SentBlock::get_block_id(b) == block_id) { @@ -331,7 +332,8 @@ void ValidatorSessionImpl::process_query(PublicKeyHash src, td::BufferSlice data } void ValidatorSessionImpl::candidate_decision_fail(td::uint32 round, ValidatorSessionCandidateId hash, - std::string result, td::BufferSlice proof) { + std::string result, td::uint32 src, td::BufferSlice proof) { + stats_set_candidate_status(round, description().get_source_id(src), ValidatorSessionStats::status_rejected); if (round != cur_round_) { return; } @@ -345,7 +347,8 @@ void ValidatorSessionImpl::candidate_decision_fail(td::uint32 round, ValidatorSe } void ValidatorSessionImpl::candidate_decision_ok(td::uint32 round, ValidatorSessionCandidateId hash, RootHash root_hash, - FileHash file_hash, td::uint32 ok_from) { + FileHash file_hash, td::uint32 src, td::uint32 ok_from) { + stats_set_candidate_status(round, description().get_source_id(src), ValidatorSessionStats::status_approved); if (round != cur_round_) { return; } @@ -512,6 +515,7 @@ void ValidatorSessionImpl::try_approve_block(const SentBlock *block) { auto P = td::PromiseCreator::lambda([round = cur_round_, hash = block_id, root_hash = block->get_root_hash(), file_hash = block->get_file_hash(), timer = std::move(timer), + src = block->get_src_idx(), SelfId = actor_id(this)](td::Result res) { if (res.is_error()) { LOG(ERROR) << "round " << round << " failed to validate candidate " << hash << ": " << res.move_as_error(); @@ -521,10 +525,10 @@ void ValidatorSessionImpl::try_approve_block(const SentBlock *block) { auto R = res.move_as_ok(); if (R.is_ok()) { td::actor::send_closure(SelfId, &ValidatorSessionImpl::candidate_decision_ok, round, hash, root_hash, - file_hash, R.ok_from()); + file_hash, src, R.ok_from()); } else { td::actor::send_closure(SelfId, &ValidatorSessionImpl::candidate_decision_fail, round, hash, R.reason(), - R.proof()); + src, R.proof()); } }); pending_approve_.insert(block_id); @@ -770,41 +774,36 @@ void ValidatorSessionImpl::on_new_round(td::uint32 round) { } auto it = blocks_[0].find(SentBlock::get_block_id(block)); - if (!block) { + bool have_block = (bool)block; + if (!have_block) { callback_->on_block_skipped(cur_round_); } else { - ValidatorSessionStats stats; - stats.round = cur_round_; - stats.total_validators = description().get_total_nodes(); - stats.total_weight = description().get_total_weight(); - stats.signatures = (td::uint32)export_sigs.size(); - stats.signatures_weight = signatures_weight; - stats.approve_signatures = (td::uint32)export_approve_sigs.size(); - stats.approve_signatures_weight = approve_signatures_weight; - stats.creator = description().get_source_id(block->get_src_idx()); - stats.producers.resize(description().get_max_priority() + 1, PublicKeyHash::zero()); - for (td::uint32 i = 0; i < description().get_total_nodes(); i++) { - td::int32 priority = description().get_node_priority(i, cur_round_); - if (priority >= 0) { - CHECK((size_t)priority < stats.producers.size()); - stats.producers[priority] = description().get_source_id(i); - } - } - while (!stats.producers.empty() && stats.producers.back().is_zero()) { - stats.producers.pop_back(); - } + cur_stats_.timestamp = (td::uint64)td::Clocks::system(); + cur_stats_.total_validators = description().get_total_nodes(); + cur_stats_.total_weight = description().get_total_weight(); + cur_stats_.signatures = (td::uint32)export_sigs.size(); + cur_stats_.signatures_weight = signatures_weight; + cur_stats_.approve_signatures = (td::uint32)export_approve_sigs.size(); + cur_stats_.approve_signatures_weight = approve_signatures_weight; + cur_stats_.creator = description().get_source_id(block->get_src_idx()); + cur_stats_.self = description().get_source_id(local_idx()); if (it == blocks_[0].end()) { callback_->on_block_committed(cur_round_, description().get_source_public_key(block->get_src_idx()), block->get_root_hash(), block->get_file_hash(), td::BufferSlice(), - std::move(export_sigs), std::move(export_approve_sigs), std::move(stats)); + std::move(export_sigs), std::move(export_approve_sigs), std::move(cur_stats_)); } else { callback_->on_block_committed(cur_round_, description().get_source_public_key(block->get_src_idx()), block->get_root_hash(), block->get_file_hash(), it->second->data_.clone(), - std::move(export_sigs), std::move(export_approve_sigs), std::move(stats)); + std::move(export_sigs), std::move(export_approve_sigs), std::move(cur_stats_)); } } cur_round_++; + if (have_block) { + stats_init(); + } else { + stats_add_round(); + } for (size_t i = 0; i < blocks_.size() - 1; i++) { blocks_[i] = std::move(blocks_[i + 1]); } @@ -895,6 +894,47 @@ void ValidatorSessionImpl::start_up() { check_all(); td::actor::send_closure(rldp_, &rldp::Rldp::add_id, description().get_source_adnl_id(local_idx())); + + stats_init(); +} + +void ValidatorSessionImpl::stats_init() { + cur_stats_ = ValidatorSessionStats(); + cur_stats_.first_round = cur_round_; + stats_add_round(); +} + +void ValidatorSessionImpl::stats_add_round() { + cur_stats_.rounds.emplace_back(); + auto& round = cur_stats_.rounds.back(); + round.timestamp = (td::uint64)td::Clocks::system(); + round.producers.resize(description().get_max_priority() + 1); + for (td::uint32 i = 0; i < description().get_total_nodes(); i++) { + td::int32 priority = description().get_node_priority(i, cur_round_); + if (priority >= 0) { + CHECK((size_t)priority < round.producers.size()); + round.producers[priority].id = description().get_source_id(i); + } + } + while (!round.producers.empty() && round.producers.back().id.is_zero()) { + round.producers.pop_back(); + } +} + +void ValidatorSessionImpl::stats_set_candidate_status(td::uint32 round, PublicKeyHash src, int status) { + if (round < cur_stats_.first_round || round - cur_stats_.first_round >= cur_stats_.rounds.size()) { + return; + } + auto& stats_round = cur_stats_.rounds[round - cur_stats_.first_round]; + auto it = std::find_if(stats_round.producers.begin(), stats_round.producers.end(), + [&](const ValidatorSessionStats::Producer& p) { return p.id == src; }); + if (it == stats_round.producers.end()) { + return; + } + if (it->block_status == ValidatorSessionStats::status_none) { + it->block_timestamp = (td::uint64)td::Clocks::system(); + } + it->block_status = status; } td::actor::ActorOwn ValidatorSession::create( diff --git a/validator-session/validator-session.hpp b/validator-session/validator-session.hpp index 9b7f9508..54bba33d 100644 --- a/validator-session/validator-session.hpp +++ b/validator-session/validator-session.hpp @@ -154,6 +154,11 @@ class ValidatorSessionImpl : public ValidatorSession { bool catchain_started_ = false; bool allow_unsafe_self_blocks_resync_; + ValidatorSessionStats cur_stats_; + void stats_init(); + void stats_add_round(); + void stats_set_candidate_status(td::uint32 round, PublicKeyHash src, int status); + public: ValidatorSessionImpl(catchain::CatChainSessionId session_id, ValidatorSessionOptions opts, PublicKeyHash local_id, std::vector nodes, std::unique_ptr callback, @@ -177,9 +182,9 @@ class ValidatorSessionImpl : public ValidatorSession { void try_sign(); void candidate_decision_fail(td::uint32 round, ValidatorSessionCandidateId hash, std::string result, - td::BufferSlice proof); + td::uint32 src, td::BufferSlice proof); void candidate_decision_ok(td::uint32 round, ValidatorSessionCandidateId hash, RootHash root_hash, FileHash file_hash, - td::uint32 ok_from); + td::uint32 src, td::uint32 ok_from); void candidate_approved_signed(td::uint32 round, ValidatorSessionCandidateId hash, td::uint32 ok_from, td::BufferSlice signature); diff --git a/validator/manager.cpp b/validator/manager.cpp index de9e9a02..d6a9b4dd 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -2523,14 +2523,22 @@ void ValidatorManagerImpl::log_validator_session_stats(BlockIdExt block_id, if (fname.empty()) { return; } - std::vector producers; - for (const PublicKeyHash& id : stats.producers) { - producers.push_back(id.bits256_value()); + + std::vector> rounds; + for (const auto& round : stats.rounds) { + std::vector> producers; + for (const auto& producer : round.producers) { + producers.push_back(create_tl_object( + producer.id.bits256_value(), producer.block_status, producer.block_timestamp)); + } + rounds.push_back(create_tl_object(round.timestamp, std::move(producers))); } + auto obj = create_tl_object( - create_tl_block_id_simple(block_id.id), stats.round, stats.total_validators, stats.total_weight, - stats.signatures, stats.signatures_weight, stats.approve_signatures, stats.approve_signatures_weight, - stats.creator.bits256_value(), std::move(producers)); + create_tl_block_id_simple(block_id.id), stats.timestamp, stats.self.bits256_value(), + stats.creator.bits256_value(), stats.total_validators, stats.total_weight, stats.signatures, + stats.signatures_weight, stats.approve_signatures, stats.approve_signatures_weight, stats.first_round, + std::move(rounds)); std::string s = td::json_encode(td::ToJson(*obj.get()), false); s.erase(std::remove_if(s.begin(), s.end(), [](char c) { return c == '\n' || c == '\r'; }), s.end());