/*
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