1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-12 11:12:16 +00:00

Catchain improvements (#698)

* Fix "sgn" in fift; fix marking infinite loops as noterurn in func

* TON-P1-1: Remove unused catchain queries

* TON-P1-15: Avoid synchronization with self

* TON-P1-3, TON-P1-17: Disallow more than one candidate per src per round (to prevent flood), add checks to process_broadcast

* TON-P1-10: Fix fast/slow attempts

* TON-P1-14: Add named constants

* TON-P1-18, TON-P1-19: Alloc temporary memory in the same way as persistent memory

* TON-P1-20: Add comment to choose_blocks_to_approve

* TON-P1-16: Avoid creating two catchain blocks on need_new_block

* TON-P1-8: Add some validation to validator-engine parameters

* TON-P1-6: Don't allow sending the same block many times

Many requests for the same block are not unusual (however, there's no need to answer them all)

* TON-P1-2: Enable prohibiting dependencies from blamed nodes (2.7.5 in CatChain doc), fix processing blame proofs

* Best practices

bp-6: Fix incorrect warning
bp-7: Remove unused code
bp-8: Bring back PerfWarningTimer logging (only when no callback)
bp-9: Remove unnecessary condition
bp-11: Remove commented-out code
bp-13: Divide code in validator-session-state
Adherence to Specification: Fix typo
This commit is contained in:
SpyCheese 2023-05-10 12:57:57 +03:00 committed by GitHub
parent 7878578dba
commit 5abfe2337e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 1417 additions and 1303 deletions

View file

@ -52,6 +52,7 @@ class CatChainReceiverInterface : public td::actor::Actor {
td::BufferSlice query, td::uint64 max_answer_size,
td::actor::ActorId<adnl::AdnlSenderInterface> via) = 0;
virtual void send_custom_message_data(const PublicKeyHash &dst, td::BufferSlice query) = 0;
virtual void on_blame_processed(td::uint32 source_id) = 0;
virtual void destroy() = 0;

View file

@ -60,15 +60,15 @@ td::Result<std::unique_ptr<CatChainReceiverSource>> CatChainReceiverSource::crea
void CatChainReceiverSourceImpl::blame(td::uint32 fork, CatChainBlockHeight height) {
blame();
if (!blamed_heights_.empty()) {
if (blamed_heights_.size() <= fork) {
blamed_heights_.resize(fork + 1, 0);
}
if (blamed_heights_[fork] == 0 || blamed_heights_[fork] > height) {
VLOG(CATCHAIN_INFO) << this << ": blamed at " << fork << " " << height;
blamed_heights_[fork] = height;
}
// if (!blamed_heights_.empty()) {
if (blamed_heights_.size() <= fork) {
blamed_heights_.resize(fork + 1, 0);
}
if (blamed_heights_[fork] == 0 || blamed_heights_[fork] > height) {
VLOG(CATCHAIN_INFO) << this << ": blamed at " << fork << " " << height;
blamed_heights_[fork] = height;
}
// }
}
void CatChainReceiverSourceImpl::blame() {
@ -144,7 +144,7 @@ void CatChainReceiverSourceImpl::on_new_block(CatChainReceivedBlock *block) {
on_found_fork_proof(create_serialize_tl_object<ton_api::catchain_block_data_fork>(block->export_tl_dep(),
it->second->export_tl_dep())
.as_slice());
chain_->add_prepared_event(fork_proof());
chain_->on_found_fork_proof(id_, fork_proof());
}
blame();
return;
@ -162,6 +162,15 @@ void CatChainReceiverSourceImpl::on_found_fork_proof(const td::Slice &proof) {
}
}
bool CatChainReceiverSourceImpl::allow_send_block(CatChainBlockHash hash) {
td::uint32 count = ++block_requests_count_[hash];
if (count > MAX_BLOCK_REQUESTS) {
VLOG(CATCHAIN_INFO) << this << ": node requested block " << hash << " " << count << " times";
return false;
}
return true;
}
} // namespace catchain
} // namespace ton

View file

@ -61,6 +61,9 @@ class CatChainReceiverSource {
virtual td::BufferSlice fork_proof() const = 0;
virtual bool fork_is_found() const = 0;
// One block can be sent to one node only a limited number of times to prevent DoS
virtual bool allow_send_block(CatChainBlockHash hash) = 0;
static td::Result<std::unique_ptr<CatChainReceiverSource>> create(CatChainReceiver *chain, PublicKey pub_key,
adnl::AdnlNodeIdShort adnl_id, td::uint32 id);

View file

@ -111,6 +111,8 @@ class CatChainReceiverSourceImpl : public CatChainReceiverSource {
return chain_;
}
bool allow_send_block(CatChainBlockHash hash) override;
CatChainReceiverSourceImpl(CatChainReceiver *chain, PublicKey source, adnl::AdnlNodeIdShort adnl_id, td::uint32 id);
private:
@ -130,6 +132,11 @@ class CatChainReceiverSourceImpl : public CatChainReceiverSource {
CatChainBlockHeight delivered_height_ = 0;
CatChainBlockHeight received_height_ = 0;
std::map<CatChainBlockHash, td::uint32> block_requests_count_;
// One block can be sent to one node up to 5 times
static const td::uint32 MAX_BLOCK_REQUESTS = 5;
};
} // namespace catchain

View file

@ -168,13 +168,6 @@ void CatChainReceiverImpl::receive_message_from_overlay(adnl::AdnlNodeIdShort sr
return;
}
/*auto S = get_source_by_hash(src);
CHECK(S != nullptr);
if (S->blamed()) {
VLOG(CATCHAIN_INFO) << this << ": dropping block update from blamed source " << src;
return;
}*/
if (data.size() > opts_.max_serialized_block_size) {
VLOG(CATCHAIN_WARNING) << this << ": dropping broken block from " << src << ": too big (size="
<< data.size() << ", limit=" << opts_.max_serialized_block_size << ")";
@ -197,19 +190,6 @@ void CatChainReceiverImpl::receive_broadcast_from_overlay(const PublicKeyHash &s
callback_->on_broadcast(src, std::move(data));
}
/*void CatChainReceiverImpl::send_block(const PublicKeyHash &src, tl_object_ptr<ton_api::catchain_block> block,
td::BufferSlice payload) {
CHECK(read_db_);
CHECK(src == local_id_);
validate_block_sync(block, payload.as_slice()).ensure();
auto B = create_block(std::move(block), td::SharedSlice{payload.as_slice()});
CHECK(B != nullptr);
run_scheduler();
CHECK(B->delivered());
}*/
CatChainReceivedBlock *CatChainReceiverImpl::create_block(tl_object_ptr<ton_api::catchain_block> block,
td::SharedSlice payload) {
if (block->height_ == 0) {
@ -267,21 +247,16 @@ td::Status CatChainReceiverImpl::validate_block_sync(const tl_object_ptr<ton_api
td::Status CatChainReceiverImpl::validate_block_sync(const tl_object_ptr<ton_api::catchain_block> &block,
const td::Slice &payload) const {
//LOG(INFO) << ton_api::to_string(block);
TRY_STATUS_PREFIX(CatChainReceivedBlock::pre_validate_block(this, block, payload), "failed to validate block: ");
// After pre_validate_block, block->height_ > 0
auto id = CatChainReceivedBlock::block_id(this, block, payload);
td::BufferSlice B = serialize_tl_object(id, true);
if (block->height_ > 0) {
auto id = CatChainReceivedBlock::block_id(this, block, payload);
td::BufferSlice B = serialize_tl_object(id, true);
CatChainReceiverSource *S = get_source_by_hash(PublicKeyHash{id->src_});
CHECK(S != nullptr);
Encryptor *E = S->get_encryptor_sync();
CHECK(E != nullptr);
return E->check_signature(B.as_slice(), block->signature_.as_slice());
} else {
return td::Status::OK();
}
CatChainReceiverSource *S = get_source_by_hash(PublicKeyHash{id->src_});
CHECK(S != nullptr);
Encryptor *E = S->get_encryptor_sync();
CHECK(E != nullptr);
return E->check_signature(B.as_slice(), block->signature_.as_slice());
}
void CatChainReceiverImpl::run_scheduler() {
@ -515,7 +490,6 @@ CatChainReceiverImpl::CatChainReceiverImpl(std::unique_ptr<Callback> callback,
}
CHECK(local_idx_ != static_cast<td::uint32>(ids.size()));
//std::sort(short_ids.begin(), short_ids.end());
auto F = create_tl_object<ton_api::catchain_firstblock>(unique_hash, std::move(short_ids));
overlay_full_id_ = overlay::OverlayIdFull{serialize_tl_object(F, true)};
@ -527,6 +501,8 @@ CatChainReceiverImpl::CatChainReceiverImpl(std::unique_ptr<Callback> callback,
blocks_[root_block_->get_hash()] = std::move(R);
last_sent_block_ = root_block_;
blame_processed_.resize(sources_.size(), false);
choose_neighbours();
}
@ -704,7 +680,6 @@ void CatChainReceiverImpl::receive_query_from_overlay(adnl::AdnlNodeIdShort src,
auto F = fetch_tl_object<ton_api::Function>(data.clone(), true);
if (F.is_error()) {
callback_->on_custom_query(get_source_by_adnl_id(src)->get_hash(), std::move(data), std::move(promise));
//LOG(WARNING) << this << ": unknown query from " << src;
return;
}
auto f = F.move_as_ok();
@ -717,68 +692,15 @@ void CatChainReceiverImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::cat
if (it == blocks_.end() || it->second->get_height() == 0 || !it->second->initialized()) {
promise.set_value(serialize_tl_object(create_tl_object<ton_api::catchain_blockNotFound>(), true));
} else {
promise.set_value(serialize_tl_object(create_tl_object<ton_api::catchain_blockResult>(it->second->export_tl()),
true, it->second->get_payload().as_slice()));
}
}
void CatChainReceiverImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlocks query,
td::Promise<td::BufferSlice> promise) {
if (query.blocks_.size() > MAX_QUERY_BLOCKS) {
promise.set_error(td::Status::Error(ErrorCode::protoviolation, "too many blocks"));
return;
}
td::int32 cnt = 0;
for (const CatChainBlockHash &b : query.blocks_) {
auto it = blocks_.find(b);
if (it != blocks_.end() && it->second->get_height() > 0) {
auto block = create_tl_object<ton_api::catchain_blockUpdate>(it->second->export_tl());
CHECK(!it->second->get_payload().empty());
td::BufferSlice B = serialize_tl_object(block, true, it->second->get_payload().clone());
CHECK(B.size() <= opts_.max_serialized_block_size);
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_message, src,
get_source(local_idx_)->get_adnl_id(), overlay_id_, std::move(B));
cnt++;
CatChainReceiverSource *S = get_source_by_adnl_id(src);
CHECK(S != nullptr);
if (S->allow_send_block(it->second->get_hash())) {
promise.set_value(serialize_tl_object(create_tl_object<ton_api::catchain_blockResult>(it->second->export_tl()),
true, it->second->get_payload().as_slice()));
} else {
promise.set_error(td::Status::Error("block was requested too many times"));
}
}
promise.set_value(serialize_tl_object(create_tl_object<ton_api::catchain_sent>(cnt), true));
}
void CatChainReceiverImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlockHistory query,
td::Promise<td::BufferSlice> promise) {
int64_t h = query.height_;
if (h <= 0) {
promise.set_error(td::Status::Error(ErrorCode::protoviolation, "not-positive height"));
return;
}
if (h > MAX_QUERY_HEIGHT) {
h = MAX_QUERY_HEIGHT;
}
std::set<CatChainBlockHash> s{query.stop_if_.begin(), query.stop_if_.end()};
CatChainReceivedBlock *B = get_block(query.block_);
if (B == nullptr) {
promise.set_value(serialize_tl_object(create_tl_object<ton_api::catchain_sent>(0), true));
return;
}
if (static_cast<CatChainBlockHeight>(h) > B->get_height()) {
h = B->get_height();
}
td::uint32 cnt = 0;
while (h-- > 0) {
if (s.find(B->get_hash()) != s.end()) {
break;
}
auto block = create_tl_object<ton_api::catchain_blockUpdate>(B->export_tl());
CHECK(!B->get_payload().empty());
td::BufferSlice BB = serialize_tl_object(block, true, B->get_payload().as_slice());
CHECK(BB.size() <= opts_.max_serialized_block_size);
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_message, src,
get_source(local_idx_)->get_adnl_id(), overlay_id_, std::move(BB));
B = B->get_prev();
cnt++;
}
promise.set_value(serialize_tl_object(create_tl_object<ton_api::catchain_sent>(cnt), true));
}
void CatChainReceiverImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getDifference query,
@ -832,6 +754,8 @@ void CatChainReceiverImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::cat
}
}
CHECK(right > 0);
CatChainReceiverSource *S0 = get_source_by_adnl_id(src);
CHECK(S0 != nullptr);
for (td::uint32 i = 0; i < get_sources_cnt(); i++) {
if (vt[i] >= 0 && my_vt[i] > vt[i]) {
CatChainReceiverSource *S = get_source(i);
@ -839,12 +763,14 @@ void CatChainReceiverImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::cat
while (t-- > 0) {
CatChainReceivedBlock *M = S->get_block(++vt[i]);
CHECK(M != nullptr);
auto block = create_tl_object<ton_api::catchain_blockUpdate>(M->export_tl());
CHECK(!M->get_payload().empty());
td::BufferSlice BB = serialize_tl_object(block, true, M->get_payload().as_slice());
CHECK(BB.size() <= opts_.max_serialized_block_size);
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_message, src,
get_source(local_idx_)->get_adnl_id(), overlay_id_, std::move(BB));
if (S0->allow_send_block(M->get_hash())) {
auto block = create_tl_object<ton_api::catchain_blockUpdate>(M->export_tl());
CHECK(!M->get_payload().empty());
td::BufferSlice BB = serialize_tl_object(block, true, M->get_payload().as_slice());
CHECK(BB.size() <= opts_.max_serialized_block_size);
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_message, src,
get_source(local_idx_)->get_adnl_id(), overlay_id_, std::move(BB));
}
}
}
}
@ -1031,10 +957,15 @@ void CatChainReceiverImpl::written_unsafe_root_block(CatChainReceivedBlock *bloc
void CatChainReceiverImpl::alarm() {
alarm_timestamp() = td::Timestamp::never();
if (next_sync_ && next_sync_.is_in_past()) {
if (next_sync_ && next_sync_.is_in_past() && get_sources_cnt() > 1) {
next_sync_ = td::Timestamp::in(td::Random::fast(SYNC_INTERVAL_MIN, SYNC_INTERVAL_MAX));
for (unsigned i = 0; i < SYNC_ITERATIONS; i++) {
CatChainReceiverSource *S = get_source(td::Random::fast(0, static_cast<td::int32>(get_sources_cnt()) - 1));
auto idx = td::Random::fast(1, static_cast<td::int32>(get_sources_cnt()) - 1);
if (idx == static_cast<td::int32>(local_idx_)) {
idx = 0;
}
// idx is a random number in [0, get_sources_cnt-1] not equal to local_idx
CatChainReceiverSource *S = get_source(idx);
CHECK(S != nullptr);
if (!S->blamed()) {
synchronize_with(S);
@ -1117,6 +1048,23 @@ static void destroy_db(const std::string& name, td::uint32 attempt) {
}
}
void CatChainReceiverImpl::on_found_fork_proof(td::uint32 source_id, td::BufferSlice data) {
if (blame_processed_[source_id]) {
add_block(std::move(data), std::vector<CatChainBlockHash>());
} else {
pending_fork_proofs_[source_id] = std::move(data);
}
}
void CatChainReceiverImpl::on_blame_processed(td::uint32 source_id) {
blame_processed_[source_id] = true;
auto it = pending_fork_proofs_.find(source_id);
if (it != pending_fork_proofs_.end()) {
add_block(std::move(it->second), std::vector<CatChainBlockHash>());
pending_fork_proofs_.erase(it);
}
}
void CatChainReceiverImpl::destroy() {
auto name = db_root_ + "/catchainreceiver" + db_suffix_ + td::base64url_encode(as_slice(incarnation_));
delay_action([name]() { destroy_db(name, 0); }, td::Timestamp::in(DESTROY_DB_DELAY));

View file

@ -56,7 +56,7 @@ class CatChainReceiver : public CatChainReceiverInterface {
virtual void run_block(CatChainReceivedBlock *block) = 0;
virtual void deliver_block(CatChainReceivedBlock *block) = 0;
virtual td::uint32 add_fork() = 0;
virtual void add_prepared_event(td::BufferSlice data) = 0;
virtual void on_found_fork_proof(td::uint32 source_id, td::BufferSlice data) = 0;
virtual void on_blame(td::uint32 source_id) = 0;
virtual const CatChainOptions &opts() const = 0;

View file

@ -39,9 +39,6 @@ class CatChainReceiverImpl final : public CatChainReceiver {
return PrintId{incarnation_, local_id_};
}
void add_prepared_event(td::BufferSlice data) override {
add_block(std::move(data), std::vector<CatChainBlockHash>());
}
CatChainSessionId get_incarnation() const override {
return incarnation_;
}
@ -73,15 +70,10 @@ class CatChainReceiverImpl final : public CatChainReceiver {
void receive_query_from_overlay(adnl::AdnlNodeIdShort src, td::BufferSlice data,
td::Promise<td::BufferSlice> promise);
void process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlock query, td::Promise<td::BufferSlice> promise);
void process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlocks query,
td::Promise<td::BufferSlice> promise);
void process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlockHistory query,
td::Promise<td::BufferSlice> promise);
void process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getDifference query,
td::Promise<td::BufferSlice> promise);
template <class T>
void process_query(adnl::AdnlNodeIdShort src, const T &query, td::Promise<td::BufferSlice> promise) {
//LOG(WARNING) << this << ": unknown query from " << src;
callback_->on_custom_query(get_source_by_adnl_id(src)->get_hash(), serialize_tl_object(&query, true),
std::move(promise));
}
@ -89,7 +81,6 @@ class CatChainReceiverImpl final : public CatChainReceiver {
void receive_block(adnl::AdnlNodeIdShort src, tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload);
void receive_block_answer(adnl::AdnlNodeIdShort src, td::BufferSlice);
//void send_block(const PublicKeyHash &src, tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload);
CatChainReceivedBlock *create_block(tl_object_ptr<ton_api::catchain_block> block, td::SharedSlice payload) override;
CatChainReceivedBlock *create_block(tl_object_ptr<ton_api::catchain_block_dep> block) override;
@ -117,6 +108,8 @@ class CatChainReceiverImpl final : public CatChainReceiver {
void on_blame(td::uint32 src) override {
callback_->blame(src);
}
void on_found_fork_proof(td::uint32 source_id, td::BufferSlice data) override;
void on_blame_processed(td::uint32 source_id) override;
const CatChainOptions &opts() const override {
return opts_;
}
@ -204,9 +197,6 @@ class CatChainReceiverImpl final : public CatChainReceiver {
std::vector<td::uint32> neighbours_;
//std::queue<tl_object_ptr<ton_api::catchain_block_inner_Data>> events_;
//std::queue<td::BufferSlice> raw_events_;
td::actor::ActorId<keyring::Keyring> keyring_;
td::actor::ActorId<adnl::Adnl> adnl_;
td::actor::ActorId<overlay::Overlays> overlay_manager_;
@ -231,6 +221,9 @@ class CatChainReceiverImpl final : public CatChainReceiver {
bool started_{false};
std::list<CatChainReceivedBlock *> to_run_;
std::vector<bool> blame_processed_;
std::map<td::uint32, td::BufferSlice> pending_fork_proofs_;
};
} // namespace catchain

View file

@ -108,11 +108,12 @@ void CatChainImpl::need_new_block(td::Timestamp t) {
if (!receiver_started_) {
return;
}
if (!force_process_) {
if (!force_process_ || !active_process_) {
VLOG(CATCHAIN_INFO) << this << ": forcing creation of new block";
}
force_process_ = true;
if (!active_process_) {
if (active_process_) {
force_process_ = true;
} else {
alarm_timestamp().relax(t);
}
}
@ -197,6 +198,14 @@ void CatChainImpl::on_blame(td::uint32 src_id) {
}
}
}
for (td::uint32 i = 0; i < process_deps_.size(); ++i) {
if (blocks_[process_deps_[i]]->source() == src_id) {
process_deps_[i] = process_deps_.back();
process_deps_.pop_back();
--i;
}
}
td::actor::send_closure(receiver_, &CatChainReceiverInterface::on_blame_processed, src_id);
}
void CatChainImpl::on_custom_query(const PublicKeyHash &src, td::BufferSlice data, td::Promise<td::BufferSlice> promise) {

View file

@ -235,7 +235,7 @@ void interpret_cmp(vm::Stack& stack, const char opt[3]) {
}
void interpret_sgn(vm::Stack& stack, const char opt[3]) {
auto x = stack.pop_int();
auto x = stack.pop_int_finite();
int r = x->sgn();
assert((unsigned)(r + 1) <= 2);
stack.push_smallint(((const signed char*)opt)[r + 1]);

View file

@ -894,7 +894,7 @@ bool Op::mark_noreturn() {
return set_noreturn((block0->mark_noreturn() & (block1 && block1->mark_noreturn())) | next->mark_noreturn());
case _Again:
block0->mark_noreturn();
return set_noreturn(false);
return set_noreturn(true);
case _Until:
return set_noreturn(block0->mark_noreturn() | next->mark_noreturn());
case _While:

View file

@ -243,7 +243,7 @@ Notice that an external punishment for creating catchain forks may be used in th
Once a fork (created by~$i$) is detected (by another process~$j$), i.e.\ $j$ learns about two different messages $m_{i,s}$ and $m'_{i,s}$ created by $i$ and having same height $s$ (usually this happens while recursively downloading dependencies of some other messages), $j$ starts ignoring~$i$ and all of its subsequent messages. They are not accepted and not broadcast further. However, messages created by~$i$ prior to the fork detection may be still downloaded if they are referred to in messages (blocks) created by processes that did not see this fork before referring to such messages created by~$i$.
\nxsubpoint\emb{Accepting messages from a ``bad'' process is bad}\label{sp:no.bad.accept}
Furthermore, if process $i$ learns about a fork created by process $j$, then $i$ shows this to its neighbors by creating a new service broadcast message that contains the corresponding fork proof (cf.~\ptref{sp:fork.proofs}). Afterwards, this and all subsequent messages of $j$ cannot directly depend on any messages by the known ``bad'' producer $i$ (but they still can refer to messages from another party $k$ that directly or indirectly refer to messages of~$i$ if no fork by~$i$ was known to $k$ at the time when the referring message was created). If $j$ violates this restriction and creates messages with such invalid references, these messages will be discarded by all honest processes in the group.
Furthermore, if process $i$ learns about a fork created by process $j$, then $i$ shows this to its neighbors by creating a new service broadcast message that contains the corresponding fork proof (cf.~\ptref{sp:fork.proofs}). Afterwards, this and all subsequent messages of $i$ cannot directly depend on any messages by the known ``bad'' producer $j$ (but they still can refer to messages from another party $k$ that directly or indirectly refer to messages of~$i$ if no fork by~$i$ was known to $k$ at the time when the referring message was created). If $i$ violates this restriction and creates messages with such invalid references, these messages will be discarded by all honest processes in the group.
\nxsubpoint\emb{The set of ``bad'' group members is a part of the intrinsic state}\label{sp:bad.proc.set}
Each process~$i$ keeps its own copy of the set of known ``bad'' processes in the group, i.e., those processes that have created at least one fork or have violated \ptref{sp:no.bad.accept}. This set is updated by adding~$j$ into it as soon as $i$ learns about a fork created by~$j$ (or about a violation of~\ptref{sp:no.bad.accept} by $j$); after that, a callback provided by the higher-level protocol is invoked. This set is used when a new broadcast message arrives: if the sender is bad, then the message is ignored and discarded.

View file

@ -78,9 +78,12 @@ void PerfWarningTimer::reset() {
return;
}
double duration = Time::now() - start_at_;
//LOG_IF(WARNING, duration > max_duration_)
//<< "SLOW: " << tag("name", name_) << tag("duration", format::as_time(duration));
callback_(duration);
if (callback_) {
callback_(duration);
} else {
LOG_IF(WARNING, duration > max_duration_)
<< "SLOW: " << tag("name", name_) << tag("duration", format::as_time(duration));
}
start_at_ = 0;
}

View file

@ -46,7 +46,7 @@ class Timer {
class PerfWarningTimer {
public:
explicit PerfWarningTimer(string name, double max_duration = 0.1, std::function<void(double)>&& callback = [] (double) {});
explicit PerfWarningTimer(string name, double max_duration = 0.1, std::function<void(double)>&& callback = {});
PerfWarningTimer(const PerfWarningTimer &) = delete;
PerfWarningTimer &operator=(const PerfWarningTimer &) = delete;
PerfWarningTimer(PerfWarningTimer &&other);

View file

@ -277,14 +277,10 @@ catchain.differenceFork left:catchain.block.dep right:catchain.block.dep = catch
catchain.blockNotFound = catchain.BlockResult;
catchain.blockResult block:catchain.block = catchain.BlockResult;
catchain.sent cnt:int = catchain.Sent;
---functions---
catchain.getBlock block:int256 = catchain.BlockResult;
catchain.getBlocks blocks:(vector int256) = catchain.Sent;
catchain.getDifference rt:(vector int) = catchain.Difference;
catchain.getBlockHistory block:int256 height:long stop_if:(vector int256) = catchain.Sent;
//catchain.getForkDifference src:int fork:catchain.fork = catchain.ForkDifference;
---types---

Binary file not shown.

View file

@ -1319,9 +1319,6 @@ td::Status ValidatorEngine::load_global_config() {
}
CHECK(mode == ton::validator::ValidatorManagerOptions::ShardCheckMode::m_validate);
return true;
/*ton::ShardIdFull p{ton::basechainId, ((cc_seqno * 1ull % 4) << 62) + 1};
auto s = ton::shard_prefix(p, 2);
return shard.is_masterchain() || ton::shard_intersects(shard, s);*/
});
if (state_ttl_ != 0) {
validator_options_.write().set_state_ttl(state_ttl_);
@ -3647,34 +3644,61 @@ int main(int argc, char *argv[]) {
logger_ = td::TsFileLog::create(fname.str()).move_as_ok();
td::log_interface = logger_.get();
});
p.add_option('s', "state-ttl", "state will be gc'd after this time (in seconds) default=3600", [&](td::Slice fname) {
auto v = td::to_double(fname);
acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_state_ttl, v); });
});
p.add_option('m', "mempool-num", "Maximal number of mempool external message", [&](td::Slice fname) {
p.add_checked_option('s', "state-ttl", "state will be gc'd after this time (in seconds) default=3600",
[&](td::Slice fname) {
auto v = td::to_double(fname);
if (v <= 0) {
return td::Status::Error("state-ttl should be positive");
}
acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_state_ttl, v); });
return td::Status::OK();
});
p.add_checked_option('m', "mempool-num", "Maximal number of mempool external message", [&](td::Slice fname) {
auto v = td::to_double(fname);
if (v < 0) {
return td::Status::Error("mempool-num should be non-negative");
}
acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_max_mempool_num, v); });
return td::Status::OK();
});
p.add_option('b', "block-ttl", "blocks will be gc'd after this time (in seconds) default=7*86400",
[&](td::Slice fname) {
auto v = td::to_double(fname);
acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_block_ttl, v); });
});
p.add_option('A', "archive-ttl", "archived blocks will be deleted after this time (in seconds) default=365*86400",
[&](td::Slice fname) {
auto v = td::to_double(fname);
acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_archive_ttl, v); });
});
p.add_option('K', "key-proof-ttl", "key blocks will be deleted after this time (in seconds) default=365*86400*10",
[&](td::Slice fname) {
auto v = td::to_double(fname);
acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_key_proof_ttl, v); });
});
p.add_option('S', "sync-before", "in initial sync download all blocks for last given seconds default=3600",
[&](td::Slice fname) {
auto v = td::to_double(fname);
acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_sync_ttl, v); });
});
p.add_checked_option('b', "block-ttl", "blocks will be gc'd after this time (in seconds) default=7*86400",
[&](td::Slice fname) {
auto v = td::to_double(fname);
if (v <= 0) {
return td::Status::Error("block-ttl should be positive");
}
acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_block_ttl, v); });
return td::Status::OK();
});
p.add_checked_option(
'A', "archive-ttl", "archived blocks will be deleted after this time (in seconds) default=365*86400",
[&](td::Slice fname) {
auto v = td::to_double(fname);
if (v <= 0) {
return td::Status::Error("archive-ttl should be positive");
}
acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_archive_ttl, v); });
return td::Status::OK();
});
p.add_checked_option(
'K', "key-proof-ttl", "key blocks will be deleted after this time (in seconds) default=365*86400*10",
[&](td::Slice fname) {
auto v = td::to_double(fname);
if (v <= 0) {
return td::Status::Error("key-proof-ttl should be positive");
}
acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_key_proof_ttl, v); });
return td::Status::OK();
});
p.add_checked_option('S', "sync-before", "in initial sync download all blocks for last given seconds default=3600",
[&](td::Slice fname) {
auto v = td::to_double(fname);
if (v <= 0) {
return td::Status::Error("sync-before should be positive");
}
acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_sync_ttl, v); });
return td::Status::OK();
});
p.add_option('T', "truncate-db", "truncate db (with specified seqno as new top masterchain block seqno)",
[&](td::Slice fname) {
auto v = td::to_integer<ton::BlockSeqno>(fname);

View file

@ -9,6 +9,7 @@ set(VALIDATOR_SESSION_SOURCE
validator-session-description.cpp
validator-session-state.cpp
validator-session.cpp
validator-session-round-attempt-state.cpp
persistent-vector.h
validator-session-description.h
@ -16,7 +17,7 @@ set(VALIDATOR_SESSION_SOURCE
validator-session-state.h
validator-session.h
validator-session.hpp
)
validator-session-round-attempt-state.h)
add_library(validatorsession STATIC ${VALIDATOR_SESSION_SOURCE})

View file

@ -322,7 +322,6 @@ class CntVector : public ValidatorSessionDescription::RootObject {
CHECK(idx < size());
return data_[idx];
}
//const T& at(size_t idx) const;
private:
const td::uint32 data_size_;
@ -546,7 +545,6 @@ class CntVector<bool> : public ValidatorSessionDescription::RootObject {
CHECK(idx < max_size());
return get_bit(data_, idx);
}
//const T& at(size_t idx) const;
private:
const td::uint32 data_size_;
@ -734,10 +732,6 @@ class CntSortedVector : public ValidatorSessionDescription::RootObject {
return CntSortedVector::create(desc, std::move(v));
}
/*static const CntSortedVector* merge(ValidatorSessionDescription& desc, const CntSortedVector* l,
const CntSortedVector* r) {
return merge(desc, l, r, [](T l, T r) { return l; });
}*/
static const CntSortedVector* push(ValidatorSessionDescription& desc, const CntSortedVector* v, T value) {
if (!v) {
return create(desc, std::vector<T>{value});
@ -795,7 +789,6 @@ class CntSortedVector : public ValidatorSessionDescription::RootObject {
CHECK(idx < size());
return data_[idx];
}
//const T& at(size_t idx) const;
private:
const td::uint32 data_size_;

View file

@ -51,13 +51,6 @@ ValidatorSessionDescriptionImpl::ValidatorSessionDescriptionImpl(ValidatorSessio
CHECK(it != rev_sources_.end());
self_idx_ = it->second;
pdata_temp_ptr_ = 0;
pdata_temp_size_ = 1 << 27;
pdata_temp_ = new td::uint8[pdata_temp_size_];
pdata_perm_size_ = 1ull << 27;
pdata_perm_ptr_ = 0;
for (auto &el : cache_) {
Cached v{nullptr};
el.store(v, std::memory_order_relaxed);
@ -162,43 +155,11 @@ void ValidatorSessionDescriptionImpl::update_hash(const RootObject *obj, HashTyp
}
void *ValidatorSessionDescriptionImpl::alloc(size_t size, size_t align, bool temp) {
CHECK(align && !(align & (align - 1))); // align should be a power of 2
auto get_padding = [&](const uint8_t* ptr) {
return (-(size_t)ptr) & (align - 1);
};
if (temp) {
pdata_temp_ptr_ += get_padding(pdata_temp_ + pdata_temp_ptr_);
auto s = pdata_temp_ptr_;
pdata_temp_ptr_ += size;
CHECK(s + size <= pdata_temp_size_);
return static_cast<void *>(pdata_temp_ + s);
} else {
while (true) {
size_t idx = pdata_perm_ptr_ / pdata_perm_size_;
if (idx < pdata_perm_.size()) {
auto ptr = pdata_perm_[idx] + (pdata_perm_ptr_ % pdata_perm_size_);
pdata_perm_ptr_ += get_padding(ptr);
ptr += get_padding(ptr);
pdata_perm_ptr_ += size;
if (pdata_perm_ptr_ <= pdata_perm_.size() * pdata_perm_size_) {
return static_cast<void *>(ptr);
}
}
pdata_perm_.push_back(new td::uint8[pdata_perm_size_]);
}
}
return (temp ? mem_temp_ : mem_perm_).alloc(size, align);
}
bool ValidatorSessionDescriptionImpl::is_persistent(const void *ptr) const {
if (ptr == nullptr) {
return true;
}
for (auto &v : pdata_perm_) {
if (ptr >= v && ptr <= v + pdata_perm_size_) {
return true;
}
}
return false;
return mem_perm_.contains(ptr);
}
std::unique_ptr<ValidatorSessionDescription> ValidatorSessionDescription::create(
@ -206,6 +167,58 @@ std::unique_ptr<ValidatorSessionDescription> ValidatorSessionDescription::create
return std::make_unique<ValidatorSessionDescriptionImpl>(std::move(opts), nodes, local_id);
}
ValidatorSessionDescriptionImpl::MemPool::MemPool(size_t chunk_size) : chunk_size_(chunk_size) {
}
ValidatorSessionDescriptionImpl::MemPool::~MemPool() {
for (auto &v : data_) {
delete[] v;
}
}
void *ValidatorSessionDescriptionImpl::MemPool::alloc(size_t size, size_t align) {
CHECK(align && !(align & (align - 1))); // align should be a power of 2
CHECK(size + align <= chunk_size_);
auto get_padding = [&](const uint8_t* ptr) {
return (-(size_t)ptr) & (align - 1);
};
while (true) {
size_t idx = ptr_ / chunk_size_;
if (idx < data_.size()) {
auto ptr = data_[idx] + (ptr_ % chunk_size_);
ptr_ += get_padding(ptr);
ptr += get_padding(ptr);
ptr_ += size;
if (ptr_ <= data_.size() * chunk_size_) {
return static_cast<void *>(ptr);
} else {
ptr_ = data_.size() * chunk_size_;
}
}
data_.push_back(new td::uint8[chunk_size_]);
}
}
void ValidatorSessionDescriptionImpl::MemPool::clear() {
while (data_.size() > 1) {
delete[] data_.back();
data_.pop_back();
}
ptr_ = 0;
}
bool ValidatorSessionDescriptionImpl::MemPool::contains(const void* ptr) const {
if (ptr == nullptr) {
return true;
}
for (auto &v : data_) {
if (ptr >= v && ptr <= v + chunk_size_) {
return true;
}
}
return false;
}
} // namespace validatorsession
} // namespace ton

View file

@ -50,20 +50,30 @@ class ValidatorSessionDescriptionImpl : public ValidatorSessionDescription {
td::uint32 self_idx_;
static constexpr td::uint32 cache_size = (1 << 20);
static constexpr size_t mem_chunk_size_perm = (1 << 27);
static constexpr size_t mem_chunk_size_temp = (1 << 27);
struct Cached {
const RootObject *ptr;
};
std::array<std::atomic<Cached>, cache_size> cache_;
//std::array<std::atomic<Cached>, cache_size> temp_cache_;
td::uint8 *pdata_temp_;
size_t pdata_temp_ptr_;
size_t pdata_temp_size_;
class MemPool {
public:
explicit MemPool(size_t chunk_size);
~MemPool();
void *alloc(size_t size, size_t align);
void clear();
bool contains(const void* ptr) const;
private:
size_t chunk_size_;
std::vector<td::uint8 *> data_;
size_t ptr_ = 0;
};
MemPool mem_perm_ = MemPool(mem_chunk_size_perm);
MemPool mem_temp_ = MemPool(mem_chunk_size_temp);
size_t pdata_perm_size_;
std::vector<td::uint8 *> pdata_perm_;
size_t pdata_perm_ptr_;
std::atomic<td::uint64> reuse_{0};
public:
@ -116,7 +126,7 @@ class ValidatorSessionDescriptionImpl : public ValidatorSessionDescription {
void update_hash(const RootObject *obj, HashType hash) override;
void *alloc(size_t size, size_t align, bool temp) override;
void clear_temp_memory() override {
pdata_temp_ptr_ = 0;
mem_temp_.clear();
}
bool is_persistent(const void *ptr) const override;
HashType compute_hash(td::Slice data) const override;
@ -152,12 +162,6 @@ class ValidatorSessionDescriptionImpl : public ValidatorSessionDescription {
const ValidatorSessionOptions &opts() const override {
return opts_;
}
~ValidatorSessionDescriptionImpl() {
delete[] pdata_temp_;
for (auto &x : pdata_perm_) {
delete[] x;
}
}
};
} // namespace validatorsession

View file

@ -0,0 +1,486 @@
/*
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 <http://www.gnu.org/licenses/>.
Copyright 2017-2020 Telegram Systems LLP
*/
#include "validator-session-state.h"
#include "td/utils/Random.h"
#include "auto/tl/ton_api.hpp"
#include <set>
namespace ton {
namespace validatorsession {
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<bool>::merge(desc, l->voted_by_, r->voted_by_);
return SessionVoteCandidate::create(desc, l->block_, std::move(v));
}
//
//
// 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<bool>::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 submitted 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<const ton_api::validatorSession_message_vote*>(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<const ton_api::validatorSession_message_precommit*>(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<bool>::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<ton_api::validatorSession_round_Message*>(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<ton_api::validatorSession_round_Message> 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<ton_api::validatorSession_message_vote>(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<ton_api::validatorSession_message_precommit>(round->get_seqno(), seqno_, block_id);
}
}
return create_tl_object<ton_api::validatorSession_message_empty>(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<td::uint32>::max()) << "\n";
} else {
sb << "vote_for=NONE\n";
}
if (votes_) {
auto s = votes_->size();
sb << "votes: ";
std::vector<td::int32> 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";
}
} // namespace validatorsession
} // namespace ton

View file

@ -0,0 +1,620 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "td/utils/int_types.h"
#include "td/utils/buffer.h"
#include "adnl/utils.hpp"
#include "common/io.hpp"
#include "persistent-vector.h"
#include "validator-session-description.h"
#include "validator-session-types.h"
#include <limits>
namespace ton {
namespace validatorsession {
using HashType = ValidatorSessionDescription::HashType;
struct SessionBlockCandidateSignature : public ValidatorSessionDescription::RootObject {
public:
static auto create_hash(ValidatorSessionDescription& desc, td::Slice data) {
auto obj = create_tl_object<ton_api::hashable_blockSignature>(desc.compute_hash(data));
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
}
static bool compare(const RootObject* r, td::Slice data, HashType hash) {
if (!r || r->get_size() < sizeof(SessionBlockCandidateSignature)) {
return false;
}
auto R = static_cast<const SessionBlockCandidateSignature*>(r);
return R->hash_ == hash && R->data_.ubegin() == data.ubegin() && R->data_.size() == data.size();
}
static auto lookup(ValidatorSessionDescription& desc, td::Slice data, HashType hash, bool temp) {
auto r = desc.get_by_hash(hash, temp);
if (compare(r, data, hash)) {
desc.on_reuse();
return static_cast<const SessionBlockCandidateSignature*>(r);
}
return static_cast<const SessionBlockCandidateSignature*>(nullptr);
}
static SessionBlockCandidateSignature* create(ValidatorSessionDescription& desc, td::BufferSlice value) {
auto hash = create_hash(desc, value.as_slice());
auto d = static_cast<td::uint8*>(desc.alloc(value.size(), 8, false));
td::MutableSlice s{d, value.size()};
s.copy_from(value.as_slice());
return new (desc, true) SessionBlockCandidateSignature{desc, s, hash};
}
static const SessionBlockCandidateSignature* move_to_persistent(ValidatorSessionDescription& desc,
const SessionBlockCandidateSignature* b) {
if (desc.is_persistent(b)) {
return b;
}
CHECK(desc.is_persistent(b->data_.ubegin()));
auto r = lookup(desc, b->data_, b->hash_, false);
if (r) {
return r;
}
return new (desc, false) SessionBlockCandidateSignature{desc, b->data_, b->hash_};
}
static const SessionBlockCandidateSignature* merge(ValidatorSessionDescription& desc,
const SessionBlockCandidateSignature* l,
const SessionBlockCandidateSignature* r);
SessionBlockCandidateSignature(ValidatorSessionDescription& desc, td::Slice data, HashType hash)
: RootObject(sizeof(SessionBlockCandidateSignature)), data_{data}, hash_(std::move(hash)) {
desc.update_hash(this, hash_);
}
td::BufferSlice value() const {
return td::BufferSlice{data_};
}
td::Slice as_slice() const {
return data_;
}
auto get_hash(ValidatorSessionDescription& desc) const {
return hash_;
}
private:
const td::Slice data_;
const HashType hash_;
};
using SessionBlockCandidateSignatureVector = CntVector<const SessionBlockCandidateSignature*>;
class SentBlock : public ValidatorSessionDescription::RootObject {
public:
static HashType create_hash(ValidatorSessionDescription& desc, td::uint32 src_idx, ValidatorSessionRootHash root_hash,
ValidatorSessionFileHash file_hash,
ValidatorSessionCollatedDataFileHash collated_data_file_hash) {
auto obj = create_tl_object<ton_api::hashable_sentBlock>(src_idx, get_vs_hash(desc, root_hash),
get_vs_hash(desc, file_hash),
get_vs_hash(desc, collated_data_file_hash));
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
}
static bool compare(const RootObject* root_object, td::uint32 src_idx, const ValidatorSessionRootHash& root_hash,
const ValidatorSessionFileHash& file_hash,
const ValidatorSessionCollatedDataFileHash& collated_data_file_hash, HashType hash) {
if (!root_object || root_object->get_size() < sizeof(SentBlock)) {
return false;
}
auto obj = static_cast<const SentBlock*>(root_object);
return obj->src_idx_ == src_idx && obj->root_hash_ == root_hash && obj->file_hash_ == file_hash &&
obj->collated_data_file_hash_ == collated_data_file_hash && obj->hash_ == hash;
}
static auto lookup(ValidatorSessionDescription& desc, td::uint32 src_idx, const ValidatorSessionRootHash& root_hash,
const ValidatorSessionFileHash& file_hash,
const ValidatorSessionCollatedDataFileHash& collated_data_file_hash, HashType hash, bool temp) {
auto r = desc.get_by_hash(hash, temp);
if (compare(r, src_idx, root_hash, file_hash, collated_data_file_hash, hash)) {
desc.on_reuse();
return static_cast<const SentBlock*>(r);
}
return static_cast<const SentBlock*>(nullptr);
}
static const SentBlock* create(ValidatorSessionDescription& desc, td::uint32 src_idx,
const ValidatorSessionRootHash& root_hash, const ValidatorSessionFileHash& file_hash,
const ValidatorSessionCollatedDataFileHash& collated_data_file_hash) {
auto hash = create_hash(desc, src_idx, root_hash, file_hash, collated_data_file_hash);
auto r = lookup(desc, src_idx, root_hash, file_hash, collated_data_file_hash, hash, true);
if (r) {
return r;
}
auto candidate_id = desc.candidate_id(src_idx, root_hash, file_hash, collated_data_file_hash);
return new (desc, true) SentBlock{desc, src_idx, root_hash, file_hash, collated_data_file_hash, candidate_id, hash};
}
static const SentBlock* create(ValidatorSessionDescription& desc, const ValidatorSessionCandidateId& zero) {
CHECK(zero.is_zero());
auto hash = create_hash(desc, 0, ValidatorSessionRootHash::zero(), ValidatorSessionFileHash::zero(),
ValidatorSessionCollatedDataFileHash::zero());
return new (desc, true) SentBlock{desc,
0,
ValidatorSessionRootHash::zero(),
ValidatorSessionFileHash::zero(),
ValidatorSessionCollatedDataFileHash::zero(),
zero,
hash};
}
static const SentBlock* move_to_persistent(ValidatorSessionDescription& desc, const SentBlock* b) {
if (desc.is_persistent(b)) {
return b;
}
auto r = lookup(desc, b->src_idx_, b->root_hash_, b->file_hash_, b->collated_data_file_hash_, b->hash_, false);
if (r) {
return r;
}
return new (desc, false) SentBlock{
desc, b->src_idx_, b->root_hash_, b->file_hash_, b->collated_data_file_hash_, b->candidate_id_, b->hash_};
}
SentBlock(ValidatorSessionDescription& desc, td::uint32 src_idx, ValidatorSessionRootHash root_hash,
ValidatorSessionFileHash file_hash, ValidatorSessionCollatedDataFileHash collated_data_file_hash,
ValidatorSessionCandidateId candidate_id, HashType hash)
: RootObject(sizeof(SentBlock))
, src_idx_(src_idx)
, root_hash_(std::move(root_hash))
, file_hash_(std::move(file_hash))
, collated_data_file_hash_(std::move(collated_data_file_hash))
, candidate_id_(candidate_id)
, hash_(std::move(hash)) {
desc.update_hash(this, hash_);
}
auto get_src_idx() const {
return src_idx_;
}
auto get_root_hash() const {
return root_hash_;
}
auto get_file_hash() const {
return file_hash_;
}
auto get_collated_data_file_hash() const {
return collated_data_file_hash_;
}
static ValidatorSessionCandidateId get_block_id(const SentBlock* block) {
return block ? block->candidate_id_ : skip_round_candidate_id();
}
HashType get_hash(ValidatorSessionDescription& desc) const {
return hash_;
}
bool operator<(const SentBlock& block) const {
if (src_idx_ < block.src_idx_) {
return true;
}
if (src_idx_ > block.src_idx_) {
return false;
}
if (candidate_id_ < block.candidate_id_) {
return true;
}
return false;
}
struct Compare {
bool operator()(const SentBlock* a, const SentBlock* b) const {
return *a < *b;
}
};
private:
const td::uint32 src_idx_;
const ValidatorSessionRootHash root_hash_;
const ValidatorSessionFileHash file_hash_;
const ValidatorSessionCollatedDataFileHash collated_data_file_hash_;
const ValidatorSessionCandidateId candidate_id_;
const HashType hash_;
};
class SessionBlockCandidate : public ValidatorSessionDescription::RootObject {
public:
static HashType create_hash(ValidatorSessionDescription& desc, HashType block, HashType approved) {
auto obj = create_tl_object<ton_api::hashable_blockCandidate>(block, approved);
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
}
static bool compare(const RootObject* r, const SentBlock* block, const SessionBlockCandidateSignatureVector* approved,
HashType hash) {
if (!r || r->get_size() < sizeof(SessionBlockCandidate)) {
return false;
}
auto R = static_cast<const SessionBlockCandidate*>(r);
return R->block_ == block && R->approved_by_ == approved && R->hash_ == hash;
}
static auto lookup(ValidatorSessionDescription& desc, const SentBlock* block,
const SessionBlockCandidateSignatureVector* approved, HashType hash, bool temp) {
auto r = desc.get_by_hash(hash, temp);
if (compare(r, block, approved, hash)) {
desc.on_reuse();
return static_cast<const SessionBlockCandidate*>(r);
}
return static_cast<const SessionBlockCandidate*>(nullptr);
}
static const SessionBlockCandidate* create(ValidatorSessionDescription& desc, const SentBlock* block,
const SessionBlockCandidateSignatureVector* approved) {
auto hash = create_hash(desc, get_vs_hash(desc, block), get_vs_hash(desc, approved));
auto r = lookup(desc, block, approved, hash, true);
if (r) {
return r;
}
return new (desc, true) SessionBlockCandidate(desc, block, approved, hash);
}
static const SessionBlockCandidate* create(ValidatorSessionDescription& desc, const SentBlock* block) {
std::vector<const SessionBlockCandidateSignature*> v;
v.resize(desc.get_total_nodes(), nullptr);
auto vec = SessionBlockCandidateSignatureVector::create(desc, std::move(v));
return create(desc, block, vec);
}
static const SessionBlockCandidate* move_to_persistent(ValidatorSessionDescription& desc,
const SessionBlockCandidate* b) {
if (desc.is_persistent(b)) {
return b;
}
auto block = SentBlock::move_to_persistent(desc, b->block_);
auto approved = SessionBlockCandidateSignatureVector::move_to_persistent(desc, b->approved_by_);
auto r = lookup(desc, block, approved, b->hash_, false);
if (r) {
return r;
}
return new (desc, false) SessionBlockCandidate{desc, block, approved, b->hash_};
}
SessionBlockCandidate(ValidatorSessionDescription& desc, const SentBlock* block,
const SessionBlockCandidateSignatureVector* approved, HashType hash)
: RootObject{sizeof(SessionBlockCandidate)}, block_(block), approved_by_(approved), hash_(hash) {
desc.update_hash(this, hash_);
}
static const SessionBlockCandidate* merge(ValidatorSessionDescription& desc, const SessionBlockCandidate* l,
const SessionBlockCandidate* r);
auto get_block() const {
return block_;
}
auto get_id() const {
return SentBlock::get_block_id(block_);
}
auto get_src_idx() const {
return block_ ? block_->get_src_idx() : std::numeric_limits<td::uint32>::max();
}
bool check_block_is_approved_by(td::uint32 src_idx) const {
return approved_by_->at(src_idx);
}
bool check_block_is_approved(ValidatorSessionDescription& desc) const;
auto get_hash(ValidatorSessionDescription& desc) const {
return hash_;
}
auto get_approvers_list() const {
return approved_by_;
}
static const SessionBlockCandidate* push(ValidatorSessionDescription& desc, const SessionBlockCandidate* state,
td::uint32 src_idx, const SessionBlockCandidateSignature* sig) {
CHECK(state);
if (state->approved_by_->at(src_idx)) {
return state;
}
return create(desc, state->block_,
SessionBlockCandidateSignatureVector::change(desc, state->approved_by_, src_idx, sig));
}
class Compare {
public:
bool operator()(const SessionBlockCandidate* l, const SessionBlockCandidate* r) {
return l->get_id() < r->get_id();
}
};
private:
const SentBlock* block_;
const SessionBlockCandidateSignatureVector* approved_by_;
const HashType hash_;
};
class SessionVoteCandidate : public ValidatorSessionDescription::RootObject {
public:
static HashType create_hash(ValidatorSessionDescription& desc, HashType block, HashType voted) {
auto obj = create_tl_object<ton_api::hashable_blockVoteCandidate>(block, voted);
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
}
static bool compare(const RootObject* r, const SentBlock* block, const CntVector<bool>* voted, HashType hash) {
if (!r || r->get_size() < sizeof(SessionVoteCandidate)) {
return false;
}
auto R = static_cast<const SessionVoteCandidate*>(r);
return R->block_ == block && R->voted_by_ == voted && R->hash_ == hash;
}
static auto lookup(ValidatorSessionDescription& desc, const SentBlock* block, const CntVector<bool>* voted,
HashType hash, bool temp) {
auto r = desc.get_by_hash(hash, temp);
if (compare(r, block, voted, hash)) {
desc.on_reuse();
return static_cast<const SessionVoteCandidate*>(r);
}
return static_cast<const SessionVoteCandidate*>(nullptr);
}
static const SessionVoteCandidate* create(ValidatorSessionDescription& desc, const SentBlock* block,
const CntVector<bool>* voted) {
auto hash = create_hash(desc, get_vs_hash(desc, block), get_vs_hash(desc, voted));
auto r = lookup(desc, block, voted, hash, true);
if (r) {
return r;
}
return new (desc, true) SessionVoteCandidate(desc, block, voted, hash);
}
static const SessionVoteCandidate* create(ValidatorSessionDescription& desc, const SentBlock* block) {
std::vector<bool> v;
v.resize(desc.get_total_nodes(), false);
auto vec = CntVector<bool>::create(desc, std::move(v));
return create(desc, block, vec);
}
static const SessionVoteCandidate* move_to_persistent(ValidatorSessionDescription& desc,
const SessionVoteCandidate* b) {
if (desc.is_persistent(b)) {
return b;
}
auto block = SentBlock::move_to_persistent(desc, b->block_);
auto voted = CntVector<bool>::move_to_persistent(desc, b->voted_by_);
auto r = lookup(desc, block, voted, b->hash_, false);
if (r) {
return r;
}
return new (desc, false) SessionVoteCandidate{desc, block, voted, b->hash_};
}
SessionVoteCandidate(ValidatorSessionDescription& desc, const SentBlock* block, const CntVector<bool>* voted,
HashType hash)
: RootObject{sizeof(SessionVoteCandidate)}, block_(block), voted_by_(voted), hash_(hash) {
desc.update_hash(this, hash_);
}
static const SessionVoteCandidate* merge(ValidatorSessionDescription& desc, const SessionVoteCandidate* l,
const SessionVoteCandidate* r);
auto get_block() const {
return block_;
}
auto get_id() const {
return SentBlock::get_block_id(block_);
}
auto get_src_idx() const {
return block_ ? block_->get_src_idx() : std::numeric_limits<td::uint32>::max();
}
bool check_block_is_voted_by(td::uint32 src_idx) const {
return voted_by_->at(src_idx);
}
bool check_block_is_voted(ValidatorSessionDescription& desc) const;
auto get_hash(ValidatorSessionDescription& desc) const {
return hash_;
}
auto get_voters_list() const {
return voted_by_;
}
static const SessionVoteCandidate* push(ValidatorSessionDescription& desc, const SessionVoteCandidate* state,
td::uint32 src_idx) {
CHECK(state);
if (state->voted_by_->at(src_idx)) {
return state;
}
return create(desc, state->block_, CntVector<bool>::change(desc, state->voted_by_, src_idx, true));
}
class Compare {
public:
bool operator()(const SessionVoteCandidate* l, const SessionVoteCandidate* r) {
return l->get_id() < r->get_id();
}
};
private:
const SentBlock* block_;
const CntVector<bool>* voted_by_;
const HashType hash_;
};
using VoteVector = CntSortedVector<const SessionVoteCandidate*, SessionVoteCandidate::Compare>;
using ApproveVector = CntSortedVector<const SessionBlockCandidate*, SessionBlockCandidate::Compare>;
class ValidatorSessionRoundState;
class ValidatorSessionRoundAttemptState : public ValidatorSessionDescription::RootObject {
public:
static HashType create_hash(ValidatorSessionDescription& desc, td::uint32 seqno, HashType votes,
HashType precommitted, bool vote_for_inited, HashType vote_for) {
auto obj = create_tl_object<ton_api::hashable_validatorSessionRoundAttempt>(seqno, votes, precommitted,
vote_for_inited, vote_for);
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
}
static bool compare(const RootObject* r, td::uint32 seqno, const VoteVector* votes,
const CntVector<bool>* precommitted, const SentBlock* vote_for, bool vote_for_inited,
HashType hash) {
if (!r || r->get_size() < sizeof(ValidatorSessionRoundAttemptState)) {
return false;
}
auto R = static_cast<const ValidatorSessionRoundAttemptState*>(r);
return R->seqno_ == seqno && R->votes_ == votes && R->precommitted_ == precommitted && R->vote_for_ == vote_for &&
R->vote_for_inited_ == vote_for_inited && R->hash_ == hash;
}
static auto lookup(ValidatorSessionDescription& desc, td::uint32 seqno, const VoteVector* votes,
const CntVector<bool>* precommitted, const SentBlock* vote_for, bool vote_for_inited,
HashType hash, bool temp) {
auto r = desc.get_by_hash(hash, temp);
if (compare(r, seqno, votes, precommitted, vote_for, vote_for_inited, hash)) {
desc.on_reuse();
return static_cast<const ValidatorSessionRoundAttemptState*>(r);
}
return static_cast<const ValidatorSessionRoundAttemptState*>(nullptr);
}
static const ValidatorSessionRoundAttemptState* move_to_persistent(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* b) {
if (desc.is_persistent(b)) {
return b;
}
auto votes = VoteVector::move_to_persistent(desc, b->votes_);
auto precommitted = CntVector<bool>::move_to_persistent(desc, b->precommitted_);
auto vote_for = SentBlock::move_to_persistent(desc, b->vote_for_);
auto r = lookup(desc, b->seqno_, votes, precommitted, vote_for, b->vote_for_inited_, b->hash_, false);
if (r) {
return r;
}
return new (desc, false) ValidatorSessionRoundAttemptState{desc, b->seqno_, votes, precommitted,
vote_for, b->vote_for_inited_, b->hash_};
}
static const ValidatorSessionRoundAttemptState* create(ValidatorSessionDescription& desc, td::uint32 seqno,
const VoteVector* votes, const CntVector<bool>* precommitted,
const SentBlock* vote_for, bool vote_for_inited) {
auto hash = create_hash(desc, seqno, get_vs_hash(desc, votes), get_vs_hash(desc, precommitted),
get_vs_hash(desc, vote_for), vote_for_inited);
auto r = lookup(desc, seqno, votes, precommitted, vote_for, vote_for_inited, hash, true);
if (r) {
return r;
}
return new (desc, true)
ValidatorSessionRoundAttemptState(desc, seqno, votes, precommitted, vote_for, vote_for_inited, hash);
}
static const ValidatorSessionRoundAttemptState* create(ValidatorSessionDescription& desc, td::uint32 seqno) {
std::vector<bool> x;
x.resize(desc.get_total_nodes(), false);
auto p = CntVector<bool>::create(desc, std::move(x));
return create(desc, seqno, nullptr, p, nullptr, false);
}
ValidatorSessionRoundAttemptState(ValidatorSessionDescription& desc, td::uint32 seqno, const VoteVector* votes,
const CntVector<bool>* precommitted, const SentBlock* vote_for,
bool vote_for_inited, HashType hash)
: RootObject{sizeof(ValidatorSessionRoundAttemptState)}
, seqno_(seqno)
, votes_(votes)
, precommitted_(precommitted)
, vote_for_(vote_for)
, vote_for_inited_(vote_for_inited)
, hash_(std::move(hash)) {
desc.update_hash(this, hash_);
}
auto get_hash(ValidatorSessionDescription& desc) const {
return hash_;
}
auto get_seqno() const {
return seqno_;
}
auto get_votes() const {
return votes_;
}
auto get_precommits() const {
return precommitted_;
}
const SentBlock* get_voted_block(ValidatorSessionDescription& desc, bool& f) const;
const SentBlock* get_vote_for_block(ValidatorSessionDescription& desc, bool& f) const {
f = vote_for_inited_;
return vote_for_;
}
bool check_attempt_is_precommitted(ValidatorSessionDescription& desc) const;
bool check_vote_received_from(td::uint32 src_idx) const;
bool check_precommit_received_from(td::uint32 src_idx) const;
bool operator<(const ValidatorSessionRoundAttemptState& right) const {
return seqno_ < right.seqno_;
}
struct Compare {
bool operator()(const ValidatorSessionRoundAttemptState* a, const ValidatorSessionRoundAttemptState* b) const {
return *a < *b;
}
};
static const ValidatorSessionRoundAttemptState* merge(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* left,
const ValidatorSessionRoundAttemptState* right);
static const ValidatorSessionRoundAttemptState* action(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* state,
td::uint32 src_idx, td::uint32 att,
const ton_api::validatorSession_message_voteFor& act,
const ValidatorSessionRoundState* round);
static const ValidatorSessionRoundAttemptState* action(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* state,
td::uint32 src_idx, td::uint32 att,
const ton_api::validatorSession_message_vote& act,
const ValidatorSessionRoundState* round);
static const ValidatorSessionRoundAttemptState* action(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* state,
td::uint32 src_idx, td::uint32 att,
const ton_api::validatorSession_message_precommit& act,
const ValidatorSessionRoundState* round);
static const ValidatorSessionRoundAttemptState* action(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* state,
td::uint32 src_idx, td::uint32 att,
const ton_api::validatorSession_message_empty& act,
const ValidatorSessionRoundState* round);
static const ValidatorSessionRoundAttemptState* action(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* state,
td::uint32 src_idx, td::uint32 att,
const ton_api::validatorSession_round_Message* action,
const ValidatorSessionRoundState* round);
template <class T>
static const ValidatorSessionRoundAttemptState* action(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* state,
td::uint32 src_idx, td::uint32 att, const T& action,
const ValidatorSessionRoundState* round) {
UNREACHABLE();
}
static const ValidatorSessionRoundAttemptState* try_vote(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* state,
td::uint32 src_idx, td::uint32 att,
const ValidatorSessionRoundState* round,
const ton_api::validatorSession_round_Message* cmp,
bool& made);
static const ValidatorSessionRoundAttemptState* try_precommit(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* state,
td::uint32 src_idx, td::uint32 att,
const ValidatorSessionRoundState* round,
const ton_api::validatorSession_round_Message* cmp,
bool& made);
static const ValidatorSessionRoundAttemptState* make_one(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* state,
td::uint32 src_idx, td::uint32 att,
const ValidatorSessionRoundState* round,
const ton_api::validatorSession_round_Message* cmp,
bool& made);
tl_object_ptr<ton_api::validatorSession_round_Message> create_action(ValidatorSessionDescription& desc,
const ValidatorSessionRoundState* round,
td::uint32 src_idx, td::uint32 att) const;
void dump(ValidatorSessionDescription& desc, td::StringBuilder& sb) const;
private:
const td::uint32 seqno_;
const VoteVector* votes_;
const CntVector<bool>* precommitted_;
const SentBlock* vote_for_;
const bool vote_for_inited_;
const HashType hash_;
};
using AttemptVector =
CntSortedVector<const ValidatorSessionRoundAttemptState*, ValidatorSessionRoundAttemptState::Compare>;
} // namespace validatorsession
} // namespace ton

View file

@ -67,14 +67,6 @@ 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<ton_api::validatorSession_round_Message*>(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;
@ -108,118 +100,12 @@ static const SessionBlockCandidate* get_candidate(const ApproveVector* vec, Vali
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<bool>::merge(desc, l->voted_by_, r->voted_by_);
return SessionVoteCandidate::create(desc, l->block_, std::move(v));
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<ton_api::validatorSession_round_Message*>(message),
[&](auto& obj) { round = obj.round_; });
CHECK(is_called);
return round;
}
//
@ -374,346 +260,6 @@ const ValidatorSessionOldRoundState* ValidatorSessionOldRoundState::action(
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<bool>::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<const ton_api::validatorSession_message_vote*>(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<const ton_api::validatorSession_message_precommit*>(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<bool>::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<ton_api::validatorSession_round_Message*>(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<ton_api::validatorSession_round_Message> 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<ton_api::validatorSession_message_vote>(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<ton_api::validatorSession_message_precommit>(round->get_seqno(), seqno_, block_id);
}
}
return create_tl_object<ton_api::validatorSession_message_empty>(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<td::uint32>::max()) << "\n";
} else {
sb << "vote_for=NONE\n";
}
if (votes_) {
auto s = votes_->size();
sb << "votes: ";
std::vector<td::int32> 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
@ -821,8 +367,6 @@ const ValidatorSessionRoundState* ValidatorSessionRoundState::merge(ValidatorSes
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);
@ -985,9 +529,6 @@ const ValidatorSessionRoundState* ValidatorSessionRoundState::forward_action_to_
attempt = ValidatorSessionRoundAttemptState::create(desc, att);
}
bool had_voted_block;
attempt->get_voted_block(desc, had_voted_block);
ton_api::downcast_call(*const_cast<ton_api::validatorSession_round_Message*>(act), [&](auto& obj) {
attempt = ValidatorSessionRoundAttemptState::action(desc, attempt, src_idx, att, obj, state);
});
@ -1302,6 +843,8 @@ std::vector<const SentBlock*> ValidatorSessionRoundState::choose_blocks_to_appro
CHECK(prio >= 0);
td::uint32 blk_src_idx = B->get_src_idx();
if (was_source.count(blk_src_idx) > 0) {
// Any honest validator submits at most one block in a round
// Therefore, we can ignore all blocks from a node if it submits more than one
x[prio] = nullptr;
} else {
was_source.insert(blk_src_idx);

View file

@ -24,15 +24,15 @@
#include "common/io.hpp"
#include "persistent-vector.h"
#include "validator-session-description.h"
#include "validator-session-types.h"
#include "validator-session-round-attempt-state.h"
#include <limits>
namespace td {
td::StringBuilder& operator<<(td::StringBuilder& sb, const ton::ton_api::validatorSession_round_Message& message);
td::StringBuilder& operator<<(td::StringBuilder& sb, const ton::ton_api::validatorSession_round_Message* message);
}
@ -41,410 +41,6 @@ namespace ton {
namespace validatorsession {
using HashType = ValidatorSessionDescription::HashType;
constexpr int VERBOSITY_NAME(VALIDATOR_SESSION_WARNING) = verbosity_WARNING;
constexpr int VERBOSITY_NAME(VALIDATOR_SESSION_NOTICE) = verbosity_DEBUG;
constexpr int VERBOSITY_NAME(VALIDATOR_SESSION_INFO) = verbosity_DEBUG;
constexpr int VERBOSITY_NAME(VALIDATOR_SESSION_DEBUG) = verbosity_DEBUG;
constexpr int VERBOSITY_NAME(VALIDATOR_SESSION_EXTRA_DEBUG) = verbosity_DEBUG + 1;
struct SessionBlockCandidateSignature : public ValidatorSessionDescription::RootObject {
public:
static auto create_hash(ValidatorSessionDescription& desc, td::Slice data) {
auto obj = create_tl_object<ton_api::hashable_blockSignature>(desc.compute_hash(data));
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
}
static bool compare(const RootObject* r, td::Slice data, HashType hash) {
if (!r || r->get_size() < sizeof(SessionBlockCandidateSignature)) {
return false;
}
auto R = static_cast<const SessionBlockCandidateSignature*>(r);
return R->hash_ == hash && R->data_.ubegin() == data.ubegin() && R->data_.size() == data.size();
}
static auto lookup(ValidatorSessionDescription& desc, td::Slice data, HashType hash, bool temp) {
auto r = desc.get_by_hash(hash, temp);
if (compare(r, data, hash)) {
desc.on_reuse();
return static_cast<const SessionBlockCandidateSignature*>(r);
}
return static_cast<const SessionBlockCandidateSignature*>(nullptr);
}
static SessionBlockCandidateSignature* create(ValidatorSessionDescription& desc, td::BufferSlice value) {
auto hash = create_hash(desc, value.as_slice());
auto d = static_cast<td::uint8*>(desc.alloc(value.size(), 8, false));
td::MutableSlice s{d, value.size()};
s.copy_from(value.as_slice());
return new (desc, true) SessionBlockCandidateSignature{desc, s, hash};
}
static const SessionBlockCandidateSignature* move_to_persistent(ValidatorSessionDescription& desc,
const SessionBlockCandidateSignature* b) {
if (desc.is_persistent(b)) {
return b;
}
CHECK(desc.is_persistent(b->data_.ubegin()));
auto r = lookup(desc, b->data_, b->hash_, false);
if (r) {
return r;
}
return new (desc, false) SessionBlockCandidateSignature{desc, b->data_, b->hash_};
}
static const SessionBlockCandidateSignature* merge(ValidatorSessionDescription& desc,
const SessionBlockCandidateSignature* l,
const SessionBlockCandidateSignature* r);
SessionBlockCandidateSignature(ValidatorSessionDescription& desc, td::Slice data, HashType hash)
: RootObject(sizeof(SessionBlockCandidateSignature)), data_{data}, hash_(std::move(hash)) {
desc.update_hash(this, hash_);
}
td::BufferSlice value() const {
return td::BufferSlice{data_};
}
td::Slice as_slice() const {
return data_;
}
auto get_hash(ValidatorSessionDescription& desc) const {
return hash_;
}
private:
const td::Slice data_;
const HashType hash_;
};
using SessionBlockCandidateSignatureVector = CntVector<const SessionBlockCandidateSignature*>;
class SentBlock : public ValidatorSessionDescription::RootObject {
public:
static HashType create_hash(ValidatorSessionDescription& desc, td::uint32 src_idx, ValidatorSessionRootHash root_hash,
ValidatorSessionFileHash file_hash,
ValidatorSessionCollatedDataFileHash collated_data_file_hash) {
auto obj = create_tl_object<ton_api::hashable_sentBlock>(src_idx, get_vs_hash(desc, root_hash),
get_vs_hash(desc, file_hash),
get_vs_hash(desc, collated_data_file_hash));
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
}
static bool compare(const RootObject* root_object, td::uint32 src_idx, const ValidatorSessionRootHash& root_hash,
const ValidatorSessionFileHash& file_hash,
const ValidatorSessionCollatedDataFileHash& collated_data_file_hash, HashType hash) {
if (!root_object || root_object->get_size() < sizeof(SentBlock)) {
return false;
}
auto obj = static_cast<const SentBlock*>(root_object);
return obj->src_idx_ == src_idx && obj->root_hash_ == root_hash && obj->file_hash_ == file_hash &&
obj->collated_data_file_hash_ == collated_data_file_hash && obj->hash_ == hash;
}
static auto lookup(ValidatorSessionDescription& desc, td::uint32 src_idx, const ValidatorSessionRootHash& root_hash,
const ValidatorSessionFileHash& file_hash,
const ValidatorSessionCollatedDataFileHash& collated_data_file_hash, HashType hash, bool temp) {
auto r = desc.get_by_hash(hash, temp);
if (compare(r, src_idx, root_hash, file_hash, collated_data_file_hash, hash)) {
desc.on_reuse();
return static_cast<const SentBlock*>(r);
}
return static_cast<const SentBlock*>(nullptr);
}
static const SentBlock* create(ValidatorSessionDescription& desc, td::uint32 src_idx,
const ValidatorSessionRootHash& root_hash, const ValidatorSessionFileHash& file_hash,
const ValidatorSessionCollatedDataFileHash& collated_data_file_hash) {
auto hash = create_hash(desc, src_idx, root_hash, file_hash, collated_data_file_hash);
auto r = lookup(desc, src_idx, root_hash, file_hash, collated_data_file_hash, hash, true);
if (r) {
return r;
}
auto candidate_id = desc.candidate_id(src_idx, root_hash, file_hash, collated_data_file_hash);
return new (desc, true) SentBlock{desc, src_idx, root_hash, file_hash, collated_data_file_hash, candidate_id, hash};
}
static const SentBlock* create(ValidatorSessionDescription& desc, const ValidatorSessionCandidateId& zero) {
CHECK(zero.is_zero());
auto hash = create_hash(desc, 0, ValidatorSessionRootHash::zero(), ValidatorSessionFileHash::zero(),
ValidatorSessionCollatedDataFileHash::zero());
return new (desc, true) SentBlock{desc,
0,
ValidatorSessionRootHash::zero(),
ValidatorSessionFileHash::zero(),
ValidatorSessionCollatedDataFileHash::zero(),
zero,
hash};
}
static const SentBlock* move_to_persistent(ValidatorSessionDescription& desc, const SentBlock* b) {
if (desc.is_persistent(b)) {
return b;
}
auto r = lookup(desc, b->src_idx_, b->root_hash_, b->file_hash_, b->collated_data_file_hash_, b->hash_, false);
if (r) {
return r;
}
return new (desc, false) SentBlock{
desc, b->src_idx_, b->root_hash_, b->file_hash_, b->collated_data_file_hash_, b->candidate_id_, b->hash_};
}
SentBlock(ValidatorSessionDescription& desc, td::uint32 src_idx, ValidatorSessionRootHash root_hash,
ValidatorSessionFileHash file_hash, ValidatorSessionCollatedDataFileHash collated_data_file_hash,
ValidatorSessionCandidateId candidate_id, HashType hash)
: RootObject(sizeof(SentBlock))
, src_idx_(src_idx)
, root_hash_(std::move(root_hash))
, file_hash_(std::move(file_hash))
, collated_data_file_hash_(std::move(collated_data_file_hash))
, candidate_id_(candidate_id)
, hash_(std::move(hash)) {
desc.update_hash(this, hash_);
}
auto get_src_idx() const {
return src_idx_;
}
auto get_root_hash() const {
return root_hash_;
}
auto get_file_hash() const {
return file_hash_;
}
auto get_collated_data_file_hash() const {
return collated_data_file_hash_;
}
static ValidatorSessionCandidateId get_block_id(const SentBlock* block) {
return block ? block->candidate_id_ : skip_round_candidate_id();
}
HashType get_hash(ValidatorSessionDescription& desc) const {
return hash_;
}
bool operator<(const SentBlock& block) const {
if (src_idx_ < block.src_idx_) {
return true;
}
if (src_idx_ > block.src_idx_) {
return false;
}
if (candidate_id_ < block.candidate_id_) {
return true;
}
return false;
}
struct Compare {
bool operator()(const SentBlock* a, const SentBlock* b) const {
return *a < *b;
}
};
private:
const td::uint32 src_idx_;
const ValidatorSessionRootHash root_hash_;
const ValidatorSessionFileHash file_hash_;
const ValidatorSessionCollatedDataFileHash collated_data_file_hash_;
const ValidatorSessionCandidateId candidate_id_;
const HashType hash_;
};
class SessionBlockCandidate : public ValidatorSessionDescription::RootObject {
public:
static HashType create_hash(ValidatorSessionDescription& desc, HashType block, HashType approved) {
auto obj = create_tl_object<ton_api::hashable_blockCandidate>(block, approved);
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
}
static bool compare(const RootObject* r, const SentBlock* block, const SessionBlockCandidateSignatureVector* approved,
HashType hash) {
if (!r || r->get_size() < sizeof(SessionBlockCandidate)) {
return false;
}
auto R = static_cast<const SessionBlockCandidate*>(r);
return R->block_ == block && R->approved_by_ == approved && R->hash_ == hash;
}
static auto lookup(ValidatorSessionDescription& desc, const SentBlock* block,
const SessionBlockCandidateSignatureVector* approved, HashType hash, bool temp) {
auto r = desc.get_by_hash(hash, temp);
if (compare(r, block, approved, hash)) {
desc.on_reuse();
return static_cast<const SessionBlockCandidate*>(r);
}
return static_cast<const SessionBlockCandidate*>(nullptr);
}
static const SessionBlockCandidate* create(ValidatorSessionDescription& desc, const SentBlock* block,
const SessionBlockCandidateSignatureVector* approved) {
auto hash = create_hash(desc, get_vs_hash(desc, block), get_vs_hash(desc, approved));
auto r = lookup(desc, block, approved, hash, true);
if (r) {
return r;
}
return new (desc, true) SessionBlockCandidate(desc, block, approved, hash);
}
static const SessionBlockCandidate* create(ValidatorSessionDescription& desc, const SentBlock* block) {
std::vector<const SessionBlockCandidateSignature*> v;
v.resize(desc.get_total_nodes(), nullptr);
auto vec = SessionBlockCandidateSignatureVector::create(desc, std::move(v));
return create(desc, block, vec);
}
static const SessionBlockCandidate* move_to_persistent(ValidatorSessionDescription& desc,
const SessionBlockCandidate* b) {
if (desc.is_persistent(b)) {
return b;
}
auto block = SentBlock::move_to_persistent(desc, b->block_);
auto approved = SessionBlockCandidateSignatureVector::move_to_persistent(desc, b->approved_by_);
auto r = lookup(desc, block, approved, b->hash_, false);
if (r) {
return r;
}
return new (desc, false) SessionBlockCandidate{desc, block, approved, b->hash_};
}
SessionBlockCandidate(ValidatorSessionDescription& desc, const SentBlock* block,
const SessionBlockCandidateSignatureVector* approved, HashType hash)
: RootObject{sizeof(SessionBlockCandidate)}, block_(block), approved_by_(approved), hash_(hash) {
desc.update_hash(this, hash_);
}
static const SessionBlockCandidate* merge(ValidatorSessionDescription& desc, const SessionBlockCandidate* l,
const SessionBlockCandidate* r);
auto get_block() const {
return block_;
}
auto get_id() const {
return SentBlock::get_block_id(block_);
}
auto get_src_idx() const {
return block_ ? block_->get_src_idx() : std::numeric_limits<td::uint32>::max();
}
bool check_block_is_approved_by(td::uint32 src_idx) const {
return approved_by_->at(src_idx);
}
bool check_block_is_approved(ValidatorSessionDescription& desc) const;
auto get_hash(ValidatorSessionDescription& desc) const {
return hash_;
}
auto get_approvers_list() const {
return approved_by_;
}
static const SessionBlockCandidate* push(ValidatorSessionDescription& desc, const SessionBlockCandidate* state,
td::uint32 src_idx, const SessionBlockCandidateSignature* sig) {
CHECK(state);
if (state->approved_by_->at(src_idx)) {
return state;
}
return create(desc, state->block_,
SessionBlockCandidateSignatureVector::change(desc, state->approved_by_, src_idx, sig));
}
class Compare {
public:
bool operator()(const SessionBlockCandidate* l, const SessionBlockCandidate* r) {
return l->get_id() < r->get_id();
}
};
private:
const SentBlock* block_;
const SessionBlockCandidateSignatureVector* approved_by_;
const HashType hash_;
};
class SessionVoteCandidate : public ValidatorSessionDescription::RootObject {
public:
static HashType create_hash(ValidatorSessionDescription& desc, HashType block, HashType voted) {
auto obj = create_tl_object<ton_api::hashable_blockVoteCandidate>(block, voted);
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
}
static bool compare(const RootObject* r, const SentBlock* block, const CntVector<bool>* voted, HashType hash) {
if (!r || r->get_size() < sizeof(SessionVoteCandidate)) {
return false;
}
auto R = static_cast<const SessionVoteCandidate*>(r);
return R->block_ == block && R->voted_by_ == voted && R->hash_ == hash;
}
static auto lookup(ValidatorSessionDescription& desc, const SentBlock* block, const CntVector<bool>* voted,
HashType hash, bool temp) {
auto r = desc.get_by_hash(hash, temp);
if (compare(r, block, voted, hash)) {
desc.on_reuse();
return static_cast<const SessionVoteCandidate*>(r);
}
return static_cast<const SessionVoteCandidate*>(nullptr);
}
static const SessionVoteCandidate* create(ValidatorSessionDescription& desc, const SentBlock* block,
const CntVector<bool>* voted) {
auto hash = create_hash(desc, get_vs_hash(desc, block), get_vs_hash(desc, voted));
auto r = lookup(desc, block, voted, hash, true);
if (r) {
return r;
}
return new (desc, true) SessionVoteCandidate(desc, block, voted, hash);
}
static const SessionVoteCandidate* create(ValidatorSessionDescription& desc, const SentBlock* block) {
std::vector<bool> v;
v.resize(desc.get_total_nodes(), false);
auto vec = CntVector<bool>::create(desc, std::move(v));
return create(desc, block, vec);
}
static const SessionVoteCandidate* move_to_persistent(ValidatorSessionDescription& desc,
const SessionVoteCandidate* b) {
if (desc.is_persistent(b)) {
return b;
}
auto block = SentBlock::move_to_persistent(desc, b->block_);
auto voted = CntVector<bool>::move_to_persistent(desc, b->voted_by_);
auto r = lookup(desc, block, voted, b->hash_, false);
if (r) {
return r;
}
return new (desc, false) SessionVoteCandidate{desc, block, voted, b->hash_};
}
SessionVoteCandidate(ValidatorSessionDescription& desc, const SentBlock* block, const CntVector<bool>* voted,
HashType hash)
: RootObject{sizeof(SessionVoteCandidate)}, block_(block), voted_by_(voted), hash_(hash) {
desc.update_hash(this, hash_);
}
static const SessionVoteCandidate* merge(ValidatorSessionDescription& desc, const SessionVoteCandidate* l,
const SessionVoteCandidate* r);
auto get_block() const {
return block_;
}
auto get_id() const {
return SentBlock::get_block_id(block_);
}
auto get_src_idx() const {
return block_ ? block_->get_src_idx() : std::numeric_limits<td::uint32>::max();
}
bool check_block_is_voted_by(td::uint32 src_idx) const {
return voted_by_->at(src_idx);
}
bool check_block_is_voted(ValidatorSessionDescription& desc) const;
auto get_hash(ValidatorSessionDescription& desc) const {
return hash_;
}
auto get_voters_list() const {
return voted_by_;
}
static const SessionVoteCandidate* push(ValidatorSessionDescription& desc, const SessionVoteCandidate* state,
td::uint32 src_idx) {
CHECK(state);
if (state->voted_by_->at(src_idx)) {
return state;
}
return create(desc, state->block_, CntVector<bool>::change(desc, state->voted_by_, src_idx, true));
}
class Compare {
public:
bool operator()(const SessionVoteCandidate* l, const SessionVoteCandidate* r) {
return l->get_id() < r->get_id();
}
};
private:
const SentBlock* block_;
const CntVector<bool>* voted_by_;
const HashType hash_;
};
//using SentBlockVector = CntSortedVector<const SentBlock*, SentBlock::Compare>;
class ValidatorSessionRoundState;
class ValidatorSessionOldRoundState : public ValidatorSessionDescription::RootObject {
public:
static HashType create_hash(ValidatorSessionDescription& desc, td::uint32 seqno, HashType block, HashType signatures,
@ -582,188 +178,6 @@ class ValidatorSessionOldRoundState : public ValidatorSessionDescription::RootOb
const HashType hash_;
};
using VoteVector = CntSortedVector<const SessionVoteCandidate*, SessionVoteCandidate::Compare>;
using ApproveVector = CntSortedVector<const SessionBlockCandidate*, SessionBlockCandidate::Compare>;
class ValidatorSessionRoundAttemptState : public ValidatorSessionDescription::RootObject {
public:
static HashType create_hash(ValidatorSessionDescription& desc, td::uint32 seqno, HashType votes,
HashType precommitted, bool vote_for_inited, HashType vote_for) {
auto obj = create_tl_object<ton_api::hashable_validatorSessionRoundAttempt>(seqno, votes, precommitted,
vote_for_inited, vote_for);
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
}
static bool compare(const RootObject* r, td::uint32 seqno, const VoteVector* votes,
const CntVector<bool>* precommitted, const SentBlock* vote_for, bool vote_for_inited,
HashType hash) {
if (!r || r->get_size() < sizeof(ValidatorSessionRoundAttemptState)) {
return false;
}
auto R = static_cast<const ValidatorSessionRoundAttemptState*>(r);
return R->seqno_ == seqno && R->votes_ == votes && R->precommitted_ == precommitted && R->vote_for_ == vote_for &&
R->vote_for_inited_ == vote_for_inited && R->hash_ == hash;
}
static auto lookup(ValidatorSessionDescription& desc, td::uint32 seqno, const VoteVector* votes,
const CntVector<bool>* precommitted, const SentBlock* vote_for, bool vote_for_inited,
HashType hash, bool temp) {
auto r = desc.get_by_hash(hash, temp);
if (compare(r, seqno, votes, precommitted, vote_for, vote_for_inited, hash)) {
desc.on_reuse();
return static_cast<const ValidatorSessionRoundAttemptState*>(r);
}
return static_cast<const ValidatorSessionRoundAttemptState*>(nullptr);
}
static const ValidatorSessionRoundAttemptState* move_to_persistent(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* b) {
if (desc.is_persistent(b)) {
return b;
}
auto votes = VoteVector::move_to_persistent(desc, b->votes_);
auto precommitted = CntVector<bool>::move_to_persistent(desc, b->precommitted_);
auto vote_for = SentBlock::move_to_persistent(desc, b->vote_for_);
auto r = lookup(desc, b->seqno_, votes, precommitted, vote_for, b->vote_for_inited_, b->hash_, false);
if (r) {
return r;
}
return new (desc, false) ValidatorSessionRoundAttemptState{desc, b->seqno_, votes, precommitted,
vote_for, b->vote_for_inited_, b->hash_};
}
static const ValidatorSessionRoundAttemptState* create(ValidatorSessionDescription& desc, td::uint32 seqno,
const VoteVector* votes, const CntVector<bool>* precommitted,
const SentBlock* vote_for, bool vote_for_inited) {
auto hash = create_hash(desc, seqno, get_vs_hash(desc, votes), get_vs_hash(desc, precommitted),
get_vs_hash(desc, vote_for), vote_for_inited);
auto r = lookup(desc, seqno, votes, precommitted, vote_for, vote_for_inited, hash, true);
if (r) {
return r;
}
return new (desc, true)
ValidatorSessionRoundAttemptState(desc, seqno, votes, precommitted, vote_for, vote_for_inited, hash);
}
static const ValidatorSessionRoundAttemptState* create(ValidatorSessionDescription& desc, td::uint32 seqno) {
std::vector<bool> x;
x.resize(desc.get_total_nodes(), false);
auto p = CntVector<bool>::create(desc, std::move(x));
return create(desc, seqno, nullptr, p, nullptr, false);
}
ValidatorSessionRoundAttemptState(ValidatorSessionDescription& desc, td::uint32 seqno, const VoteVector* votes,
const CntVector<bool>* precommitted, const SentBlock* vote_for,
bool vote_for_inited, HashType hash)
: RootObject{sizeof(ValidatorSessionRoundAttemptState)}
, seqno_(seqno)
, votes_(votes)
, precommitted_(precommitted)
, vote_for_(vote_for)
, vote_for_inited_(vote_for_inited)
, hash_(std::move(hash)) {
desc.update_hash(this, hash_);
}
auto get_hash(ValidatorSessionDescription& desc) const {
return hash_;
}
auto get_seqno() const {
return seqno_;
}
auto get_votes() const {
return votes_;
}
auto get_precommits() const {
return precommitted_;
}
const SentBlock* get_voted_block(ValidatorSessionDescription& desc, bool& f) const;
const SentBlock* get_vote_for_block(ValidatorSessionDescription& desc, bool& f) const {
f = vote_for_inited_;
return vote_for_;
}
bool check_attempt_is_precommitted(ValidatorSessionDescription& desc) const;
bool check_vote_received_from(td::uint32 src_idx) const;
bool check_precommit_received_from(td::uint32 src_idx) const;
bool operator<(const ValidatorSessionRoundAttemptState& right) const {
return seqno_ < right.seqno_;
}
struct Compare {
bool operator()(const ValidatorSessionRoundAttemptState* a, const ValidatorSessionRoundAttemptState* b) const {
return *a < *b;
}
};
static const ValidatorSessionRoundAttemptState* merge(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* left,
const ValidatorSessionRoundAttemptState* right);
static const ValidatorSessionRoundAttemptState* action(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* state,
td::uint32 src_idx, td::uint32 att,
const ton_api::validatorSession_message_voteFor& act,
const ValidatorSessionRoundState* round);
static const ValidatorSessionRoundAttemptState* action(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* state,
td::uint32 src_idx, td::uint32 att,
const ton_api::validatorSession_message_vote& act,
const ValidatorSessionRoundState* round);
static const ValidatorSessionRoundAttemptState* action(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* state,
td::uint32 src_idx, td::uint32 att,
const ton_api::validatorSession_message_precommit& act,
const ValidatorSessionRoundState* round);
static const ValidatorSessionRoundAttemptState* action(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* state,
td::uint32 src_idx, td::uint32 att,
const ton_api::validatorSession_message_empty& act,
const ValidatorSessionRoundState* round);
static const ValidatorSessionRoundAttemptState* action(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* state,
td::uint32 src_idx, td::uint32 att,
const ton_api::validatorSession_round_Message* action,
const ValidatorSessionRoundState* round);
template <class T>
static const ValidatorSessionRoundAttemptState* action(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* state,
td::uint32 src_idx, td::uint32 att, const T& action,
const ValidatorSessionRoundState* round) {
UNREACHABLE();
}
static const ValidatorSessionRoundAttemptState* try_vote(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* state,
td::uint32 src_idx, td::uint32 att,
const ValidatorSessionRoundState* round,
const ton_api::validatorSession_round_Message* cmp,
bool& made);
static const ValidatorSessionRoundAttemptState* try_precommit(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* state,
td::uint32 src_idx, td::uint32 att,
const ValidatorSessionRoundState* round,
const ton_api::validatorSession_round_Message* cmp,
bool& made);
static const ValidatorSessionRoundAttemptState* make_one(ValidatorSessionDescription& desc,
const ValidatorSessionRoundAttemptState* state,
td::uint32 src_idx, td::uint32 att,
const ValidatorSessionRoundState* round,
const ton_api::validatorSession_round_Message* cmp,
bool& made);
tl_object_ptr<ton_api::validatorSession_round_Message> create_action(ValidatorSessionDescription& desc,
const ValidatorSessionRoundState* round,
td::uint32 src_idx, td::uint32 att) const;
void dump(ValidatorSessionDescription& desc, td::StringBuilder& sb) const;
private:
const td::uint32 seqno_;
const VoteVector* votes_;
const CntVector<bool>* precommitted_;
const SentBlock* vote_for_;
const bool vote_for_inited_;
const HashType hash_;
};
using AttemptVector =
CntSortedVector<const ValidatorSessionRoundAttemptState*, ValidatorSessionRoundAttemptState::Compare>;
class ValidatorSessionRoundState : public ValidatorSessionDescription::RootObject {
public:

View file

@ -27,6 +27,12 @@ namespace ton {
namespace validatorsession {
constexpr int VERBOSITY_NAME(VALIDATOR_SESSION_WARNING) = verbosity_WARNING;
constexpr int VERBOSITY_NAME(VALIDATOR_SESSION_NOTICE) = verbosity_DEBUG;
constexpr int VERBOSITY_NAME(VALIDATOR_SESSION_INFO) = verbosity_DEBUG;
constexpr int VERBOSITY_NAME(VALIDATOR_SESSION_DEBUG) = verbosity_DEBUG;
constexpr int VERBOSITY_NAME(VALIDATOR_SESSION_EXTRA_DEBUG) = verbosity_DEBUG + 1;
using ValidatorSessionRootHash = td::Bits256;
using ValidatorSessionFileHash = td::Bits256;
using ValidatorSessionCollatedDataFileHash = td::Bits256;

View file

@ -203,7 +203,23 @@ void ValidatorSessionImpl::preprocess_block(catchain::CatChainBlock *block) {
<< "ms: state=" << state->get_hash(description());
}
void ValidatorSessionImpl::process_broadcast(PublicKeyHash src, td::BufferSlice data) {
bool ValidatorSessionImpl::ensure_candidate_unique(td::uint32 src_idx, td::uint32 round,
ValidatorSessionCandidateId block_id) {
auto it = src_round_candidate_[src_idx].find(round);
if (it != src_round_candidate_[src_idx].end() && it->second != block_id) {
VLOG(VALIDATOR_SESSION_WARNING) << this << "[node " << description_->get_source_adnl_id(src_idx) << "][candidate "
<< block_id << "]: this node already has candidate in round " << round;
return false;
}
src_round_candidate_[src_idx][round] = block_id;
return true;
}
void ValidatorSessionImpl::process_broadcast(PublicKeyHash src, td::BufferSlice data,
td::optional<ValidatorSessionCandidateId> expected_id,
bool is_overlay_broadcast) {
// Note: src is not necessarily equal to the sender of this message:
// If requested using get_broadcast_p2p, src is the creator of the block, sender possibly is some other node.
auto src_idx = description().get_source_idx(src);
auto R = fetch_tl_object<ton_api::validatorSession_candidate>(data.clone(), true);
if (R.is_error()) {
@ -230,6 +246,12 @@ void ValidatorSessionImpl::process_broadcast(PublicKeyHash src, td::BufferSlice
auto block_round = static_cast<td::uint32>(candidate->round_);
auto block_id = description().candidate_id(src_idx, candidate->root_hash_, file_hash, collated_data_file_hash);
if (expected_id && expected_id.value() != block_id) {
VLOG(VALIDATOR_SESSION_WARNING) << this << "[node " << src << "][broadcast " << sha256_bits256(data.as_slice())
<< "]: id mismatch";
return;
}
if ((td::int32)block_round < (td::int32)cur_round_ - MAX_PAST_ROUND_BLOCK ||
block_round >= cur_round_ + MAX_FUTURE_ROUND_BLOCK) {
VLOG(VALIDATOR_SESSION_NOTICE) << this << "[node " << src << "][broadcast " << block_id
@ -250,6 +272,10 @@ void ValidatorSessionImpl::process_broadcast(PublicKeyHash src, td::BufferSlice
return;
}
if (is_overlay_broadcast && !ensure_candidate_unique(src_idx, block_round, block_id)) {
return;
}
blocks_[block_id] = std::move(candidate);
VLOG(VALIDATOR_SESSION_WARNING) << this << ": received broadcast " << block_id;
@ -508,7 +534,11 @@ void ValidatorSessionImpl::try_approve_block(const SentBlock *block) {
}
if (block) {
auto T = td::Timestamp::at(round_started_at_.at() + description().get_delay(block->get_src_idx()) + 2.0);
if (!ensure_candidate_unique(block->get_src_idx(), cur_round_, SentBlock::get_block_id(block))) {
return;
}
auto T = td::Timestamp::at(round_started_at_.at() + description().get_delay(block->get_src_idx()) +
REQUEST_BROADCAST_P2P_DELAY);
auto it = blocks_.find(block_id);
if (it != blocks_.end()) {
@ -545,16 +575,18 @@ void ValidatorSessionImpl::try_approve_block(const SentBlock *block) {
auto id = description().get_source_id(v[td::Random::fast(0, static_cast<td::int32>(v.size() - 1))]);
auto src_id = description().get_source_id(block->get_src_idx());
active_requests_.insert(block_id);
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id, src_id, print_id = print_id(),
hash = block_id, round = cur_round_](td::Result<td::BufferSlice> R) {
td::actor::send_closure(SelfId, &ValidatorSessionImpl::end_request, round, hash);
if (R.is_error()) {
VLOG(VALIDATOR_SESSION_WARNING)
<< print_id << ": failed to get candidate " << hash << " from " << id << ": " << R.move_as_error();
} else {
td::actor::send_closure(SelfId, &ValidatorSessionImpl::process_broadcast, src_id, R.move_as_ok());
}
});
auto P = td::PromiseCreator::lambda(
[SelfId = actor_id(this), id, src_id, print_id = print_id(), hash = block_id, round = cur_round_,
candidate_id = SentBlock::get_block_id(block)](td::Result<td::BufferSlice> R) {
td::actor::send_closure(SelfId, &ValidatorSessionImpl::end_request, round, hash);
if (R.is_error()) {
VLOG(VALIDATOR_SESSION_WARNING) << print_id << ": failed to get candidate " << hash << " from " << id
<< ": " << R.move_as_error();
} else {
td::actor::send_closure(SelfId, &ValidatorSessionImpl::process_broadcast, src_id, R.move_as_ok(),
candidate_id, false);
}
});
get_broadcast_p2p(id, block->get_file_hash(), block->get_collated_data_file_hash(),
description().get_source_id(block->get_src_idx()), cur_round_, block->get_root_hash(),
@ -586,10 +618,11 @@ void ValidatorSessionImpl::get_broadcast_p2p(PublicKeyHash node, ValidatorSessio
round,
create_tl_object<ton_api::validatorSession_candidateId>(src.tl(), root_hash, file_hash, collated_data_file_hash));
td::actor::send_closure(catchain_, &catchain::CatChain::send_query_via, node, "download candidate",
std::move(promise), timeout, serialize_tl_object(obj, true),
description().opts().max_block_size + description().opts().max_collated_data_size + 1024,
rldp_);
td::actor::send_closure(
catchain_, &catchain::CatChain::send_query_via, node, "download candidate", std::move(promise), timeout,
serialize_tl_object(obj, true),
description().opts().max_block_size + description().opts().max_collated_data_size + MAX_CANDIDATE_EXTRA_SIZE,
rldp_);
}
void ValidatorSessionImpl::check_sign_slot() {
@ -745,7 +778,6 @@ void ValidatorSessionImpl::on_new_round(td::uint32 round) {
while (cur_round_ < round) {
auto block = real_state_->get_committed_block(description(), cur_round_);
//CHECK(block);
auto sigs = real_state_->get_committed_block_signatures(description(), cur_round_);
CHECK(sigs);
auto approve_sigs = real_state_->get_committed_block_approve_signatures(description(), cur_round_);
@ -838,7 +870,8 @@ void ValidatorSessionImpl::on_catchain_started() {
auto broadcast = create_tl_object<ton_api::validatorSession_candidate>(
src.tl(), round, root_hash, std::move(B.data), std::move(B.collated_data));
td::actor::send_closure(SelfId, &ValidatorSessionImpl::process_broadcast, src,
serialize_tl_object(broadcast, true));
serialize_tl_object(broadcast, true), td::optional<ValidatorSessionCandidateId>(),
false);
}
});
callback_->get_approved_candidate(description().get_source_public_key(x->get_src_idx()), x->get_root_hash(),
@ -866,6 +899,7 @@ ValidatorSessionImpl::ValidatorSessionImpl(catchain::CatChainSessionId session_i
, overlay_manager_(overlays)
, allow_unsafe_self_blocks_resync_(allow_unsafe_self_blocks_resync) {
description_ = ValidatorSessionDescription::create(std::move(opts), nodes, local_id);
src_round_candidate_.resize(description_->get_total_nodes());
}
void ValidatorSessionImpl::start() {

View file

@ -74,6 +74,8 @@ class ValidatorSessionImpl : public ValidatorSession {
td::BufferSlice signature_;
std::map<ValidatorSessionCandidateId, tl_object_ptr<ton_api::validatorSession_candidate>> blocks_;
// src_round_candidate_[src_id][round] -> candidate id
std::vector<std::map<td::uint32, ValidatorSessionCandidateId>> src_round_candidate_;
catchain::CatChainSessionId unique_hash_;
@ -110,7 +112,8 @@ class ValidatorSessionImpl : public ValidatorSession {
td::actor::send_closure(id_, &ValidatorSessionImpl::preprocess_block, block);
}
void process_broadcast(const PublicKeyHash &src, td::BufferSlice data) override {
td::actor::send_closure(id_, &ValidatorSessionImpl::process_broadcast, src, std::move(data));
td::actor::send_closure(id_, &ValidatorSessionImpl::process_broadcast, src, std::move(data),
td::optional<ValidatorSessionCandidateId>(), true);
}
void process_message(const PublicKeyHash &src, td::BufferSlice data) override {
td::actor::send_closure(id_, &ValidatorSessionImpl::process_message, src, std::move(data));
@ -174,7 +177,9 @@ class ValidatorSessionImpl : public ValidatorSession {
void process_blocks(std::vector<catchain::CatChainBlock *> blocks);
void finished_processing();
void preprocess_block(catchain::CatChainBlock *block);
void process_broadcast(PublicKeyHash src, td::BufferSlice data);
bool ensure_candidate_unique(td::uint32 src_idx, td::uint32 round, ValidatorSessionCandidateId block_id);
void process_broadcast(PublicKeyHash src, td::BufferSlice data, td::optional<ValidatorSessionCandidateId> expected_id,
bool is_overlay_broadcast);
void process_message(PublicKeyHash src, td::BufferSlice data);
void process_query(PublicKeyHash src, td::BufferSlice data, td::Promise<td::BufferSlice> promise);
@ -206,6 +211,8 @@ class ValidatorSessionImpl : public ValidatorSession {
static const size_t MAX_REJECT_REASON_SIZE = 1024;
static const td::int32 MAX_FUTURE_ROUND_BLOCK = 100;
static const td::int32 MAX_PAST_ROUND_BLOCK = 20;
constexpr static const double REQUEST_BROADCAST_P2P_DELAY = 2.0;
static const td::uint32 MAX_CANDIDATE_EXTRA_SIZE = 1024;
};
} // namespace validatorsession