diff --git a/catchain/catchain-receiver-interface.h b/catchain/catchain-receiver-interface.h index c8f1ef66..bc02832a 100644 --- a/catchain/catchain-receiver-interface.h +++ b/catchain/catchain-receiver-interface.h @@ -52,6 +52,7 @@ class CatChainReceiverInterface : public td::actor::Actor { td::BufferSlice query, td::uint64 max_answer_size, td::actor::ActorId 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; diff --git a/catchain/catchain-receiver-source.cpp b/catchain/catchain-receiver-source.cpp index 6a9e0777..e758b335 100644 --- a/catchain/catchain-receiver-source.cpp +++ b/catchain/catchain-receiver-source.cpp @@ -60,15 +60,15 @@ td::Result> 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(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 diff --git a/catchain/catchain-receiver-source.h b/catchain/catchain-receiver-source.h index e805c2a5..55035e77 100644 --- a/catchain/catchain-receiver-source.h +++ b/catchain/catchain-receiver-source.h @@ -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> create(CatChainReceiver *chain, PublicKey pub_key, adnl::AdnlNodeIdShort adnl_id, td::uint32 id); diff --git a/catchain/catchain-receiver-source.hpp b/catchain/catchain-receiver-source.hpp index 0785d8c2..cf08c421 100644 --- a/catchain/catchain-receiver-source.hpp +++ b/catchain/catchain-receiver-source.hpp @@ -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 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 diff --git a/catchain/catchain-receiver.cpp b/catchain/catchain-receiver.cpp index 488fbb9d..482cfb43 100644 --- a/catchain/catchain-receiver.cpp +++ b/catchain/catchain-receiver.cpp @@ -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 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 block, td::SharedSlice payload) { if (block->height_ == 0) { @@ -267,21 +247,16 @@ td::Status CatChainReceiverImpl::validate_block_sync(const tl_object_ptr &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, } CHECK(local_idx_ != static_cast(ids.size())); - //std::sort(short_ids.begin(), short_ids.end()); auto F = create_tl_object(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, 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(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(), true)); } else { - promise.set_value(serialize_tl_object(create_tl_object(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 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(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(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(cnt), true)); -} - -void CatChainReceiverImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlockHistory query, - td::Promise 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 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(0), true)); - return; - } - if (static_cast(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(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(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(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(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(get_sources_cnt()) - 1)); + auto idx = td::Random::fast(1, static_cast(get_sources_cnt()) - 1); + if (idx == static_cast(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()); + } 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()); + 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)); diff --git a/catchain/catchain-receiver.h b/catchain/catchain-receiver.h index 2c959fbc..75c87351 100644 --- a/catchain/catchain-receiver.h +++ b/catchain/catchain-receiver.h @@ -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; diff --git a/catchain/catchain-receiver.hpp b/catchain/catchain-receiver.hpp index 5da4001c..5c4d3764 100644 --- a/catchain/catchain-receiver.hpp +++ b/catchain/catchain-receiver.hpp @@ -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()); - } 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 promise); void process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlock query, td::Promise promise); - void process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlocks query, - td::Promise promise); - void process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlockHistory query, - td::Promise promise); void process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getDifference query, td::Promise promise); template void process_query(adnl::AdnlNodeIdShort src, const T &query, td::Promise 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 block, td::BufferSlice payload); void receive_block_answer(adnl::AdnlNodeIdShort src, td::BufferSlice); - //void send_block(const PublicKeyHash &src, tl_object_ptr block, td::BufferSlice payload); CatChainReceivedBlock *create_block(tl_object_ptr block, td::SharedSlice payload) override; CatChainReceivedBlock *create_block(tl_object_ptr 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 neighbours_; - //std::queue> events_; - //std::queue raw_events_; - td::actor::ActorId keyring_; td::actor::ActorId adnl_; td::actor::ActorId overlay_manager_; @@ -231,6 +221,9 @@ class CatChainReceiverImpl final : public CatChainReceiver { bool started_{false}; std::list to_run_; + + std::vector blame_processed_; + std::map pending_fork_proofs_; }; } // namespace catchain diff --git a/catchain/catchain.cpp b/catchain/catchain.cpp index 001840fd..53ae16b5 100644 --- a/catchain/catchain.cpp +++ b/catchain/catchain.cpp @@ -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 promise) { diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index 964b4328..b5e6dbd6 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -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]); diff --git a/crypto/func/analyzer.cpp b/crypto/func/analyzer.cpp index 989e9ebe..d59230e8 100644 --- a/crypto/func/analyzer.cpp +++ b/crypto/func/analyzer.cpp @@ -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: diff --git a/doc/catchain.tex b/doc/catchain.tex index b3cae642..b1a27164 100644 --- a/doc/catchain.tex +++ b/doc/catchain.tex @@ -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. diff --git a/tdutils/td/utils/Timer.cpp b/tdutils/td/utils/Timer.cpp index f33a30dc..0f6a7d6e 100644 --- a/tdutils/td/utils/Timer.cpp +++ b/tdutils/td/utils/Timer.cpp @@ -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; } diff --git a/tdutils/td/utils/Timer.h b/tdutils/td/utils/Timer.h index 64f3e934..18ea35d7 100644 --- a/tdutils/td/utils/Timer.h +++ b/tdutils/td/utils/Timer.h @@ -46,7 +46,7 @@ class Timer { class PerfWarningTimer { public: - explicit PerfWarningTimer(string name, double max_duration = 0.1, std::function&& callback = [] (double) {}); + explicit PerfWarningTimer(string name, double max_duration = 0.1, std::function&& callback = {}); PerfWarningTimer(const PerfWarningTimer &) = delete; PerfWarningTimer &operator=(const PerfWarningTimer &) = delete; PerfWarningTimer(PerfWarningTimer &&other); diff --git a/tl/generate/scheme/ton_api.tl b/tl/generate/scheme/ton_api.tl index 66e6f4a6..346d9152 100644 --- a/tl/generate/scheme/ton_api.tl +++ b/tl/generate/scheme/ton_api.tl @@ -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--- diff --git a/tl/generate/scheme/ton_api.tlo b/tl/generate/scheme/ton_api.tlo index 8ce7a579..5b8e1c72 100644 Binary files a/tl/generate/scheme/ton_api.tlo and b/tl/generate/scheme/ton_api.tlo differ diff --git a/validator-engine/validator-engine.cpp b/validator-engine/validator-engine.cpp index 02ab42f9..65b93df7 100644 --- a/validator-engine/validator-engine.cpp +++ b/validator-engine/validator-engine.cpp @@ -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(fname); diff --git a/validator-session/CMakeLists.txt b/validator-session/CMakeLists.txt index dc837219..4931e464 100644 --- a/validator-session/CMakeLists.txt +++ b/validator-session/CMakeLists.txt @@ -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}) diff --git a/validator-session/persistent-vector.h b/validator-session/persistent-vector.h index 77852f80..3491164c 100644 --- a/validator-session/persistent-vector.h +++ b/validator-session/persistent-vector.h @@ -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 : 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{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_; diff --git a/validator-session/validator-session-description.cpp b/validator-session/validator-session-description.cpp index eb4c3a8e..34dc9021 100644 --- a/validator-session/validator-session-description.cpp +++ b/validator-session/validator-session-description.cpp @@ -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(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(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::create( @@ -206,6 +167,58 @@ std::unique_ptr ValidatorSessionDescription::create return std::make_unique(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(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 diff --git a/validator-session/validator-session-description.hpp b/validator-session/validator-session-description.hpp index d65369ac..0c662b7a 100644 --- a/validator-session/validator-session-description.hpp +++ b/validator-session/validator-session-description.hpp @@ -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, cache_size> cache_; - //std::array, 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 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 pdata_perm_; - size_t pdata_perm_ptr_; std::atomic 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 diff --git a/validator-session/validator-session-round-attempt-state.cpp b/validator-session/validator-session-round-attempt-state.cpp new file mode 100644 index 00000000..33c3d804 --- /dev/null +++ b/validator-session/validator-session-round-attempt-state.cpp @@ -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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ +#include "validator-session-state.h" +#include "td/utils/Random.h" +#include "auto/tl/ton_api.hpp" + +#include + +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::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::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(act); + if (x->candidate_ != block_id) { + VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act + << "]: expected VOTE(" << block_id << ")"; + } + } + } else { + VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) + << "]: making implicit VOTE(" << block_id << ")"; + } + + auto candidate = get_candidate(state->votes_, block_id); + if (!candidate) { + candidate = SessionVoteCandidate::create(desc, block); + } + candidate = SessionVoteCandidate::push(desc, candidate, src_idx); + auto v = VoteVector::push(desc, state->votes_, candidate); + return ValidatorSessionRoundAttemptState::create(desc, state->seqno_, std::move(v), state->precommitted_, + state->vote_for_, state->vote_for_inited_); +} + +const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::try_precommit( + ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, + td::uint32 att, const ValidatorSessionRoundState* round, const ton_api::validatorSession_round_Message* act, + bool& made) { + made = false; + if (state->check_precommit_received_from(src_idx)) { + return state; + } + bool found; + auto block = state->get_voted_block(desc, found); + if (!found) { + return state; + } + made = true; + auto block_id = SentBlock::get_block_id(block); + + if (act) { + if (act->get_id() != ton_api::validatorSession_message_precommit::ID) { + VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act + << "]: expected PRECOMMIT(" << block_id << ")"; + } else { + auto x = static_cast(act); + if (x->candidate_ != block_id) { + VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act + << "]: expected PRECOMMIT(" << block_id << ")"; + } + } + } else { + VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) + << "]: making implicit PRECOMMIT(" << block_id << ")"; + } + + auto v = CntVector::change(desc, state->precommitted_, src_idx, true); + return ValidatorSessionRoundAttemptState::create(desc, state->seqno_, state->votes_, std::move(v), state->vote_for_, + state->vote_for_inited_); +} + +const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::make_one( + ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, + td::uint32 att, const ValidatorSessionRoundState* round, const ton_api::validatorSession_round_Message* act, + bool& made) { + made = false; + state = try_vote(desc, state, src_idx, att, round, act, made); + if (made) { + return state; + } + state = try_precommit(desc, state, src_idx, att, round, act, made); + if (made) { + return state; + } + if (act && act->get_id() != ton_api::validatorSession_message_empty::ID) { + VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act + << "]: invalid message: expected EMPTY"; + } + return state; +} + +const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::action( + ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, + td::uint32 att, const ton_api::validatorSession_round_Message* act, const ValidatorSessionRoundState* round) { + ton_api::downcast_call(*const_cast(act), + [&](auto& obj) { state = action(desc, state, src_idx, att, obj, round); }); + return state; +} + +bool ValidatorSessionRoundAttemptState::check_vote_received_from(td::uint32 src_idx) const { + if (!votes_) { + return false; + } + auto size = votes_->size(); + auto v = votes_->data(); + for (td::uint32 i = 0; i < size; i++) { + if (v[i]->check_block_is_voted_by(src_idx)) { + return true; + } + } + return false; +} + +bool ValidatorSessionRoundAttemptState::check_precommit_received_from(td::uint32 src_idx) const { + return precommitted_->at(src_idx); +} + +const SentBlock* ValidatorSessionRoundAttemptState::get_voted_block(ValidatorSessionDescription& desc, bool& f) const { + f = false; + if (!votes_) { + return nullptr; + } + + auto size = votes_->size(); + auto v = votes_->data(); + for (td::uint32 i = 0; i < size; i++) { + if (v[i]->check_block_is_voted(desc)) { + f = true; + return v[i]->get_block(); + } + } + return nullptr; +} + +bool ValidatorSessionRoundAttemptState::check_attempt_is_precommitted(ValidatorSessionDescription& desc) const { + ValidatorWeight weight = 0; + for (td::uint32 i = 0; i < desc.get_total_nodes(); i++) { + if (precommitted_->at(i)) { + weight += desc.get_node_weight(i); + if (weight >= desc.get_cutoff_weight()) { + return true; + } + } + } + return false; +} + +tl_object_ptr ValidatorSessionRoundAttemptState::create_action( + ValidatorSessionDescription& desc, const ValidatorSessionRoundState* round, td::uint32 src_idx, + td::uint32 att) const { + if (!check_vote_received_from(src_idx)) { + auto found = false; + auto B = round->choose_block_to_vote(desc, src_idx, att, vote_for_, vote_for_inited_, found); + if (found) { + auto block_id = SentBlock::get_block_id(B); + return create_tl_object(round->get_seqno(), seqno_, block_id); + } + } + if (!check_precommit_received_from(src_idx)) { + bool f = false; + auto B = get_voted_block(desc, f); + + if (f) { + auto block_id = SentBlock::get_block_id(B); + + return create_tl_object(round->get_seqno(), seqno_, block_id); + } + } + + return create_tl_object(round->get_seqno(), seqno_); +} + +void ValidatorSessionRoundAttemptState::dump(ValidatorSessionDescription& desc, td::StringBuilder& sb) const { + sb << "attempt=" << seqno_ << "\n"; + sb << ">>>>\n"; + + if (vote_for_inited_) { + sb << "vote_for=" << (vote_for_ ? vote_for_->get_src_idx() : std::numeric_limits::max()) << "\n"; + } else { + sb << "vote_for=NONE\n"; + } + + if (votes_) { + auto s = votes_->size(); + sb << "votes: "; + std::vector R; + R.resize(desc.get_total_nodes(), -1); + for (td::uint32 i = 0; i < s; i++) { + const auto e = votes_->at(i); + const auto& x = e->get_voters_list(); + for (td::uint32 j = 0; j < desc.get_total_nodes(); j++) { + if (x->at(j)) { + R[j] = e->get_src_idx(); + } + } + } + for (td::uint32 i = 0; i < desc.get_total_nodes(); i++) { + sb << R[i] << " "; + } + sb << "\n"; + } else { + sb << "votes: EMPTY\n"; + } + + sb << "precommits: "; + for (td::uint32 i = 0; i < desc.get_total_nodes(); i++) { + const auto e = precommitted_->at(i); + if (e) { + sb << "+ "; + } else { + sb << "- "; + } + } + sb << "\n"; + sb << "<<<<\n"; +} + + +} // namespace validatorsession + +} // namespace ton diff --git a/validator-session/validator-session-round-attempt-state.h b/validator-session/validator-session-round-attempt-state.h new file mode 100644 index 00000000..a4a6b68b --- /dev/null +++ b/validator-session/validator-session-round-attempt-state.h @@ -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 . +*/ + +#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 + + +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(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(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(r); + } + return static_cast(nullptr); + } + static SessionBlockCandidateSignature* create(ValidatorSessionDescription& desc, td::BufferSlice value) { + auto hash = create_hash(desc, value.as_slice()); + auto d = static_cast(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; + +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(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(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(r); + } + return static_cast(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(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(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(r); + } + return static_cast(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 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::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(block, voted); + return desc.compute_hash(serialize_tl_object(obj, true).as_slice()); + } + static bool compare(const RootObject* r, const SentBlock* block, const CntVector* voted, HashType hash) { + if (!r || r->get_size() < sizeof(SessionVoteCandidate)) { + return false; + } + auto R = static_cast(r); + return R->block_ == block && R->voted_by_ == voted && R->hash_ == hash; + } + static auto lookup(ValidatorSessionDescription& desc, const SentBlock* block, const CntVector* 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(r); + } + return static_cast(nullptr); + } + static const SessionVoteCandidate* create(ValidatorSessionDescription& desc, const SentBlock* block, + const CntVector* 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 v; + v.resize(desc.get_total_nodes(), false); + auto vec = CntVector::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::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* 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::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::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* voted_by_; + const HashType hash_; +}; + +using VoteVector = CntSortedVector; +using ApproveVector = CntSortedVector; +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(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* precommitted, const SentBlock* vote_for, bool vote_for_inited, + HashType hash) { + if (!r || r->get_size() < sizeof(ValidatorSessionRoundAttemptState)) { + return false; + } + auto R = static_cast(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* 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(r); + } + return static_cast(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::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* 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 x; + x.resize(desc.get_total_nodes(), false); + auto p = CntVector::create(desc, std::move(x)); + + return create(desc, seqno, nullptr, p, nullptr, false); + } + + ValidatorSessionRoundAttemptState(ValidatorSessionDescription& desc, td::uint32 seqno, const VoteVector* votes, + const CntVector* 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 + 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 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* precommitted_; + const SentBlock* vote_for_; + const bool vote_for_inited_; + const HashType hash_; +}; + +using AttemptVector = + CntSortedVector; + + +} // namespace validatorsession + +} // namespace ton diff --git a/validator-session/validator-session-state.cpp b/validator-session/validator-session-state.cpp index 62a054d5..97b91895 100644 --- a/validator-session/validator-session-state.cpp +++ b/validator-session/validator-session-state.cpp @@ -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(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::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(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::merge(desc, left->precommitted_, right->precommitted_); - auto votes = VoteVector::merge(desc, left->votes_, right->votes_, - [&](const SessionVoteCandidate* l, const SessionVoteCandidate* r) { - return SessionVoteCandidate::merge(desc, l, r); - }); - - return ValidatorSessionRoundAttemptState::create(desc, left->seqno_, std::move(votes), std::move(precommitted), - vote_for, vote_for_inited); -} - -const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::action( - ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, - td::uint32 att, const ton_api::validatorSession_message_voteFor& act, const ValidatorSessionRoundState* round) { - if (state->vote_for_inited_) { - VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act - << "]: invalid message: duplicate VOTEFOR"; - return state; - } - if (src_idx != desc.get_vote_for_author(att)) { - VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act - << "]: invalid message: bad VOTEFOR author"; - return state; - } - if (round->get_first_attempt(src_idx) == 0 && desc.opts().max_round_attempts > 0) { - VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act - << "]: invalid message: too early for VOTEFOR"; - return state; - } - if (round->get_first_attempt(src_idx) + desc.opts().max_round_attempts > att && desc.opts().max_round_attempts == 0) { - VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act - << "]: invalid message: too early for VOTEFOR"; - return state; - } - - auto x = round->get_block(act.candidate_); - - if (!x) { - VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act - << "]: invalid message: VOTEFOR for not approved block"; - return state; - } - if (!x->check_block_is_approved(desc)) { - VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act - << "]: invalid message: VOTEFOR for not approved block"; - return state; - } - - return ValidatorSessionRoundAttemptState::create(desc, state->seqno_, state->votes_, state->precommitted_, - x->get_block(), true); -} - -const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::action( - ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, - td::uint32 att, const ton_api::validatorSession_message_vote& act, const ValidatorSessionRoundState* round) { - bool made; - return make_one(desc, state, src_idx, att, round, &act, made); -} - -const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::action( - ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, - td::uint32 att, const ton_api::validatorSession_message_precommit& act, const ValidatorSessionRoundState* round) { - bool made; - return make_one(desc, state, src_idx, att, round, &act, made); -} - -const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::action( - ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, - td::uint32 att, const ton_api::validatorSession_message_empty& act, const ValidatorSessionRoundState* round) { - bool made; - return make_one(desc, state, src_idx, att, round, &act, made); -} - -const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::try_vote( - ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, - td::uint32 att, const ValidatorSessionRoundState* round, const ton_api::validatorSession_round_Message* act, - bool& made) { - made = false; - if (state->check_vote_received_from(src_idx)) { - return state; - } - auto found = false; - auto block = round->choose_block_to_vote(desc, src_idx, att, state->vote_for_, state->vote_for_inited_, found); - if (!found) { - return state; - } - auto block_id = SentBlock::get_block_id(block); - made = true; - - if (act) { - if (act->get_id() != ton_api::validatorSession_message_vote::ID) { - VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act - << "]: expected VOTE(" << block_id << ")"; - } else { - auto x = static_cast(act); - if (x->candidate_ != block_id) { - VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act - << "]: expected VOTE(" << block_id << ")"; - } - } - } else { - VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) - << "]: making implicit VOTE(" << block_id << ")"; - } - - auto candidate = get_candidate(state->votes_, block_id); - if (!candidate) { - candidate = SessionVoteCandidate::create(desc, block); - } - candidate = SessionVoteCandidate::push(desc, candidate, src_idx); - auto v = VoteVector::push(desc, state->votes_, candidate); - return ValidatorSessionRoundAttemptState::create(desc, state->seqno_, std::move(v), state->precommitted_, - state->vote_for_, state->vote_for_inited_); -} - -const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::try_precommit( - ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, - td::uint32 att, const ValidatorSessionRoundState* round, const ton_api::validatorSession_round_Message* act, - bool& made) { - made = false; - if (state->check_precommit_received_from(src_idx)) { - return state; - } - bool found; - auto block = state->get_voted_block(desc, found); - if (!found) { - return state; - } - made = true; - auto block_id = SentBlock::get_block_id(block); - - if (act) { - if (act->get_id() != ton_api::validatorSession_message_precommit::ID) { - VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act - << "]: expected PRECOMMIT(" << block_id << ")"; - } else { - auto x = static_cast(act); - if (x->candidate_ != block_id) { - VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act - << "]: expected PRECOMMIT(" << block_id << ")"; - } - } - } else { - VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) - << "]: making implicit PRECOMMIT(" << block_id << ")"; - } - - auto v = CntVector::change(desc, state->precommitted_, src_idx, true); - return ValidatorSessionRoundAttemptState::create(desc, state->seqno_, state->votes_, std::move(v), state->vote_for_, - state->vote_for_inited_); -} - -const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::make_one( - ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, - td::uint32 att, const ValidatorSessionRoundState* round, const ton_api::validatorSession_round_Message* act, - bool& made) { - made = false; - state = try_vote(desc, state, src_idx, att, round, act, made); - if (made) { - return state; - } - state = try_precommit(desc, state, src_idx, att, round, act, made); - if (made) { - return state; - } - if (act && act->get_id() != ton_api::validatorSession_message_empty::ID) { - VLOG(VALIDATOR_SESSION_WARNING) << "[validator session][node " << desc.get_source_id(src_idx) << "][" << act - << "]: invalid message: expected EMPTY"; - } - return state; -} - -const ValidatorSessionRoundAttemptState* ValidatorSessionRoundAttemptState::action( - ValidatorSessionDescription& desc, const ValidatorSessionRoundAttemptState* state, td::uint32 src_idx, - td::uint32 att, const ton_api::validatorSession_round_Message* act, const ValidatorSessionRoundState* round) { - ton_api::downcast_call(*const_cast(act), - [&](auto& obj) { state = action(desc, state, src_idx, att, obj, round); }); - return state; -} - -bool ValidatorSessionRoundAttemptState::check_vote_received_from(td::uint32 src_idx) const { - if (!votes_) { - return false; - } - auto size = votes_->size(); - auto v = votes_->data(); - for (td::uint32 i = 0; i < size; i++) { - if (v[i]->check_block_is_voted_by(src_idx)) { - return true; - } - } - return false; -} - -bool ValidatorSessionRoundAttemptState::check_precommit_received_from(td::uint32 src_idx) const { - return precommitted_->at(src_idx); -} - -const SentBlock* ValidatorSessionRoundAttemptState::get_voted_block(ValidatorSessionDescription& desc, bool& f) const { - f = false; - if (!votes_) { - return nullptr; - } - - auto size = votes_->size(); - auto v = votes_->data(); - for (td::uint32 i = 0; i < size; i++) { - if (v[i]->check_block_is_voted(desc)) { - f = true; - return v[i]->get_block(); - } - } - return nullptr; -} - -bool ValidatorSessionRoundAttemptState::check_attempt_is_precommitted(ValidatorSessionDescription& desc) const { - ValidatorWeight weight = 0; - for (td::uint32 i = 0; i < desc.get_total_nodes(); i++) { - if (precommitted_->at(i)) { - weight += desc.get_node_weight(i); - if (weight >= desc.get_cutoff_weight()) { - return true; - } - } - } - return false; -} - -tl_object_ptr ValidatorSessionRoundAttemptState::create_action( - ValidatorSessionDescription& desc, const ValidatorSessionRoundState* round, td::uint32 src_idx, - td::uint32 att) const { - if (!check_vote_received_from(src_idx)) { - auto found = false; - auto B = round->choose_block_to_vote(desc, src_idx, att, vote_for_, vote_for_inited_, found); - if (found) { - auto block_id = SentBlock::get_block_id(B); - return create_tl_object(round->get_seqno(), seqno_, block_id); - } - } - if (!check_precommit_received_from(src_idx)) { - bool f = false; - auto B = get_voted_block(desc, f); - - if (f) { - auto block_id = SentBlock::get_block_id(B); - - return create_tl_object(round->get_seqno(), seqno_, block_id); - } - } - - return create_tl_object(round->get_seqno(), seqno_); -} - -void ValidatorSessionRoundAttemptState::dump(ValidatorSessionDescription& desc, td::StringBuilder& sb) const { - sb << "attempt=" << seqno_ << "\n"; - sb << ">>>>\n"; - - if (vote_for_inited_) { - sb << "vote_for=" << (vote_for_ ? vote_for_->get_src_idx() : std::numeric_limits::max()) << "\n"; - } else { - sb << "vote_for=NONE\n"; - } - - if (votes_) { - auto s = votes_->size(); - sb << "votes: "; - std::vector R; - R.resize(desc.get_total_nodes(), -1); - for (td::uint32 i = 0; i < s; i++) { - const auto e = votes_->at(i); - const auto& x = e->get_voters_list(); - for (td::uint32 j = 0; j < desc.get_total_nodes(); j++) { - if (x->at(j)) { - R[j] = e->get_src_idx(); - } - } - } - for (td::uint32 i = 0; i < desc.get_total_nodes(); i++) { - sb << R[i] << " "; - } - sb << "\n"; - } else { - sb << "votes: EMPTY\n"; - } - - sb << "precommits: "; - for (td::uint32 i = 0; i < desc.get_total_nodes(); i++) { - const auto e = precommitted_->at(i); - if (e) { - sb << "+ "; - } else { - sb << "- "; - } - } - sb << "\n"; - sb << "<<<<\n"; -} - // // // ROUND STATE @@ -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(act), [&](auto& obj) { attempt = ValidatorSessionRoundAttemptState::action(desc, attempt, src_idx, att, obj, state); }); @@ -1302,6 +843,8 @@ std::vector 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); diff --git a/validator-session/validator-session-state.h b/validator-session/validator-session-state.h index 37708cba..f827b3fc 100644 --- a/validator-session/validator-session-state.h +++ b/validator-session/validator-session-state.h @@ -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 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(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(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(r); - } - return static_cast(nullptr); - } - static SessionBlockCandidateSignature* create(ValidatorSessionDescription& desc, td::BufferSlice value) { - auto hash = create_hash(desc, value.as_slice()); - auto d = static_cast(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; - -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(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(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(r); - } - return static_cast(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(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(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(r); - } - return static_cast(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 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::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(block, voted); - return desc.compute_hash(serialize_tl_object(obj, true).as_slice()); - } - static bool compare(const RootObject* r, const SentBlock* block, const CntVector* voted, HashType hash) { - if (!r || r->get_size() < sizeof(SessionVoteCandidate)) { - return false; - } - auto R = static_cast(r); - return R->block_ == block && R->voted_by_ == voted && R->hash_ == hash; - } - static auto lookup(ValidatorSessionDescription& desc, const SentBlock* block, const CntVector* 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(r); - } - return static_cast(nullptr); - } - static const SessionVoteCandidate* create(ValidatorSessionDescription& desc, const SentBlock* block, - const CntVector* 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 v; - v.resize(desc.get_total_nodes(), false); - auto vec = CntVector::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::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* 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::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::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* voted_by_; - const HashType hash_; -}; - -//using SentBlockVector = CntSortedVector; - -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; -using ApproveVector = CntSortedVector; - -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(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* precommitted, const SentBlock* vote_for, bool vote_for_inited, - HashType hash) { - if (!r || r->get_size() < sizeof(ValidatorSessionRoundAttemptState)) { - return false; - } - auto R = static_cast(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* 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(r); - } - return static_cast(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::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* 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 x; - x.resize(desc.get_total_nodes(), false); - auto p = CntVector::create(desc, std::move(x)); - - return create(desc, seqno, nullptr, p, nullptr, false); - } - - ValidatorSessionRoundAttemptState(ValidatorSessionDescription& desc, td::uint32 seqno, const VoteVector* votes, - const CntVector* 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 - 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 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* precommitted_; - const SentBlock* vote_for_; - const bool vote_for_inited_; - const HashType hash_; -}; - -using AttemptVector = - CntSortedVector; class ValidatorSessionRoundState : public ValidatorSessionDescription::RootObject { public: diff --git a/validator-session/validator-session-types.h b/validator-session/validator-session-types.h index 357f6217..57957478 100644 --- a/validator-session/validator-session-types.h +++ b/validator-session/validator-session-types.h @@ -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; diff --git a/validator-session/validator-session.cpp b/validator-session/validator-session.cpp index 2902b082..e08d8a7e 100644 --- a/validator-session/validator-session.cpp +++ b/validator-session/validator-session.cpp @@ -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 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(data.clone(), true); if (R.is_error()) { @@ -230,6 +246,12 @@ void ValidatorSessionImpl::process_broadcast(PublicKeyHash src, td::BufferSlice auto block_round = static_cast(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(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 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 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(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( 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(), + 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() { diff --git a/validator-session/validator-session.hpp b/validator-session/validator-session.hpp index e8274554..1717c99f 100644 --- a/validator-session/validator-session.hpp +++ b/validator-session/validator-session.hpp @@ -74,6 +74,8 @@ class ValidatorSessionImpl : public ValidatorSession { td::BufferSlice signature_; std::map> blocks_; + // src_round_candidate_[src_id][round] -> candidate id + std::vector> 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(), 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 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 expected_id, + bool is_overlay_broadcast); void process_message(PublicKeyHash src, td::BufferSlice data); void process_query(PublicKeyHash src, td::BufferSlice data, td::Promise 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