/* This file is part of TON Blockchain Library. TON Blockchain Library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. TON Blockchain Library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . Copyright 2017-2019 Telegram Systems LLP */ #include "validator-session-state.h" #include "td/utils/overloaded.h" #include "td/utils/Random.h" #include "auto/tl/ton_api.hpp" #include namespace td { td::StringBuilder& operator<<(td::StringBuilder& sb, const ton::ton_api::validatorSession_round_Message& message) { ton::ton_api::downcast_call( const_cast(message), td::overloaded( [&](const ton::ton_api::validatorSession_message_submittedBlock& obj) { sb << "SUBMIT(" << obj.round_ << "," << obj.root_hash_ << "," << obj.file_hash_ << obj.collated_data_file_hash_ << ")"; }, [&](const ton::ton_api::validatorSession_message_approvedBlock& obj) { sb << "APPROVE(" << obj.round_ << "," << obj.candidate_ << ")"; }, [&](const ton::ton_api::validatorSession_message_rejectedBlock& obj) { sb << "COMMIT(" << obj.round_ << "," << obj.candidate_ << ")"; }, [&](const ton::ton_api::validatorSession_message_commit& obj) { sb << "COMMIT(" << obj.round_ << "," << obj.candidate_ << ")"; }, [&](const ton::ton_api::validatorSession_message_vote& obj) { sb << "VOTE(" << obj.round_ << "," << obj.attempt_ << "," << obj.candidate_ << ")"; }, [&](const ton::ton_api::validatorSession_message_voteFor& obj) { sb << "VOTEFOR(" << obj.round_ << "," << obj.attempt_ << "," << obj.candidate_ << ")"; }, [&](const ton::ton_api::validatorSession_message_precommit& obj) { sb << "PRECOMMIT(" << obj.round_ << "," << obj.attempt_ << "," << obj.candidate_ << ")"; }, [&](const ton::ton_api::validatorSession_message_empty& obj) { sb << "EMPTY(" << obj.round_ << "," << obj.attempt_ << ")"; })); return sb; } td::StringBuilder& operator<<(td::StringBuilder& sb, const ton::ton_api::validatorSession_round_Message* message) { return sb << *message; } } // namespace td namespace ton { namespace validatorsession { static td::uint32 get_round_id(const ton_api::validatorSession_round_Message* message) { td::uint32 round = 0; bool is_called = ton_api::downcast_call(*const_cast(message), [&](auto& obj) { round = obj.round_; }); CHECK(is_called); return round; } static const ValidatorSessionRoundAttemptState* get_attempt(const AttemptVector* vec, td::uint32 seqno) { if (!vec) { return nullptr; } td::int32 l = -1; td::int32 r = vec->size(); while (r - l > 1) { auto x = (r + l) / 2; if (vec->at(x)->get_seqno() < seqno) { l = x; } else if (vec->at(x)->get_seqno() > seqno) { r = x; } else { return vec->at(x); } } return nullptr; } static const SessionBlockCandidate* get_candidate(const ApproveVector* vec, ValidatorSessionCandidateId id) { if (!vec) { return nullptr; } auto size = vec->size(); auto v = vec->data(); for (td::uint32 i = 0; i < size; i++) { if (v[i]->get_id() == id) { return v[i]; } } return nullptr; } static const SessionVoteCandidate* get_candidate(const VoteVector* vec, ValidatorSessionCandidateId id) { if (!vec) { return nullptr; } auto size = vec->size(); auto v = vec->data(); for (td::uint32 i = 0; i < size; i++) { if (v[i]->get_id() == id) { return v[i]; } } return nullptr; } // // // SessionBlockCandidateSignature // // const SessionBlockCandidateSignature* SessionBlockCandidateSignature::merge(ValidatorSessionDescription& desc, const SessionBlockCandidateSignature* l, const SessionBlockCandidateSignature* r) { if (!l) { return r; } if (!r) { return l; } if (l == r) { return l; } if (l->as_slice() < r->as_slice()) { return l; } else { return r; } } // // // SessionBlockCandidate // // bool SessionBlockCandidate::check_block_is_approved(ValidatorSessionDescription& desc) const { ValidatorWeight w = 0; for (td::uint32 i = 0; i < desc.get_total_nodes(); i++) { if (approved_by_->at(i)) { w += desc.get_node_weight(i); if (w >= desc.get_cutoff_weight()) { return true; } } } return false; } const SessionBlockCandidate* SessionBlockCandidate::merge(ValidatorSessionDescription& desc, const SessionBlockCandidate* l, const SessionBlockCandidate* r) { if (!l) { return r; } if (!r) { return l; } if (l == r) { return l; } CHECK(l->get_id() == r->get_id()); auto v = SessionBlockCandidateSignatureVector::merge( desc, l->approved_by_, r->approved_by_, [&](const SessionBlockCandidateSignature* l, const SessionBlockCandidateSignature* r) { return SessionBlockCandidateSignature::merge(desc, l, r); }); return SessionBlockCandidate::create(desc, l->block_, std::move(v)); } // // // SessionVoteCandidate // // bool SessionVoteCandidate::check_block_is_voted(ValidatorSessionDescription& desc) const { ValidatorWeight w = 0; for (td::uint32 i = 0; i < desc.get_total_nodes(); i++) { if (voted_by_->at(i)) { w += desc.get_node_weight(i); if (w >= desc.get_cutoff_weight()) { return true; } } } return false; } const SessionVoteCandidate* SessionVoteCandidate::merge(ValidatorSessionDescription& desc, const SessionVoteCandidate* l, const SessionVoteCandidate* r) { if (!l) { return r; } if (!r) { return l; } if (l == r) { return l; } CHECK(l->get_id() == r->get_id()); auto v = CntVector::merge(desc, l->voted_by_, r->voted_by_); return SessionVoteCandidate::create(desc, l->block_, std::move(v)); } // // // OLD ROUND STATE // // const ValidatorSessionOldRoundState* ValidatorSessionOldRoundState::create(ValidatorSessionDescription& desc, const ValidatorSessionRoundState* round) { bool found; auto B = round->get_precommitted_block(found); CHECK(found); CHECK(round->check_block_is_signed(desc)); auto E = round->get_block(SentBlock::get_block_id(B)); CHECK(E); return create(desc, round->get_seqno(), B, round->get_signatures(), E->get_approvers_list()); } const ValidatorSessionOldRoundState* ValidatorSessionOldRoundState::merge(ValidatorSessionDescription& desc, const ValidatorSessionOldRoundState* left, const ValidatorSessionOldRoundState* right) { if (!left) { return right; } if (!right) { return left; } if (left == right) { return left; } CHECK(left->seqno_ == right->seqno_); auto signs = SessionBlockCandidateSignatureVector::merge( desc, left->signatures_, right->signatures_, [&](const SessionBlockCandidateSignature* l, const SessionBlockCandidateSignature* r) { return SessionBlockCandidateSignature::merge(desc, l, r); }); auto approve_signs = SessionBlockCandidateSignatureVector::merge( desc, left->approve_signatures_, right->approve_signatures_, [&](const SessionBlockCandidateSignature* l, const SessionBlockCandidateSignature* r) { return SessionBlockCandidateSignature::merge(desc, l, r); }); return ValidatorSessionOldRoundState::create(desc, left->seqno_, left->block_, std::move(signs), std::move(approve_signs)); } const ValidatorSessionOldRoundState* ValidatorSessionOldRoundState::merge(ValidatorSessionDescription& desc, const ValidatorSessionOldRoundState* left, const ValidatorSessionRoundState* right) { CHECK(left->seqno_ == right->get_seqno()); auto signs = SessionBlockCandidateSignatureVector::merge( desc, left->signatures_, right->get_signatures(), [&](const SessionBlockCandidateSignature* l, const SessionBlockCandidateSignature* r) { return SessionBlockCandidateSignature::merge(desc, l, r); }); auto C = right->get_block(left->get_block_id()); auto approve_signs = C ? SessionBlockCandidateSignatureVector::merge( desc, left->approve_signatures_, C->get_approvers_list(), [&](const SessionBlockCandidateSignature* l, const SessionBlockCandidateSignature* r) { return SessionBlockCandidateSignature::merge(desc, l, r); }) : left->approve_signatures_; return ValidatorSessionOldRoundState::create(desc, left->seqno_, left->block_, std::move(signs), std::move(approve_signs)); } const ValidatorSessionOldRoundState* ValidatorSessionOldRoundState::action( ValidatorSessionDescription& desc, const ValidatorSessionOldRoundState* state, td::uint32 src_idx, td::uint32 att, const ton_api::validatorSession_message_approvedBlock& act) { if (act.candidate_ != state->get_block_id()) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: approved not committed block in old round. Ignoring"; return state; } if (state->approve_signatures_->at(src_idx)) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: double approve. Ignoring"; return state; } if (act.candidate_ == skip_round_candidate_id()) { if (act.signature_.size() != 0) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: bad signature in APPROVE. Ignoring"; return state; } } else { auto S = desc.check_approve_signature(state->get_block()->get_root_hash(), state->get_block()->get_file_hash(), src_idx, act.signature_.as_slice()); if (S.is_error()) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: bad signature in APPROVE. Ignoring: " << S; return state; } } auto sig = SessionBlockCandidateSignature::create(desc, act.signature_.clone()); auto approve = SessionBlockCandidateSignatureVector::change(desc, state->approve_signatures_, src_idx, sig); return ValidatorSessionOldRoundState::create(desc, state->seqno_, state->get_block(), state->signatures_, std::move(approve)); } const ValidatorSessionOldRoundState* ValidatorSessionOldRoundState::action( ValidatorSessionDescription& desc, const ValidatorSessionOldRoundState* state, td::uint32 src_idx, td::uint32 att, const ton_api::validatorSession_message_commit& act) { auto block_id = state->get_block_id(); if (act.candidate_ != block_id) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: signed wrong block " << "should be " << block_id; return state; } if (act.candidate_ == skip_round_candidate_id()) { if (act.signature_.size() != 0) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid skip block signature"; return state; } } else { auto S = desc.check_signature(state->get_block()->get_root_hash(), state->get_block()->get_file_hash(), src_idx, act.signature_.as_slice()); if (S.is_error()) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: bad signature: " << S.move_as_error(); return state; } } if (state->check_block_is_signed_by(src_idx)) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: duplicate signature"; return state; } auto signs = SessionBlockCandidateSignatureVector::change( desc, state->get_signatures(), src_idx, SessionBlockCandidateSignature::create(desc, act.signature_.clone())); return ValidatorSessionOldRoundState::create(desc, state->seqno_, state->get_block(), std::move(signs), state->approve_signatures_); } const ValidatorSessionOldRoundState* ValidatorSessionOldRoundState::action( ValidatorSessionDescription& desc, const ValidatorSessionOldRoundState* state, td::uint32 src_idx, td::uint32 att, const ton_api::validatorSession_round_Message* act) { ton_api::downcast_call(*const_cast(act), [&](const auto& obj) { state = action(desc, state, src_idx, att, obj); }); return state; } // // // ATTEMPT STATE // // const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::merge( ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* left, const ValidatorSessionRoundAttemptState* right) { if (!left) { return right; } if (!right) { return left; } if (left == right) { return left; } CHECK(left->seqno_ == right->seqno_); const SentBlock* vote_for = nullptr; bool vote_for_inited = false; if (!left->vote_for_inited_) { vote_for = right->vote_for_; vote_for_inited = right->vote_for_inited_; } else if (!right->vote_for_inited_) { vote_for = left->vote_for_; vote_for_inited = left->vote_for_inited_; } else if (left->vote_for_ == right->vote_for_) { vote_for_inited = true; vote_for = left->vote_for_; } else { auto l = SentBlock::get_block_id(left->vote_for_); auto r = SentBlock::get_block_id(right->vote_for_); vote_for_inited = true; if (l < r) { vote_for = left->vote_for_; } else { vote_for = right->vote_for_; } } auto precommitted = CntVector::merge(desc, left->precommitted_, right->precommitted_); auto votes = VoteVector::merge(desc, left->votes_, right->votes_, [&](const SessionVoteCandidate* l, const SessionVoteCandidate* r) { return SessionVoteCandidate::merge(desc, l, r); }); return ValidatorSessionRoundAttemptState::create(desc, left->seqno_, std::move(votes), std::move(precommitted), vote_for, vote_for_inited); } const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::action( ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, td::uint32 att, const ton_api::validatorSession_message_voteFor& act, const ValidatorSessionRoundState* round) { if (state->vote_for_inited_) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: duplicate VOTEFOR"; return state; } if (src_idx != desc.get_vote_for_author(att)) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: bad VOTEFOR author"; return state; } if (round->get_first_attempt(src_idx) == 0 && desc.opts().max_round_attempts > 0) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: too early for VOTEFOR"; return state; } if (round->get_first_attempt(src_idx) + desc.opts().max_round_attempts > att && desc.opts().max_round_attempts == 0) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: too early for VOTEFOR"; return state; } auto x = round->get_block(act.candidate_); if (!x) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: VOTEFOR for not approved block"; return state; } if (!x->check_block_is_approved(desc)) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: VOTEFOR for not approved block"; return state; } return ValidatorSessionRoundAttemptState::create(desc, state->seqno_, state->votes_, state->precommitted_, x->get_block(), true); } const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::action( ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, td::uint32 att, const ton_api::validatorSession_message_vote& act, const ValidatorSessionRoundState* round) { bool made; return make_one(desc, state, src_idx, att, round, &act, made); } const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::action( ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, td::uint32 att, const ton_api::validatorSession_message_precommit& act, const ValidatorSessionRoundState* round) { bool made; return make_one(desc, state, src_idx, att, round, &act, made); } const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::action( ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, td::uint32 att, const ton_api::validatorSession_message_empty& act, const ValidatorSessionRoundState* round) { bool made; return make_one(desc, state, src_idx, att, round, &act, made); } const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::try_vote( ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, td::uint32 att, const ValidatorSessionRoundState* round, const ton_api::validatorSession_round_Message* act, bool& made) { made = false; if (state->check_vote_received_from(src_idx)) { return state; } auto found = false; auto block = round->choose_block_to_vote(desc, src_idx, att, state->vote_for_, state->vote_for_inited_, found); if (!found) { return state; } auto block_id = SentBlock::get_block_id(block); made = true; if (act) { if (act->get_id() != ton_api::validatorSession_message_vote::ID) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: expected VOTE(" << block_id << ")"; } else { auto x = static_cast(act); if (x->candidate_ != block_id) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: expected VOTE(" << block_id << ")"; } } } else { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "]: making implicit VOTE(" << block_id << ")"; } auto candidate = get_candidate(state->votes_, block_id); if (!candidate) { candidate = SessionVoteCandidate::create(desc, block); } candidate = SessionVoteCandidate::push(desc, candidate, src_idx); auto v = VoteVector::push(desc, state->votes_, candidate); return ValidatorSessionRoundAttemptState::create(desc, state->seqno_, std::move(v), state->precommitted_, state->vote_for_, state->vote_for_inited_); } const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::try_precommit( ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, td::uint32 att, const ValidatorSessionRoundState* round, const ton_api::validatorSession_round_Message* act, bool& made) { made = false; if (state->check_precommit_received_from(src_idx)) { return state; } bool found; auto block = state->get_voted_block(desc, found); if (!found) { return state; } made = true; auto block_id = SentBlock::get_block_id(block); if (act) { if (act->get_id() != ton_api::validatorSession_message_precommit::ID) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: expected PRECOMMIT(" << block_id << ")"; } else { auto x = static_cast(act); if (x->candidate_ != block_id) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: expected PRECOMMIT(" << block_id << ")"; } } } else { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "]: making implicit PRECOMMIT(" << block_id << ")"; } auto v = CntVector::change(desc, state->precommitted_, src_idx, true); return ValidatorSessionRoundAttemptState::create(desc, state->seqno_, state->votes_, std::move(v), state->vote_for_, state->vote_for_inited_); } const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::make_one( ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, td::uint32 att, const ValidatorSessionRoundState* round, const ton_api::validatorSession_round_Message* act, bool& made) { made = false; state = try_vote(desc, state, src_idx, att, round, act, made); if (made) { return state; } state = try_precommit(desc, state, src_idx, att, round, act, made); if (made) { return state; } if (act && act->get_id() != ton_api::validatorSession_message_empty::ID) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: expected EMPTY"; } return state; } const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::action( ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, td::uint32 att, const ton_api::validatorSession_round_Message* act, const ValidatorSessionRoundState* round) { ton_api::downcast_call(*const_cast(act), [&](auto& obj) { state = action(desc, state, src_idx, att, obj, round); }); return state; } bool ValidatorSessionRoundAttemptState::check_vote_received_from(td::uint32 src_idx) const { if (!votes_) { return false; } auto size = votes_->size(); auto v = votes_->data(); for (td::uint32 i = 0; i < size; i++) { if (v[i]->check_block_is_voted_by(src_idx)) { return true; } } return false; } bool ValidatorSessionRoundAttemptState::check_precommit_received_from(td::uint32 src_idx) const { return precommitted_->at(src_idx); } const SentBlock* ValidatorSessionRoundAttemptState::get_voted_block(ValidatorSessionDescription& desc, bool& f) const { f = false; if (!votes_) { return nullptr; } auto size = votes_->size(); auto v = votes_->data(); for (td::uint32 i = 0; i < size; i++) { if (v[i]->check_block_is_voted(desc)) { f = true; return v[i]->get_block(); } } return nullptr; } bool ValidatorSessionRoundAttemptState::check_attempt_is_precommitted(ValidatorSessionDescription& desc) const { ValidatorWeight weight = 0; for (td::uint32 i = 0; i < desc.get_total_nodes(); i++) { if (precommitted_->at(i)) { weight += desc.get_node_weight(i); if (weight >= desc.get_cutoff_weight()) { return true; } } } return false; } tl_object_ptr ValidatorSessionRoundAttemptState::create_action( ValidatorSessionDescription& desc, const ValidatorSessionRoundState* round, td::uint32 src_idx, td::uint32 att) const { if (!check_vote_received_from(src_idx)) { auto found = false; auto B = round->choose_block_to_vote(desc, src_idx, att, vote_for_, vote_for_inited_, found); if (found) { auto block_id = SentBlock::get_block_id(B); return create_tl_object(round->get_seqno(), seqno_, block_id); } } if (!check_precommit_received_from(src_idx)) { bool f = false; auto B = get_voted_block(desc, f); if (f) { auto block_id = SentBlock::get_block_id(B); return create_tl_object(round->get_seqno(), seqno_, block_id); } } return create_tl_object(round->get_seqno(), seqno_); } void ValidatorSessionRoundAttemptState::dump(ValidatorSessionDescription& desc, td::StringBuilder& sb) const { sb << "attempt=" << seqno_ << "\n"; sb << ">>>>\n"; if (vote_for_inited_) { sb << "vote_for=" << (vote_for_ ? vote_for_->get_src_idx() : std::numeric_limits::max()) << "\n"; } else { sb << "vote_for=NONE\n"; } if (votes_) { auto s = votes_->size(); sb << "votes: "; std::vector R; R.resize(desc.get_total_nodes(), -1); for (td::uint32 i = 0; i < s; i++) { const auto e = votes_->at(i); const auto& x = e->get_voters_list(); for (td::uint32 j = 0; j < desc.get_total_nodes(); j++) { if (x->at(j)) { R[j] = e->get_src_idx(); } } } for (td::uint32 i = 0; i < desc.get_total_nodes(); i++) { sb << R[i] << " "; } sb << "\n"; } else { sb << "votes: EMPTY\n"; } sb << "precommits: "; for (td::uint32 i = 0; i < desc.get_total_nodes(); i++) { const auto e = precommitted_->at(i); if (e) { sb << "+ "; } else { sb << "- "; } } sb << "\n"; sb << "<<<<\n"; } // // // ROUND STATE // // const ValidatorSessionRoundState* ValidatorSessionRoundState::merge(ValidatorSessionDescription& desc, const ValidatorSessionRoundState* left, const ValidatorSessionRoundState* right) { if (!left) { return right; } if (!right) { return left; } if (left == right) { return left; } CHECK(left->seqno_ == right->seqno_); if (left->precommitted_ && right->precommitted_) { CHECK(SentBlock::get_block_id(left->precommitted_block_) == SentBlock::get_block_id(right->precommitted_block_)); } const SentBlock* precommitted_block = left->precommitted_block_ ? left->precommitted_block_ : right->precommitted_block_; bool precommitted = left->precommitted_ || right->precommitted_; auto first_attempt = CntVector::merge(desc, left->first_attempt_, right->first_attempt_, [&](td::uint32 l, td::uint32 r) { if (l == 0) { return r; } if (r == 0) { return l; } return std::min(l, r); }); auto att_vec = AttemptVector::merge(desc, left->attempts_, right->attempts_, [&](const ValidatorSessionRoundAttemptState* l, const ValidatorSessionRoundAttemptState* r) { return ValidatorSessionRoundAttemptState::merge(desc, l, r); }); const SentBlock* f[2]; td::uint32 f_att[2] = {}; td::uint32 f_cnt = 0; for (td::uint32 i = att_vec->size(); i > 0; i--) { auto b = att_vec->at(i - 1); if (f_cnt <= 1) { bool found; auto B = b->get_voted_block(desc, found); if (found) { if (f_cnt == 1) { auto l = SentBlock::get_block_id(f[0]); auto r = SentBlock::get_block_id(B); if (l == r) { found = false; } } if (found) { f[f_cnt] = B; f_att[f_cnt] = b->get_seqno(); f_cnt++; } } } if (!precommitted && b->check_attempt_is_precommitted(desc)) { precommitted = true; bool found; precommitted_block = b->get_voted_block(desc, found); CHECK(found); } if (precommitted && f_cnt == 2) { break; } } if (f_cnt >= 1) { CHECK(f_att[0] > f_att[1]); } auto last_precommit = CntVector::merge( desc, left->last_precommit_, right->last_precommit_, [&](td::uint32 l, td::uint32 r) -> td::uint32 { auto x = std::max(l, r); if (f_cnt == 0) { CHECK(x == 0); return x; } if (x > f_att[1]) { return x; } else { return 0; } }, true); auto signs = CntVector::merge( desc, left->signatures_, right->signatures_, [&](const SessionBlockCandidateSignature* l, const SessionBlockCandidateSignature* r) { return SessionBlockCandidateSignature::merge(desc, l, r); }); //auto sent_vec = SentBlockVector::merge(desc, left->sent_blocks_, right->sent_blocks_); auto sent = ApproveVector::merge(desc, left->sent_blocks_, right->sent_blocks_, [&](const SessionBlockCandidate* l, const SessionBlockCandidate* r) { return SessionBlockCandidate::merge(desc, l, r); }); return ValidatorSessionRoundState::create(desc, precommitted_block, left->seqno_, precommitted, std::move(first_attempt), last_precommit, std::move(sent), std::move(signs), std::move(att_vec)); } const ValidatorSessionRoundState* ValidatorSessionRoundState::action( ValidatorSessionDescription& desc, const ValidatorSessionRoundState* state, td::uint32 src_idx, td::uint32 att, const ton_api::validatorSession_message_submittedBlock& act) { if (desc.get_node_priority(src_idx, state->seqno_) < 0) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: cannot propose blocks on this round"; return state; } if (state->check_block_is_sent_by(src_idx)) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: duplicate block propose"; return state; } auto b = SentBlock::create(desc, src_idx, act.root_hash_, act.file_hash_, act.collated_data_file_hash_); auto a = SessionBlockCandidate::create(desc, b); auto sent = ApproveVector::push(desc, state->sent_blocks_, a); return ValidatorSessionRoundState::create(desc, state->precommitted_block_, state->seqno_, state->precommitted_, state->first_attempt_, state->last_precommit_, sent, state->signatures_, state->attempts_); } const ValidatorSessionRoundState* ValidatorSessionRoundState::action( ValidatorSessionDescription& desc, const ValidatorSessionRoundState* state, td::uint32 src_idx, td::uint32 att, const ton_api::validatorSession_message_approvedBlock& act) { auto sent_block = state->get_block(act.candidate_); if (act.candidate_ != skip_round_candidate_id() && !sent_block) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: approved unknown block " << act.candidate_; return state; } if (sent_block && sent_block->check_block_is_approved_by(src_idx)) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: duplicate block " << act.candidate_ << " approve"; return state; } if (act.candidate_ != skip_round_candidate_id()) { auto v = state->sent_blocks_->data(); auto s = state->sent_blocks_->size(); for (td::uint32 i = 0; i < s; i++) { auto B = v[i]; if (B->get_src_idx() == sent_block->get_src_idx() && B->check_block_is_approved_by(src_idx)) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: approved block, but already approved another from this node"; return state; } } } if (act.candidate_ == skip_round_candidate_id()) { if (act.signature_.size() != 0) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: bad signature in APPROVE. Ignoring"; return state; } } else { auto S = desc.check_approve_signature(sent_block->get_block()->get_root_hash(), sent_block->get_block()->get_file_hash(), src_idx, act.signature_.as_slice()); if (S.is_error()) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: bad signature in APPROVE. Ignoring: " << S; return state; } } if (!sent_block) { CHECK(act.candidate_ == skip_round_candidate_id()); sent_block = SessionBlockCandidate::create(desc, nullptr); } sent_block = SessionBlockCandidate::push(desc, sent_block, src_idx, SessionBlockCandidateSignature::create(desc, act.signature_.clone())); auto v = ApproveVector::push(desc, state->sent_blocks_, sent_block); return ValidatorSessionRoundState::create(desc, state->precommitted_block_, state->seqno_, state->precommitted_, state->first_attempt_, state->last_precommit_, std::move(v), state->signatures_, state->attempts_); } const ValidatorSessionRoundState* ValidatorSessionRoundState::action( ValidatorSessionDescription& desc, const ValidatorSessionRoundState* state, td::uint32 src_idx, td::uint32 att, const ton_api::validatorSession_message_rejectedBlock& act) { LOG(ERROR) << "VALIDATOR SESSION: NODE " << desc.get_source_id(src_idx) << " REJECTED CANDIDATE " << act.candidate_ << " WITH REASON " << act.reason_.as_slice(); return state; } const ValidatorSessionRoundState* ValidatorSessionRoundState::action( ValidatorSessionDescription& desc, const ValidatorSessionRoundState* state, td::uint32 src_idx, td::uint32 att, const ton_api::validatorSession_message_commit& act) { if (!state->precommitted_) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: committing not precommitted block"; return state; } auto block_id = SentBlock::get_block_id(state->precommitted_block_); if (block_id != act.candidate_) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: committing wrong block " << act.candidate_; return state; } if (state->signatures_->at(src_idx)) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: duplicate signature"; return state; } if (act.candidate_ == skip_round_candidate_id()) { if (act.signature_.size() != 0) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: bad signature in COMMIT. Ignoring"; return state; } } else { auto S = desc.check_signature(state->precommitted_block_->get_root_hash(), state->precommitted_block_->get_file_hash(), src_idx, act.signature_.as_slice()); if (S.is_error()) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act << "]: invalid message: bad signature: " << S.move_as_error(); return state; } } auto s = CntVector::change( desc, state->signatures_, src_idx, SessionBlockCandidateSignature::create(desc, act.signature_.clone())); return ValidatorSessionRoundState::create(desc, state->precommitted_block_, state->seqno_, state->precommitted_, state->first_attempt_, state->last_precommit_, state->sent_blocks_, std::move(s), state->attempts_); } const ValidatorSessionRoundState* ValidatorSessionRoundState::forward_action_to_attempt( ValidatorSessionDescription& desc, const ValidatorSessionRoundState* state, td::uint32 src_idx, td::uint32 att, const ton_api::validatorSession_round_Message* act) { if (state->precommitted_) { if (act->get_id() != ton_api::validatorSession_message_empty::ID) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << *act << "]: invalid message in precommitted round: expected EMPTY"; } return state; } auto attempt = get_attempt(state->attempts_, att); if (!attempt) { attempt = ValidatorSessionRoundAttemptState::create(desc, att); } bool had_voted_block; attempt->get_voted_block(desc, had_voted_block); ton_api::downcast_call(*const_cast(act), [&](auto& obj) { attempt = ValidatorSessionRoundAttemptState::action(desc, attempt, src_idx, att, obj, state); }); bool has_voted_block; auto block = attempt->get_voted_block(desc, has_voted_block); auto precommitted = state->precommitted_; auto precommitted_block = state->precommitted_block_; if (!precommitted && attempt->check_attempt_is_precommitted(desc)) { precommitted = true; CHECK(has_voted_block); precommitted_block = block; } auto last_precommit = state->last_precommit_; td::uint32 e = 0; for (td::uint32 i = 0; i < last_precommit->size(); i++) { e = std::max(e, last_precommit->at(i)); } bool eq = true; if (e && has_voted_block) { auto attempt = get_attempt(state->attempts_, e); CHECK(attempt); bool f; auto B = attempt->get_voted_block(desc, f); CHECK(f); auto l = SentBlock::get_block_id(block); auto r = SentBlock::get_block_id(B); eq = (l == r); if (!eq) { last_precommit = CntVector::modify(desc, last_precommit, [&](td::uint32 a) -> td::uint32 { if (a > att) { return a; } else { return 0; } }); } } if (attempt->check_precommit_received_from(src_idx) && last_precommit->at(src_idx) < att && (att > e || eq)) { CHECK(has_voted_block); last_precommit = CntVector::change(desc, last_precommit, src_idx, att); } auto vec = AttemptVector::push(desc, state->attempts_, std::move(attempt)); return ValidatorSessionRoundState::create(desc, precommitted_block, state->seqno_, precommitted, state->first_attempt_, last_precommit, state->sent_blocks_, state->signatures_, std::move(vec)); } const ValidatorSessionRoundState* ValidatorSessionRoundState::action( ValidatorSessionDescription& desc, const ValidatorSessionRoundState* state, td::uint32 src_idx, td::uint32 att, const ton_api::validatorSession_round_Message* act) { auto first_attempt = state->first_attempt_; if (first_attempt->at(src_idx) == 0 || first_attempt->at(src_idx) > att) { first_attempt = CntVector::change(desc, first_attempt, src_idx, att); state = ValidatorSessionRoundState::create(desc, state->precommitted_block_, state->seqno_, state->precommitted_, first_attempt, state->last_precommit_, state->sent_blocks_, state->signatures_, state->attempts_); } ton_api::downcast_call(*const_cast(act), [&](auto& obj) { state = action(desc, state, src_idx, att, obj); }); return state; } bool ValidatorSessionRoundState::check_block_is_sent_by(td::uint32 src_idx) const { if (!sent_blocks_) { return false; } auto v = sent_blocks_->data(); auto size = sent_blocks_->size(); for (td::uint32 i = 0; i < size; i++) { auto B = v[i]->get_block(); if (B && B->get_src_idx() == src_idx) { return true; } } return false; } bool ValidatorSessionRoundState::check_block_is_signed(ValidatorSessionDescription& desc) const { ValidatorWeight weight = 0; for (td::uint32 i = 0; i < desc.get_total_nodes(); i++) { if (signatures_->at(i)) { weight += desc.get_node_weight(i); if (weight >= desc.get_cutoff_weight()) { return true; } } } return false; } bool ValidatorSessionRoundState::check_need_generate_vote_for(ValidatorSessionDescription& desc, td::uint32 src_idx, td::uint32 att) const { if (src_idx != desc.get_vote_for_author(att)) { return false; } if (precommitted_) { return false; } if (get_first_attempt(src_idx) == 0 && desc.opts().max_round_attempts > 0) { return false; } if (get_first_attempt(src_idx) + desc.opts().max_round_attempts > att && desc.opts().max_round_attempts > 0) { return false; } auto attempt = get_attempt(attempts_, att); if (attempt) { bool vote_for_inited; attempt->get_vote_for_block(desc, vote_for_inited); if (attempt && vote_for_inited) { return false; } } if (!sent_blocks_) { return false; } for (td::uint32 i = 0; i < sent_blocks_->size(); i++) { auto B = sent_blocks_->at(i); if (B->check_block_is_approved(desc)) { return true; } } return false; } tl_object_ptr ValidatorSessionRoundState::generate_vote_for( ValidatorSessionDescription& desc, td::uint32 src_idx, td::uint32 att) const { CHECK(src_idx == desc.get_vote_for_author(att)); std::vector v; for (td::uint32 i = 0; i < sent_blocks_->size(); i++) { auto B = sent_blocks_->at(i); if (B->check_block_is_approved(desc)) { v.push_back(B->get_id()); } } CHECK(v.size() > 0); td::uint32 x = td::Random::secure_uint32(); return create_tl_object(get_seqno(), static_cast(att), v[x % v.size()]); } const SentBlock* ValidatorSessionRoundState::choose_block_to_vote(ValidatorSessionDescription& desc, td::uint32 src_idx, td::uint32 att, const SentBlock* vote_for, bool vote_for_inited, bool& found) const { found = false; if (!sent_blocks_) { return nullptr; } if (last_precommit_->at(src_idx) > 0) { auto attempt = get_attempt(attempts_, last_precommit_->at(src_idx)); CHECK(attempt); bool f; auto B = attempt->get_voted_block(desc, f); CHECK(f); found = true; return B; } auto t = first_attempt_->at(src_idx); auto slow_mode = (t > 0 && t + desc.opts().max_round_attempts <= att) || desc.opts().max_round_attempts == 0; if (!slow_mode) { bool f = false; const SentBlock* block = nullptr; auto size = attempts_ ? attempts_->size() : 0; for (td::uint32 i = size; i > 0; i--) { auto attempt = attempts_->at(i - 1); CHECK(attempt); block = attempt->get_voted_block(desc, f); if (f) { break; } } if (f) { found = true; return block; } td::int32 min_priority = desc.get_max_priority() + 2; for (td::uint32 i = 0; i < sent_blocks_->size(); i++) { auto B = sent_blocks_->at(i); if (!B->check_block_is_approved(desc)) { continue; } td::int32 e = B->get_block() ? desc.get_node_priority(B->get_src_idx(), seqno_) : desc.get_max_priority() + 1; CHECK(e >= 0); if (e < min_priority) { min_priority = e; block = B->get_block(); } } found = min_priority < static_cast(desc.get_max_priority() + 2); return block; } found = vote_for_inited; return vote_for; } bool ValidatorSessionRoundState::check_block_is_approved_by(td::uint32 src_idx, ValidatorSessionCandidateId block_id) const { if (!sent_blocks_) { return false; } auto size = sent_blocks_->size(); auto v = sent_blocks_->data(); for (td::uint32 i = 0; i < size; i++) { if (v[i]->get_id() == block_id) { return v[i]->check_block_is_approved_by(src_idx); } } return false; } tl_object_ptr ValidatorSessionRoundState::create_action( ValidatorSessionDescription& desc, td::uint32 src_idx, td::uint32 att) const { if (precommitted_) { return create_tl_object(seqno_, att); } auto attempt = get_attempt(attempts_, att); if (attempt) { return attempt->create_action(desc, this, src_idx, att); } bool found = false; auto block = choose_block_to_vote(desc, src_idx, att, nullptr, false, found); if (!found) { return create_tl_object(seqno_, att); } auto block_id = SentBlock::get_block_id(block); return create_tl_object(seqno_, att, block_id); } const SentBlock* ValidatorSessionRoundState::choose_block_to_sign(ValidatorSessionDescription& desc, td::uint32 src_idx, bool& found) const { found = false; if (!precommitted_) { return nullptr; } found = true; return precommitted_block_; } const ValidatorSessionRoundState* ValidatorSessionRoundState::make_one(ValidatorSessionDescription& desc, const ValidatorSessionRoundState* state, td::uint32 src_idx, td::uint32 att, bool& made) { made = false; auto first_attempt = state->first_attempt_; if (first_attempt->at(src_idx) == 0 || first_attempt->at(src_idx) > att) { made = true; first_attempt = CntVector::change(desc, state->first_attempt_, src_idx, att); state = ValidatorSessionRoundState::create(desc, state->precommitted_block_, state->seqno_, state->precommitted_, first_attempt, state->last_precommit_, state->sent_blocks_, state->signatures_, state->attempts_); } if (state->precommitted_) { return state; } auto attempt = get_attempt(state->attempts_, att); if (!attempt) { attempt = ValidatorSessionRoundAttemptState::create(desc, att); } bool m; auto a = ValidatorSessionRoundAttemptState::make_one(desc, attempt, src_idx, att, state, nullptr, m); if (!m) { return state; } made = true; auto att_vec = AttemptVector::push(desc, state->attempts_, std::move(a)); return ValidatorSessionRoundState::create(desc, state->precommitted_block_, state->seqno_, state->precommitted_, state->first_attempt_, state->last_precommit_, state->sent_blocks_, state->signatures_, std::move(att_vec)); } std::vector ValidatorSessionRoundState::choose_blocks_to_approve(ValidatorSessionDescription& desc, td::uint32 src_idx) const { if (!sent_blocks_) { return {nullptr}; } std::set was_source; std::vector x; x.resize(desc.get_max_priority() + 2, nullptr); bool was_empty = false; for (td::uint32 i = 0; i < sent_blocks_->size(); i++) { auto B = sent_blocks_->at(i); if (!B->get_block()) { was_empty = B->check_block_is_approved_by(src_idx); continue; } td::int32 prio = desc.get_node_priority(B->get_src_idx(), seqno_); CHECK(prio >= 0); td::uint32 blk_src_idx = B->get_src_idx(); if (was_source.count(blk_src_idx) > 0) { x[prio] = nullptr; } else { was_source.insert(blk_src_idx); if (!B->check_block_is_approved_by(src_idx)) { x[prio] = B; } } } std::vector res; for (auto& B : x) { if (B) { res.push_back(B->get_block()); } } if (!was_empty) { res.push_back(nullptr); } return res; } const SessionBlockCandidate* ValidatorSessionRoundState::get_block(ValidatorSessionCandidateId block_hash) const { if (!sent_blocks_) { return nullptr; } return get_candidate(sent_blocks_, block_hash); } std::vector ValidatorSessionRoundState::get_blocks_approved_by(ValidatorSessionDescription& desc, td::uint32 src_idx) const { if (!sent_blocks_) { return {}; } std::vector res; for (td::uint32 i = 0; i < sent_blocks_->size(); i++) { auto B = sent_blocks_->at(i); if (B->check_block_is_approved_by(src_idx)) { res.push_back(B->get_block()); } } return res; } std::vector ValidatorSessionRoundState::get_block_approvers(ValidatorSessionDescription& desc, ValidatorSessionCandidateId block) const { auto B = get_candidate(sent_blocks_, block); if (!B) { return {}; } std::vector v; for (td::uint32 i = 0; i < desc.get_total_nodes(); i++) { if (B->check_block_is_approved_by(i)) { v.push_back(i); } } return v; } // // // STATE // // const ValidatorSessionState* ValidatorSessionState::merge(ValidatorSessionDescription& desc, const ValidatorSessionState* left, const ValidatorSessionState* right) { if (!left) { return right; } if (!right) { return left; } if (left == right) { return left; } CHECK(left->att_->size() == desc.get_total_nodes()); CHECK(right->att_->size() == desc.get_total_nodes()); auto ts = CntVector::merge(desc, left->att_, right->att_, [](td::uint32 l, td::uint32 r) { return std::max(l, r); }); auto old = CntVector::merge( desc, left->old_rounds_, right->old_rounds_, [&](const ValidatorSessionOldRoundState* l, const ValidatorSessionOldRoundState* r) { return ValidatorSessionOldRoundState::merge(desc, l, r); }); const ValidatorSessionRoundState* round; if (left->cur_round_->get_seqno() < right->cur_round_->get_seqno()) { auto r = old->at(left->cur_round_->get_seqno()); old = CntVector::change( desc, old, left->cur_round_->get_seqno(), ValidatorSessionOldRoundState::merge(desc, r, left->cur_round_)); round = right->cur_round_; } else if (left->cur_round_->get_seqno() > right->cur_round_->get_seqno()) { auto r = old->at(right->cur_round_->get_seqno()); old = CntVector::change( desc, old, right->cur_round_->get_seqno(), ValidatorSessionOldRoundState::merge(desc, r, right->cur_round_)); round = left->cur_round_; } else { round = ValidatorSessionRoundState::merge(desc, left->cur_round_, right->cur_round_); } if (round->check_block_is_signed(desc)) { old = CntVector::push(desc, old, round->get_seqno(), ValidatorSessionOldRoundState::create(desc, round)); round = ValidatorSessionRoundState::create(desc, round->get_seqno() + 1); } return ValidatorSessionState::create(desc, std::move(ts), std::move(old), std::move(round)); } const ValidatorSessionState* ValidatorSessionState::action(ValidatorSessionDescription& desc, const ValidatorSessionState* state, td::uint32 src_idx, td::uint32 att, const ton_api::validatorSession_round_Message* action) { if (att < state->att_->at(src_idx)) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << action << "]: invalid message: bad ts: times goes back: " << state->att_->at(src_idx) << "->" << att; att = state->att_->at(src_idx); } if (att < 1024) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << action << "]: invalid message: bad ts: too small: " << att; att = 1024; } auto ts_vec = CntVector::change(desc, state->att_, src_idx, att); auto round_id = get_round_id(action); if (round_id > state->cur_round_->get_seqno()) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << action << "]: too big round id"; return ValidatorSessionState::create(desc, std::move(ts_vec), state->old_rounds_, state->cur_round_); } if (round_id == state->cur_round_->get_seqno()) { auto round = ValidatorSessionRoundState::action(desc, state->cur_round_, src_idx, att, std::move(action)); auto old = state->old_rounds_; if (round->check_block_is_signed(desc)) { old = CntVector::push(desc, old, round->get_seqno(), ValidatorSessionOldRoundState::create(desc, round)); round = ValidatorSessionRoundState::create(desc, round->get_seqno() + 1); } return ValidatorSessionState::create(desc, std::move(ts_vec), std::move(old), std::move(round)); } else { auto old = state->old_rounds_; old = CntVector::change( desc, old, round_id, ValidatorSessionOldRoundState::action(desc, old->at(round_id), src_idx, att, std::move(action))); return ValidatorSessionState::create(desc, std::move(ts_vec), std::move(old), state->cur_round_); } } tl_object_ptr ValidatorSessionState::create_action( ValidatorSessionDescription& desc, td::uint32 src_idx, td::uint32 att) const { return cur_round_->create_action(desc, src_idx, att); } const SentBlock* ValidatorSessionState::choose_block_to_sign(ValidatorSessionDescription& desc, td::uint32 src_idx, bool& found) const { found = false; if (cur_round_->check_block_is_signed_by(src_idx)) { return nullptr; } return cur_round_->choose_block_to_sign(desc, src_idx, found); } const ValidatorSessionState* ValidatorSessionState::make_one(ValidatorSessionDescription& desc, const ValidatorSessionState* state, td::uint32 src_idx, td::uint32 att, bool& made) { made = false; if (att < state->att_->at(src_idx)) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "]: time goes back, using old (bigger) value "; att = state->att_->at(src_idx); } if (att < 1024) { VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "] bad ts: too small: " << att; att = 1024; } bool upd_time = false; auto ts_vec = state->att_; if (ts_vec->at(src_idx) < att) { upd_time = true; ts_vec = CntVector::change(desc, ts_vec, src_idx, att); VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "]: updating time in make_all()"; } auto round = ValidatorSessionRoundState::make_one(desc, state->cur_round_, src_idx, att, made); if (!made && !upd_time) { return state; } made = true; auto old = state->old_rounds_; if (made && round->check_block_is_signed(desc)) { old = CntVector::push(desc, old, round->get_seqno(), ValidatorSessionOldRoundState::create(desc, round)); round = ValidatorSessionRoundState::create(desc, round->get_seqno()); } return ValidatorSessionState::create(desc, std::move(ts_vec), std::move(old), std::move(round)); } const SentBlock* ValidatorSessionState::get_committed_block(ValidatorSessionDescription& desc, td::uint32 seqno) const { if (seqno < old_rounds_->size()) { return old_rounds_->at(seqno)->get_block(); } else { return nullptr; } } const SentBlock* ValidatorSessionState::get_block(ValidatorSessionDescription& desc, ValidatorSessionCandidateId id, bool& found) const { auto B = cur_round_->get_block(id); if (!B) { found = false; return nullptr; } else { found = true; return B->get_block(); } } std::vector ValidatorSessionState::get_blocks_approved_by(ValidatorSessionDescription& desc, td::uint32 src_idx) const { return cur_round_->get_blocks_approved_by(desc, src_idx); } const CntVector* ValidatorSessionState::get_committed_block_signatures( ValidatorSessionDescription& desc, td::uint32 seqno) const { if (seqno < old_rounds_->size()) { return old_rounds_->at(seqno)->get_signatures(); } else { return nullptr; } } const CntVector* ValidatorSessionState::get_committed_block_approve_signatures( ValidatorSessionDescription& desc, td::uint32 seqno) const { if (seqno < old_rounds_->size()) { return old_rounds_->at(seqno)->get_approve_signatures(); } else { return nullptr; } } void ValidatorSessionState::dump(ValidatorSessionDescription& desc, td::StringBuilder& sb, td::uint32 att) const { cur_round_->dump(desc, sb, att); } void ValidatorSessionRoundState::dump_cur_attempt(ValidatorSessionDescription& desc, td::StringBuilder& sb) const { dump(desc, sb, desc.get_attempt_seqno(desc.get_ts())); } void ValidatorSessionRoundState::dump(ValidatorSessionDescription& desc, td::StringBuilder& sb, td::uint32 att) const { sb << "round_id=" << seqno_ << " total_weight=" << desc.get_total_weight() << " cutoff_weight=" << desc.get_cutoff_weight() << " precommitted=" << precommitted_ << "\n"; sb << "sent blocks:>>>>\n"; if (sent_blocks_) { const auto& v = sent_blocks_->data(); auto size = sent_blocks_->size(); for (td::uint32 i = 0; i < size; i++) { const auto& el = v[i]; const auto& b = el->get_block(); auto priority = b ? desc.get_node_priority(el->get_src_idx(), seqno_) : desc.get_max_priority() + 1; const auto& x = el->get_approvers_list(); ValidatorWeight cnt = 0; if (x) { auto s = desc.get_total_nodes(); for (td::uint32 j = 0; j < s; j++) { if (x->at(j)) { cnt += desc.get_node_weight(j); } } } if (b) { sb << " block hash=" << SentBlock::get_block_id(b) << " root_hash=" << b->get_root_hash() << " file_hash=" << b->get_file_hash() << " approved=" << cnt << " priority=" << priority << "\n"; } else { sb << " SKIP block approved=" << cnt << "\n"; } } } sb << " first attempt: "; for (td::uint32 i = 0; i < desc.get_total_nodes(); i++) { sb << first_attempt_->at(i) << " "; } sb << "\n"; sb << "<<<<\n"; auto attempt = get_attempt(attempts_, att); if (attempt) { attempt->dump(desc, sb); } } } // namespace validatorsession } // namespace ton